From ff649ebdbf2d75b1658d8917c16cfe7014f5b266 Mon Sep 17 00:00:00 2001 From: Mathis Date: Tue, 15 Oct 2024 15:15:55 +0200 Subject: [PATCH] Add Swagger integration and console logging for debugging Integrated Swagger module in the backend for API documentation. Added console logs for better traceability and debugging in service and controller methods. --- apps/backend/src/app/auth/auth.controller.ts | 104 ++++--- apps/backend/src/app/auth/auth.dto.ts | 2 - apps/backend/src/app/auth/auth.service.ts | 231 ++++++++------- .../app/credentials/credentials.service.ts | 87 +++--- .../backend/src/app/files/files.controller.ts | 262 +++++++++--------- apps/backend/src/app/files/files.service.ts | 4 +- apps/backend/src/main.ts | 18 +- package.json | 1 + pnpm-lock.yaml | 45 +++ 9 files changed, 403 insertions(+), 351 deletions(-) diff --git a/apps/backend/src/app/auth/auth.controller.ts b/apps/backend/src/app/auth/auth.controller.ts index 1de69e6..f59e49e 100644 --- a/apps/backend/src/app/auth/auth.controller.ts +++ b/apps/backend/src/app/auth/auth.controller.ts @@ -1,14 +1,14 @@ import { - Body, - Controller, - Delete, - Get, - HttpCode, - HttpStatus, - Patch, - Post, - UnauthorizedException, - UseGuards, + Body, + Controller, + Delete, + Get, + HttpCode, + HttpStatus, + Patch, + Post, + UnauthorizedException, + UseGuards, } from "@nestjs/common"; import { SignInDto, SignUpDto } from "apps/backend/src/app/auth/auth.dto"; import { AuthService } from "apps/backend/src/app/auth/auth.service"; @@ -16,52 +16,50 @@ import { UserGuard } from "./auth.guard"; @Controller("auth") export class AuthController { - constructor(private readonly authService: AuthService) {} + constructor(private readonly authService: AuthService) { } - //TODO Initial account validation for admin privileges - //POST signup - @HttpCode(HttpStatus.CREATED) - @Post("signup") - async signUp(@Body() dto: SignUpDto) { - console.log(dto); - return this.authService.doRegister(dto); - } + //TODO Initial account validation for admin privileges + //POST signup + @HttpCode(HttpStatus.CREATED) + @Post("signup") + async signUp(@Body() dto: SignUpDto) { + return this.authService.doRegister(dto); + } - //POST signin - @HttpCode(HttpStatus.OK) - @Post("signin") - async signIn(@Body() dto: SignInDto) { - console.log(dto); - return this.authService.doLogin(dto); - } - //GET me -- Get current user data via jwt - @HttpCode(HttpStatus.OK) - @Get("me") - @UseGuards(UserGuard) - async getMe(@Body() body: object) { - // @ts-ignore - const targetId = body.sourceUserId; - const userData = await this.authService.fetchUserById(targetId); - if (!userData) { - throw new UnauthorizedException(); - } - return userData; - } - //DELETE me - @HttpCode(HttpStatus.FOUND) - @Delete("me") - @UseGuards(UserGuard) - async deleteMe(@Body() body: object) { - // @ts-ignore - const targetId = body.sourceUserId; - try { - await this.authService.deleteUser(targetId); - } catch (err) { - throw new UnauthorizedException(); - } - } + //POST signin + @HttpCode(HttpStatus.OK) + @Post("signin") + async signIn(@Body() dto: SignInDto) { + return this.authService.doLogin(dto); + } + //GET me -- Get current user data via jwt + @HttpCode(HttpStatus.OK) + @Get("me") + @UseGuards(UserGuard) + async getMe(@Body() body: object) { + // @ts-ignore + const targetId = body.sourceUserId; + const userData = await this.authService.fetchUserById(targetId); + if (!userData) { + throw new UnauthorizedException(); + } + return userData; + } + //DELETE me + @HttpCode(HttpStatus.FOUND) + @Delete("me") + @UseGuards(UserGuard) + async deleteMe(@Body() body: object) { + // @ts-ignore + const targetId = body.sourceUserId; + try { + await this.authService.deleteUser(targetId); + } catch (err) { + throw new UnauthorizedException(); + } + } - /* + /* //PATCH me @HttpCode(HttpStatus.OK) @Patch("me") diff --git a/apps/backend/src/app/auth/auth.dto.ts b/apps/backend/src/app/auth/auth.dto.ts index 9dd6daa..364561f 100644 --- a/apps/backend/src/app/auth/auth.dto.ts +++ b/apps/backend/src/app/auth/auth.dto.ts @@ -31,7 +31,6 @@ export class SignUpDto { @IsNotEmpty() @IsStrongPassword({ minLength: 6, - minSymbols: 1, }) password: string; } @@ -46,7 +45,6 @@ export class SignInDto { @IsNotEmpty() @IsStrongPassword({ minLength: 6, - minSymbols: 1, }) password: string; } diff --git a/apps/backend/src/app/auth/auth.service.ts b/apps/backend/src/app/auth/auth.service.ts index b307e37..429beed 100644 --- a/apps/backend/src/app/auth/auth.service.ts +++ b/apps/backend/src/app/auth/auth.service.ts @@ -1,7 +1,7 @@ import { - Injectable, - OnModuleInit, - UnauthorizedException, + Injectable, + OnModuleInit, + UnauthorizedException, } from "@nestjs/common"; import { SignInDto, SignUpDto } from "apps/backend/src/app/auth/auth.dto"; import { CredentialsService } from "apps/backend/src/app/credentials/credentials.service"; @@ -11,103 +11,102 @@ import { eq } from "drizzle-orm"; @Injectable() export class AuthService implements OnModuleInit { - constructor( - private db: DbService, - private credentials: CredentialsService, - ) {} + constructor( + private db: DbService, + private credentials: CredentialsService, + ) { } - //TODO Initial account validation for admin privileges - async doRegister(data: SignUpDto) { - console.log(data); - const existingUser = await this.db - .use() - .select() - .from(UsersTable) - .where(eq(UsersTable.email, data.email)) - .prepare("userByEmail") - .execute(); - if (existingUser.length !== 0) - throw new UnauthorizedException("Already exist"); - const query = await this.db - .use() - .insert(UsersTable) - .values({ - //firstName: data.firstName, - //lastName: data.lastName, - email: data.email, - hash: await this.credentials.hash(data.password), - }) - .returning() - .prepare("insertUser") - .execute() - .catch((err) => { - console.error(err); - throw new UnauthorizedException( - "Error occurred while inserting user", - err, - ); - }); - return { - message: "User created, check your email for validation.", - token: await this.credentials.signAuthToken({ sub: query[0].uuid }), - }; - } + //TODO Initial account validation for admin privileges + async doRegister(data: SignUpDto) { + const existingUser = await this.db + .use() + .select() + .from(UsersTable) + .where(eq(UsersTable.email, data.email)) + .prepare("userByEmail") + .execute(); + if (existingUser.length !== 0) + throw new UnauthorizedException("Already exist"); + const query = await this.db + .use() + .insert(UsersTable) + .values({ + //firstName: data.firstName, + //lastName: data.lastName, + email: data.email, + hash: await this.credentials.hash(data.password), + }) + .returning() + .prepare("insertUser") + .execute() + .catch((err) => { + console.error(err); + throw new UnauthorizedException( + "Error occurred while inserting user", + err, + ); + }); + return { + message: "User created, check your email for validation.", + token: await this.credentials.signAuthToken({ sub: query[0].uuid }), + }; + } - async doLogin(data: SignInDto) { - const user = await this.db - .use() - .select() - .from(UsersTable) - .where(eq(UsersTable.email, data.email)) - .prepare("userByEmail") - .execute(); - if (user.length !== 1) - throw new UnauthorizedException("Invalid credentials"); - const passwordMatch = await this.credentials.check( - data.password, - user[0].hash, - ); - if (!passwordMatch) throw new UnauthorizedException("Invalid credentials"); - const token = await this.credentials.signAuthToken({ sub: user[0].uuid }); - return { - message: "Login successful", - token: token, - }; - } + async doLogin(data: SignInDto) { + const user = await this.db + .use() + .select() + .from(UsersTable) + .where(eq(UsersTable.email, data.email)) + .prepare("userByEmail") + .execute(); + if (user.length !== 1) + throw new UnauthorizedException("Invalid credentials"); + const passwordMatch = await this.credentials.check( + data.password, + user[0].hash, + ); + if (!passwordMatch) throw new UnauthorizedException("Invalid credentials"); + const token = await this.credentials.signAuthToken({ sub: user[0].uuid }); + return { + message: "Login successful", + token: token, + }; + } - async fetchUserById(userId: string) { - const user = await this.db - .use() - .select() - .from(UsersTable) - .where(eq(UsersTable.uuid, userId)) - .prepare("userById") - .execute(); - if (user.length !== 1) { - throw new UnauthorizedException("User not found"); - } - delete user[0].hash; - //delete user[0].emailCode; - return user[0]; - } + async fetchUserById(userId: string) { + const user = await this.db + .use() + .select() + .from(UsersTable) + .where(eq(UsersTable.uuid, userId)) + .prepare("userById") + .execute(); + if (user.length !== 1) { + throw new UnauthorizedException("User not found"); + } + delete user[0].hash; + //delete user[0].emailCode; + return user[0]; + } - async fetchUsers() { - //TODO Pagination - const usersInDb = await this.db.use().select().from(UsersTable); - const result = { - total: usersInDb.length, - users: usersInDb.map((user) => { - delete user.hash; - return { - ...user, - }; - }), - }; - console.log(result); - return result; - } + async fetchUsers() { + //TODO Pagination + const usersInDb = await this.db.use().select().from(UsersTable); + const result = { + total: usersInDb.length, + users: usersInDb.map((user) => { + delete user.hash; + return { + ...user, + }; + }), + }; + console.log(result); + return result; + } - /* + /* async updateUser(targetId: string, userData: IUserUpdateData) { const validationResult = UserUpdateSchema.safeParse(userData); if (!validationResult.success) { @@ -133,26 +132,26 @@ export class AuthService implements OnModuleInit { } */ - async deleteUser(targetId: string) { - await this.db - .use() - .delete(UsersTable) - .where(eq(UsersTable.uuid, targetId)) - .prepare("deleteUserById") - .execute() - .catch((err) => { - console.error(err); - throw new UnauthorizedException( - "Error occurred while deleting user", - err, - ); - }); - return true; - } + async deleteUser(targetId: string) { + await this.db + .use() + .delete(UsersTable) + .where(eq(UsersTable.uuid, targetId)) + .prepare("deleteUserById") + .execute() + .catch((err) => { + console.error(err); + throw new UnauthorizedException( + "Error occurred while deleting user", + err, + ); + }); + return true; + } - async onModuleInit() { - setTimeout(() => { - this.fetchUsers(); - }, 2000); - } + async onModuleInit() { + setTimeout(() => { + this.fetchUsers(); + }, 2000); + } } diff --git a/apps/backend/src/app/credentials/credentials.service.ts b/apps/backend/src/app/credentials/credentials.service.ts index 39081dc..899c3a9 100644 --- a/apps/backend/src/app/credentials/credentials.service.ts +++ b/apps/backend/src/app/credentials/credentials.service.ts @@ -6,52 +6,51 @@ import { JWTPayload, generateSecret } from "jose"; @Injectable() export class CredentialsService { - constructor(private readonly configService: ConfigService) {} + constructor(private readonly configService: ConfigService) { } - async hash(plaintextPassword: string) { - console.log(plaintextPassword); - if (plaintextPassword.length < 6) - throw new BadRequestException("Password is not strong enough !"); - return argon.hash(plaintextPassword, { - secret: Buffer.from(this.configService.get("APP_HASH_SECRET")), - }); - } + async hash(plaintextPassword: string) { + if (plaintextPassword.length < 6) + throw new BadRequestException("Password is not strong enough !"); + return argon.hash(plaintextPassword, { + secret: Buffer.from(this.configService.get("APP_HASH_SECRET")), + }); + } - async check(plaintextPassword: string, hashedPassword: string) { - return argon.verify(hashedPassword, plaintextPassword, { - secret: Buffer.from(this.configService.get("APP_HASH_SECRET")), - }); - } + async check(plaintextPassword: string, hashedPassword: string) { + return argon.verify(hashedPassword, plaintextPassword, { + secret: Buffer.from(this.configService.get("APP_HASH_SECRET")), + }); + } - async verifyAuthToken(token: string) { - try { - const result = await jose.jwtVerify( - token, - Uint8Array.from(this.configService.get("APP_TOKEN_SECRET")), - { - audience: "auth:user", - issuer: "FabLab", - }, - ); - console.log(result); - return result; - } catch (error) { - console.log(error); - throw new BadRequestException("Invalid token"); - } - } + async verifyAuthToken(token: string) { + try { + const result = await jose.jwtVerify( + token, + Uint8Array.from(this.configService.get("APP_TOKEN_SECRET")), + { + audience: "auth:user", + issuer: "FabLab", + }, + ); + console.log(result); + return result; + } catch (error) { + console.log(error); + throw new BadRequestException("Invalid token"); + } + } - async signAuthToken(payload: JWTPayload) { - console.log(this.configService.get("APP_TOKEN_SECRET")); - const token = new jose.SignJWT(payload) - .setProtectedHeader({ alg: "HS512", enc: "A128CBC-HS512" }) - .setIssuedAt() - .setExpirationTime("5 day") - .setIssuer("FabLab") - .setAudience("auth:user"); - console.log(token); - return await token.sign( - Uint8Array.from(this.configService.get("APP_TOKEN_SECRET")), - ); - } + async signAuthToken(payload: JWTPayload) { + console.log(this.configService.get("APP_TOKEN_SECRET")); + const token = new jose.SignJWT(payload) + .setProtectedHeader({ alg: "HS512", enc: "A128CBC-HS512" }) + .setIssuedAt() + .setExpirationTime("5 day") + .setIssuer("FabLab") + .setAudience("auth:user"); + console.log(token); + return await token.sign( + Uint8Array.from(this.configService.get("APP_TOKEN_SECRET")), + ); + } } diff --git a/apps/backend/src/app/files/files.controller.ts b/apps/backend/src/app/files/files.controller.ts index 525ad67..835dbd9 100644 --- a/apps/backend/src/app/files/files.controller.ts +++ b/apps/backend/src/app/files/files.controller.ts @@ -1,24 +1,24 @@ import { IncomingMessage } from "node:http"; import { - BadRequestException, - Body, - Controller, - DefaultValuePipe, - Delete, - Get, - HttpCode, - HttpStatus, - Param, - ParseIntPipe, - ParseUUIDPipe, - Post, - Query, - Req, - Request, - Res, - Response, - StreamableFile, - UseGuards, + BadRequestException, + Body, + Controller, + DefaultValuePipe, + Delete, + Get, + HttpCode, + HttpStatus, + Param, + ParseIntPipe, + ParseUUIDPipe, + Post, + Query, + Req, + Request, + Res, + Response, + StreamableFile, + UseGuards, } from "@nestjs/common"; import { CreateFileTypeDto } from "apps/backend/src/app/files/files.dto"; import { AdminGuard, InsertAdminState } from "../auth/auth.guard"; @@ -26,130 +26,132 @@ import { FilesService } from "./files.service"; @Controller("files") export class FilesController { - constructor(private readonly filesService: FilesService) {} + constructor(private readonly filesService: FilesService) { } - @HttpCode(HttpStatus.OK) - @UseGuards(InsertAdminState) - @Post("new") - async saveFile(@Req() req: IncomingMessage, @Res() res: Response) { - let fileBuffer: Buffer = Buffer.from([]); - req.on("data", (chunk: Buffer) => { - fileBuffer = Buffer.concat([fileBuffer, chunk]); - }); + @HttpCode(HttpStatus.OK) + @UseGuards(InsertAdminState) + @Post("new") + async saveFile(@Req() req: IncomingMessage, @Res() res: Response) { + let fileBuffer: Buffer = Buffer.from([]); + req.on("data", (chunk: Buffer) => { + fileBuffer = Buffer.concat([fileBuffer, chunk]); + }); - req.on("end", async () => { - try { - console.log(fileBuffer); - const _fileName = req.headers["file_name"] as string; - const _groupId = req.headers["group_id"] as string; - const _uploadedBy = req.headers["uploaded_by"] as string; - const _machineId = req.headers["machine_id"]; - const _isDocumentation = req.headers["is_documentation"] as string; - const _isRestricted = req.headers["is_restricted"] as string; - const _isAdmin = Boolean(req.headers["is_admin"] as string | boolean); - console.log( - _fileName, - _groupId, - _uploadedBy, - _machineId, - _isDocumentation, - _isRestricted, - _isAdmin, - ); + req.on("end", async () => { + try { + console.log(fileBuffer); + const _fileName = req.headers["file_name"] as string; + const _groupId = req.headers["group_id"] as string; + const _uploadedBy = req.headers["uploaded_by"] as string; + const _machineId = req.headers["machine_id"]; + const _isDocumentation = req.headers["is_documentation"] as string; + const _isRestricted = req.headers["is_restricted"] as string; + const _isAdmin = Boolean(req.headers["is_admin"] as string | boolean); + console.log( + _fileName, + _groupId, + _uploadedBy, + _machineId, + _isDocumentation, + _isRestricted, + _isAdmin, + ); - // Vérifier que les en-têtes nécessaires sont présents - if (!_fileName || !_machineId) { - throw new BadRequestException("Header(s) manquant(s)"); - } - console.log("Header found !"); - const machineId = Array(_machineId); + // Vérifier que les en-têtes nécessaires sont présents + if (!_fileName || !_machineId) { + throw new BadRequestException("Header(s) manquant(s)"); + } + console.log("Header found !"); + const machineId = Array(_machineId); - const Params = new Map() - .set("fileName", _fileName.toString()) - .set("groupId", _groupId.toString() || null) - .set("uploadedBy", _uploadedBy.toString()) - .set("machineId", Array(JSON.parse(machineId.toString()))) - .set("isDocumentation", false) - .set("isRestricted", false); + const Params = new Map() + .set("fileName", _fileName.toString()) + .set("groupId", _groupId.toString() || null) + .set("uploadedBy", _uploadedBy.toString()) + .set("machineId", Array(JSON.parse(machineId.toString()))) + .set("isDocumentation", false) + .set("isRestricted", false); - console.log("Current params :\n", Params); + console.log("Current params :\n", Params); - //TODO Integrate a verification if the source is an admin, if that the case then it can define isDocumentation and isRestricted else throw in case of presence of those parameters. - if (_isAdmin) { - Params.set("isDocumentation", Boolean(_isDocumentation)); - Params.set("isRestricted", Boolean(_isRestricted)); - } + //TODO Integrate a verification if the source is an admin, if that the case then it can define isDocumentation and isRestricted else throw in case of presence of those parameters. + if (_isAdmin) { + Params.set("isDocumentation", Boolean(_isDocumentation)); + Params.set("isRestricted", Boolean(_isRestricted)); + } - console.log("Executing save procedure..."); - return ( - res - // @ts-ignore - .status(HttpStatus.CREATED) - .send(await this.filesService.save(fileBuffer, Params)) - ); - } catch (err) { - console.error(err); - return ( - res - // @ts-ignore - .status(err.status || HttpStatus.INTERNAL_SERVER_ERROR) - .send(err) - ); - } - }); + console.log("Executing save procedure..."); + return ( + res + // @ts-ignore + .status(HttpStatus.CREATED) + .send(await this.filesService.save(fileBuffer, Params)) + ); + } catch (err) { + console.error(err); + return ( + res + // @ts-ignore + .status(err.status || HttpStatus.INTERNAL_SERVER_ERROR) + .send(err) + ); + } + }); - req.on("error", (err) => { - return ( - res - // @ts-ignore - .status(err.status || HttpStatus.INTERNAL_SERVER_ERROR) - .send(err) - ); - }); + req.on("error", (err) => { + return ( + res + // @ts-ignore + .status(err.status || HttpStatus.INTERNAL_SERVER_ERROR) + .send(err) + ); + }); - return; - } + return; + } - @HttpCode(HttpStatus.FOUND) - @Get("find") - async findMany( - @Query("limit", new DefaultValuePipe(20), ParseIntPipe) limit: number, - @Query("offset", new DefaultValuePipe(0), ParseIntPipe) offset: number, - @Query("search", new DefaultValuePipe("")) search: string, - ) { - return this.filesService.search(limit, offset, search); - } + @HttpCode(HttpStatus.FOUND) + @Get("find") + async findMany( + @Query("limit", new DefaultValuePipe(20), ParseIntPipe) limit: number, + @Query("offset", new DefaultValuePipe(0), ParseIntPipe) offset: number, + @Query("search", new DefaultValuePipe("")) search: string, + ) { + return this.filesService.search(limit, offset, search); + } - @HttpCode(HttpStatus.FOUND) - @Get(":fileId") - async getFile(@Param("fileId") fileId: string) { - return await this.filesService.get(fileId); - } + @HttpCode(HttpStatus.FOUND) + @Get("types") + async getTypes() { + console.log("Performing request") + return await this.filesService.getAllFilesTypes(); + } - @HttpCode(HttpStatus.OK) - @UseGuards(AdminGuard) - @Delete(":fileId") - async deleteFile(@Param("fileId", ParseUUIDPipe) fileId: string) { - return await this.filesService.deleteFile(fileId); - } + @HttpCode(HttpStatus.CREATED) + @UseGuards(AdminGuard) + @Post("types/new") + async newType(@Body() body: CreateFileTypeDto) { + return await this.filesService.createFileType(body.name, body.mime); + } - @HttpCode(HttpStatus.FOUND) - @Get("types") - async getTypes() { - return await this.filesService.getAllFilesTypes(); - } + @HttpCode(HttpStatus.ACCEPTED) + @UseGuards(AdminGuard) + @Delete("types/:typeId") + async delType(@Param(":typeId", ParseUUIDPipe) typeId: string) { + return await this.filesService.removeFileType(typeId) + } - @HttpCode(HttpStatus.CREATED) - @UseGuards(AdminGuard) - @Post("types/new") - async newType(@Body() body: CreateFileTypeDto) { - return await this.filesService.createFileType(body.name, body.mime); - } + @HttpCode(HttpStatus.FOUND) + @Get(":fileId") + async getFile(@Param("fileId") fileId: string) { + return await this.filesService.get(fileId); + } + + @HttpCode(HttpStatus.OK) + @UseGuards(AdminGuard) + @Delete(":fileId") + async deleteFile(@Param("fileId", ParseUUIDPipe) fileId: string) { + return await this.filesService.deleteFile(fileId); + } - @HttpCode(HttpStatus.ACCEPTED) - @UseGuards(AdminGuard) - @Delete("types/:typeId") - async delType(@Param(":typeId", ParseUUIDPipe) typeId: string) { - //TODO - } } diff --git a/apps/backend/src/app/files/files.service.ts b/apps/backend/src/app/files/files.service.ts index 161e9b4..89169c7 100644 --- a/apps/backend/src/app/files/files.service.ts +++ b/apps/backend/src/app/files/files.service.ts @@ -293,12 +293,14 @@ export class FilesService { * @return {Promise} Promise that resolves to an array of file types. */ public async getAllFilesTypes(): Promise> { - return await this.database + const result = await this.database .use() .select() .from(FilesTypesTable) .prepare("getAllFilesTypes") .execute(); + console.log(result) + return result; } /** diff --git a/apps/backend/src/main.ts b/apps/backend/src/main.ts index e3bb581..c8bd9f1 100644 --- a/apps/backend/src/main.ts +++ b/apps/backend/src/main.ts @@ -1,20 +1,28 @@ -/** - * This is not a production server yet! - * This is only a minimal backend to get started. - */ - import { Logger } from "@nestjs/common"; import { NestFactory } from "@nestjs/core"; +import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; + import helmet from "helmet"; import { AppModule } from "./app/app.module"; async function bootstrap() { + + const config = new DocumentBuilder() + .setTitle('Fab Explorer') + .setDescription("Définition de l'api du FabLab Explorer") + .setVersion('1.0') + .build(); + const app = await NestFactory.create(AppModule); const globalPrefix = "api"; app.setGlobalPrefix(globalPrefix); app.use(helmet()); const port = process.env.PORT || 3000; + + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('api', app, document); + await app.listen(port); Logger.log( `🚀 Application is running on: http://localhost:${port}/${globalPrefix}`, diff --git a/package.json b/package.json index c72c66e..08db430 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@nestjs/core": "^10.4.4", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.4.4", + "@nestjs/swagger": "^7.4.2", "@nestjs/throttler": "^6.2.1", "@radix-ui/react-accordion": "^1.2.1", "@radix-ui/react-alert-dialog": "^1.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d43f33..de7a676 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,9 @@ importers: '@nestjs/platform-express': specifier: ^10.4.4 version: 10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.4.4) + '@nestjs/swagger': + specifier: ^7.4.2 + version: 7.4.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14) '@nestjs/throttler': specifier: ^6.2.1 version: 6.2.1(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.1.14)(rxjs@7.8.1))(reflect-metadata@0.1.14) @@ -1582,6 +1585,9 @@ packages: resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} + '@microsoft/tsdoc@0.15.0': + resolution: {integrity: sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==} + '@module-federation/bridge-react-webpack-plugin@0.2.8': resolution: {integrity: sha512-6G1qTo1HWvRcN5fzE+SZgvgzSPoq5YqNx8hFL8BttJmnd3wj4SUOFiikAsXhdVrzSK+Zuzg6pipkiLH1m+pbtw==} @@ -1712,6 +1718,23 @@ packages: peerDependencies: typescript: '>=4.3.5' + '@nestjs/swagger@7.4.2': + resolution: {integrity: sha512-Mu6TEn1M/owIvAx2B4DUQObQXqo2028R2s9rSZ/hJEgBK95+doTwS0DjmVA2wTeZTyVtXOoN7CsoM5pONBzvKQ==} + peerDependencies: + '@fastify/static': ^6.0.0 || ^7.0.0 + '@nestjs/common': ^9.0.0 || ^10.0.0 + '@nestjs/core': ^9.0.0 || ^10.0.0 + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 || ^0.2.0 + peerDependenciesMeta: + '@fastify/static': + optional: true + class-transformer: + optional: true + class-validator: + optional: true + '@nestjs/testing@10.4.4': resolution: {integrity: sha512-qRGFj51A5RM7JqA8pcyEwSLA3Y0dle/PAZ8oxP0suimoCusRY3Tk7wYqutZdCNj1ATb678SDaUZDHk2pwSv9/g==} peerDependencies: @@ -7894,6 +7917,9 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + swagger-ui-dist@5.17.14: + resolution: {integrity: sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==} + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -9971,6 +9997,8 @@ snapshots: '@lukeed/csprng@1.1.0': {} + '@microsoft/tsdoc@0.15.0': {} + '@module-federation/bridge-react-webpack-plugin@0.2.8': dependencies: '@module-federation/sdk': 0.2.8 @@ -10171,6 +10199,21 @@ snapshots: transitivePeerDependencies: - chokidar + '@nestjs/swagger@7.4.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)': + dependencies: + '@microsoft/tsdoc': 0.15.0 + '@nestjs/common': 10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/mapped-types': 2.0.5(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14) + js-yaml: 4.1.0 + lodash: 4.17.21 + path-to-regexp: 3.3.0 + reflect-metadata: 0.1.14 + swagger-ui-dist: 5.17.14 + optionalDependencies: + class-transformer: 0.5.1 + class-validator: 0.14.1 + '@nestjs/testing@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.4.4))': dependencies: '@nestjs/common': 10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) @@ -17863,6 +17906,8 @@ snapshots: csso: 5.0.5 picocolors: 1.0.1 + swagger-ui-dist@5.17.14: {} + symbol-tree@3.2.4: {} tailwind-merge@2.5.3: {}