From 9b0ab6adef62f81abbe42cfed0968a677cfa3131 Mon Sep 17 00:00:00 2001 From: rubikscraft Date: Sun, 18 Sep 2022 15:16:34 +0200 Subject: [PATCH] change upload mechanisms --- backend/package.json | 2 + .../config/early/multipart.config.service.ts | 6 +- backend/src/decorators/decorators.module.ts | 2 +- .../multipart/inject-request.decorator.ts | 5 +- .../multipart/multipart.decorator.ts | 4 +- .../decorators/multipart/multipart.pipe.ts | 82 ---------- .../src/decorators/multipart/postfile.pipe.ts | 6 +- .../decorators/multipart/postfiles.pipe.ts | 37 +++++ backend/src/models/dto/image-upload.dto.ts | 9 -- backend/src/models/dto/multipart.dto.ts | 48 ------ .../api/experiment/experiment.controller.ts | 32 ++-- .../api/experiment/experiment.module.ts | 2 +- .../routes/image/image-manage.controller.ts | 22 ++- backend/src/routes/image/image.controller.ts | 4 +- backend/src/util/iterator.ts | 9 ++ backend/src/workers/sharp/universal-sharp.ts | 2 +- .../models/dto/processing-view-meta.dto.ts | 10 +- .../routes/processing/processing.component.ts | 7 +- .../src/app/routes/upload/upload.component.ts | 24 +-- shared/src/types/failable.ts | 6 + yarn.lock | 149 +++++++++++++++++- 21 files changed, 271 insertions(+), 197 deletions(-) delete mode 100644 backend/src/decorators/multipart/multipart.pipe.ts create mode 100644 backend/src/decorators/multipart/postfiles.pipe.ts delete mode 100644 backend/src/models/dto/image-upload.dto.ts delete mode 100644 backend/src/models/dto/multipart.dto.ts create mode 100644 backend/src/util/iterator.ts diff --git a/backend/package.json b/backend/package.json index e6d45f7..ce429dc 100644 --- a/backend/package.json +++ b/backend/package.json @@ -32,9 +32,11 @@ "@nestjs/jwt": "^9.0.0", "@nestjs/passport": "^9.0.0", "@nestjs/platform-fastify": "^9.0.11", + "@nestjs/platform-socket.io": "^9.0.11", "@nestjs/serve-static": "^3.0.0", "@nestjs/throttler": "^3.0.0", "@nestjs/typeorm": "^9.0.1", + "@nestjs/websockets": "^9.0.11", "bcrypt": "^5.0.1", "bmp-img": "^1.2.1", "cors": "^2.8.5", diff --git a/backend/src/config/early/multipart.config.service.ts b/backend/src/config/early/multipart.config.service.ts index a31902d..12beb03 100644 --- a/backend/src/config/early/multipart.config.service.ts +++ b/backend/src/config/early/multipart.config.service.ts @@ -18,12 +18,12 @@ export class MultipartConfigService { ); } - public getLimits() { + public getLimits(fileLimit?: number) { return { fieldNameSize: 128, fieldSize: 1024, - fields: 16, - files: 16, + fields: 20, + files: fileLimit ?? 20, fileSize: this.getMaxFileSize(), }; } diff --git a/backend/src/decorators/decorators.module.ts b/backend/src/decorators/decorators.module.ts index 021bf37..d03d9a5 100644 --- a/backend/src/decorators/decorators.module.ts +++ b/backend/src/decorators/decorators.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; import { EarlyConfigModule } from '../config/early/early-config.module'; import { ImageIdPipe } from './image-id/image-id.pipe'; -import { MultiPartPipe } from './multipart/multipart.pipe'; import { PostFilePipe } from './multipart/postfile.pipe'; +import { MultiPartPipe } from './multipart/postfiles.pipe'; @Module({ imports: [EarlyConfigModule], diff --git a/backend/src/decorators/multipart/inject-request.decorator.ts b/backend/src/decorators/multipart/inject-request.decorator.ts index 591acfa..a9e467b 100644 --- a/backend/src/decorators/multipart/inject-request.decorator.ts +++ b/backend/src/decorators/multipart/inject-request.decorator.ts @@ -3,6 +3,9 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; // Since pipes dont have direct access to the request object, we need this decorator to inject it export const InjectRequest = createParamDecorator( async (data: any, ctx: ExecutionContext) => { - return ctx.switchToHttp().getRequest(); + return { + data: data, + request: ctx.switchToHttp().getRequest(), + }; }, ); diff --git a/backend/src/decorators/multipart/multipart.decorator.ts b/backend/src/decorators/multipart/multipart.decorator.ts index 91056a0..443fd9a 100644 --- a/backend/src/decorators/multipart/multipart.decorator.ts +++ b/backend/src/decorators/multipart/multipart.decorator.ts @@ -1,7 +1,7 @@ import { InjectRequest } from './inject-request.decorator'; -import { MultiPartPipe } from './multipart.pipe'; import { PostFilePipe } from './postfile.pipe'; +import { MultiPartPipe } from './postfiles.pipe'; export const PostFile = () => InjectRequest(PostFilePipe); -export const MultiPart = () => InjectRequest(MultiPartPipe); +export const PostFiles = (maxFiles?: number) => InjectRequest(maxFiles, MultiPartPipe); diff --git a/backend/src/decorators/multipart/multipart.pipe.ts b/backend/src/decorators/multipart/multipart.pipe.ts deleted file mode 100644 index 475c4ac..0000000 --- a/backend/src/decorators/multipart/multipart.pipe.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { MultipartFields, MultipartFile } from '@fastify/multipart'; -import { - ArgumentMetadata, - Injectable, - Logger, - PipeTransform, - Scope, -} from '@nestjs/common'; -import { FastifyRequest } from 'fastify'; -import { Fail, FT, HasFailed } from 'picsur-shared/dist/types'; -import { ZodDtoStatic } from 'picsur-shared/dist/util/create-zod-dto'; -import { MultipartConfigService } from '../../config/early/multipart.config.service'; -import { - CreateMultiPartFieldDto, - CreateMultiPartFileDto, -} from '../../models/dto/multipart.dto'; - -@Injectable({ scope: Scope.REQUEST }) -export class MultiPartPipe implements PipeTransform { - private readonly logger = new Logger(MultiPartPipe.name); - - constructor( - private readonly multipartConfigService: MultipartConfigService, - ) {} - - async transform( - req: FastifyRequest, - metadata: ArgumentMetadata, - ) { - let zodSchema = (metadata?.metatype as ZodDtoStatic)?.zodSchema; - if (!zodSchema) { - this.logger.error('Invalid scheme on multipart body'); - throw Fail(FT.Internal, 'Invalid scheme on backend'); - } - - let multipartData = {}; - if (!req.isMultipart()) throw Fail(FT.UsrValidation, 'Invalid file'); - - // Fetch all fields from the request - let fields: MultipartFields | null = null; - try { - fields = - ( - await req.file({ - limits: this.multipartConfigService.getLimits(), - }) - )?.fields ?? null; - } catch (e) { - this.logger.warn(e); - } - if (!fields) throw Fail(FT.UsrValidation, 'Invalid file'); - - // Loop over every formfield that was sent - for (const key of Object.keys(fields)) { - // Ignore duplicate fields - if (Array.isArray(fields[key])) { - continue; - } - - // Use the value property to differentiate between a field and a file - // And then put the value into the correct property on the validatable class - if ((fields[key] as any).value) { - (multipartData as any)[key] = CreateMultiPartFieldDto( - fields[key] as MultipartFile, - ); - } else { - const file = await CreateMultiPartFileDto(fields[key] as MultipartFile); - if (HasFailed(file)) throw file; - (multipartData as any)[key] = file; - } - } - - // Now validate the class we made, if any properties were invalid, it will error here - const result = zodSchema.safeParse(multipartData); - if (!result.success) { - this.logger.warn(result.error); - throw Fail(FT.UsrValidation, 'Invalid file'); - } - - return result.data; - } -} diff --git a/backend/src/decorators/multipart/postfile.pipe.ts b/backend/src/decorators/multipart/postfile.pipe.ts index b37deae..55aae7c 100644 --- a/backend/src/decorators/multipart/postfile.pipe.ts +++ b/backend/src/decorators/multipart/postfile.pipe.ts @@ -12,11 +12,11 @@ export class PostFilePipe implements PipeTransform { private readonly multipartConfigService: MultipartConfigService, ) {} - async transform({ req }: { req: FastifyRequest }) { - if (!req.isMultipart()) throw Fail(FT.UsrValidation, 'Invalid file'); + async transform({ request, data }: { data: any; request: FastifyRequest },) { + if (!request.isMultipart()) throw Fail(FT.UsrValidation, 'Invalid file'); // Only one file is allowed - const file = await req.file({ + const file = await request.file({ limits: { ...this.multipartConfigService.getLimits(), files: 1, diff --git a/backend/src/decorators/multipart/postfiles.pipe.ts b/backend/src/decorators/multipart/postfiles.pipe.ts new file mode 100644 index 0000000..3a3f18f --- /dev/null +++ b/backend/src/decorators/multipart/postfiles.pipe.ts @@ -0,0 +1,37 @@ +import { MultipartFile } from '@fastify/multipart'; +import { + ArgumentMetadata, + Injectable, + Logger, + PipeTransform, + Scope +} from '@nestjs/common'; +import { FastifyRequest } from 'fastify'; +import { Fail, FT } from 'picsur-shared/dist/types'; +import { MultipartConfigService } from '../../config/early/multipart.config.service'; + +export type FileIterator = AsyncIterableIterator; + +@Injectable({ scope: Scope.REQUEST }) +export class MultiPartPipe implements PipeTransform { + private readonly logger = new Logger(MultiPartPipe.name); + + constructor( + private readonly multipartConfigService: MultipartConfigService, + ) {} + + async transform( + { request, data }: { data: any; request: FastifyRequest }, + metadata: ArgumentMetadata, + ) { + const filesLimit = typeof data === 'number' ? data : undefined; + + if (!request.isMultipart()) throw Fail(FT.UsrValidation, 'Invalid file'); + + const files = request.files({ + limits: this.multipartConfigService.getLimits(filesLimit), + }); + + return files; + } +} diff --git a/backend/src/models/dto/image-upload.dto.ts b/backend/src/models/dto/image-upload.dto.ts deleted file mode 100644 index b7a8016..0000000 --- a/backend/src/models/dto/image-upload.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createZodDto } from 'picsur-shared/dist/util/create-zod-dto'; -import { z } from 'zod'; -import { MultiPartFileDtoSchema } from './multipart.dto'; - -// A validation class for form based file upload of an image -export const ImageUploadDtoSchema = z.object({ - image: MultiPartFileDtoSchema, -}); -export class ImageUploadDto extends createZodDto(ImageUploadDtoSchema) {} diff --git a/backend/src/models/dto/multipart.dto.ts b/backend/src/models/dto/multipart.dto.ts deleted file mode 100644 index d2180ee..0000000 --- a/backend/src/models/dto/multipart.dto.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { MultipartFile } from '@fastify/multipart'; -import { AsyncFailable, Fail, FT } from 'picsur-shared/dist/types'; -import { z } from 'zod'; - -export const MultiPartFileDtoSchema = z.object({ - fieldname: z.string(), - encoding: z.string(), - filename: z.string(), - mimetype: z.string(), - buffer: z.any(), - file: z.any(), -}); -export type MultiPartFileDto = z.infer; - -export async function CreateMultiPartFileDto( - file: MultipartFile, -): AsyncFailable { - try { - const buffer = await file.toBuffer(); - return { - fieldname: file.fieldname, - encoding: file.encoding, - filename: file.filename, - mimetype: file.mimetype, - buffer, - file: file.file, - }; - } catch (e) { - return Fail(FT.Internal, e); - } -} - -export const MultiPartFieldDtoSchema = z.object({ - fieldname: z.string(), - encoding: z.string(), - value: z.string(), -}); -export type MultiPartFieldDto = z.infer; - -export function CreateMultiPartFieldDto( - file: MultipartFile, -): MultiPartFieldDto { - return { - fieldname: file.fieldname, - encoding: file.encoding, - value: (file as any).value, - }; -} diff --git a/backend/src/routes/api/experiment/experiment.controller.ts b/backend/src/routes/api/experiment/experiment.controller.ts index 4aeaeb4..e7d8e2d 100644 --- a/backend/src/routes/api/experiment/experiment.controller.ts +++ b/backend/src/routes/api/experiment/experiment.controller.ts @@ -1,18 +1,20 @@ -import { Controller } from '@nestjs/common'; -import { NoPermissions } from '../../../decorators/permissions.decorator'; -@Controller('api/experiment') -@NoPermissions() -export class ExperimentController { - constructor() {} +import { WebSocketGateway } from '@nestjs/websockets'; - // @Get() - // @Returns(UserInfoResponse) - // async testRoute( - // @Request() req: AuthFastifyRequest, - // @Response({ passthrough: true }) res: FastifyReply, - // ): Promise { - // res.header('Location', '/error/delete-success'); - // res.code(302); - // return req.user; + +@WebSocketGateway({ + namespace: 'experiment', +}) +export class ExperimentController { + constructor() { + console.log('ExperimentController created'); + } + + // @SubscribeMessage('test') + // async testRoute(@MessageBody() data: any): Promise { + // console.log('testRoute', data); + // return Promise.resolve({ + // event: 'test', + // data: Buffer.from('Hello'), + // }) // } } diff --git a/backend/src/routes/api/experiment/experiment.module.ts b/backend/src/routes/api/experiment/experiment.module.ts index fd032ec..727993b 100644 --- a/backend/src/routes/api/experiment/experiment.module.ts +++ b/backend/src/routes/api/experiment/experiment.module.ts @@ -6,6 +6,6 @@ import { ExperimentController } from './experiment.controller'; @Module({ imports: [], - controllers: [ExperimentController], + providers: [ExperimentController] }) export class ExperimentModule {} diff --git a/backend/src/routes/image/image-manage.controller.ts b/backend/src/routes/image/image-manage.controller.ts index a0a18ca..be9422a 100644 --- a/backend/src/routes/image/image-manage.controller.ts +++ b/backend/src/routes/image/image-manage.controller.ts @@ -21,8 +21,9 @@ import { ImageUploadResponse } from 'picsur-shared/dist/dto/api/image-manage.dto'; import { Permission } from 'picsur-shared/dist/dto/permissions.enum'; -import { HasFailed, ThrowIfFailed } from 'picsur-shared/dist/types'; -import { MultiPart } from '../../decorators/multipart/multipart.decorator'; +import { Fail, FT, HasFailed, ThrowIfFailed } from 'picsur-shared/dist/types'; +import { PostFiles } from '../../decorators/multipart/multipart.decorator'; +import type { FileIterator } from '../../decorators/multipart/postfiles.pipe'; import { HasPermission, RequiredPermissions @@ -30,7 +31,7 @@ import { import { ReqUserID } from '../../decorators/request-user.decorator'; import { Returns } from '../../decorators/returns.decorator'; import { ImageManagerService } from '../../managers/image/image.service'; -import { ImageUploadDto } from '../../models/dto/image-upload.dto'; +import { GetNextAsync } from '../../util/iterator'; @Controller('api/image') @RequiredPermissions(Permission.ImageUpload) export class ImageManageController { @@ -42,15 +43,24 @@ export class ImageManageController { @Returns(ImageUploadResponse) @Throttle(20) async uploadImage( - @MultiPart() multipart: ImageUploadDto, + @PostFiles(1) multipart: FileIterator, @ReqUserID() userid: string, @HasPermission(Permission.ImageDeleteKey) withDeleteKey: boolean, ): Promise { + const file = ThrowIfFailed(await GetNextAsync(multipart)); + + let buffer: Buffer; + try { + buffer = await file.toBuffer(); + } catch (e) { + throw Fail(FT.Internal, e); + }; + const image = ThrowIfFailed( await this.imagesService.upload( userid, - multipart.image.filename, - multipart.image.buffer, + file.filename, + buffer, withDeleteKey, ), ); diff --git a/backend/src/routes/image/image.controller.ts b/backend/src/routes/image/image.controller.ts index 0e3bb9d..6335991 100644 --- a/backend/src/routes/image/image.controller.ts +++ b/backend/src/routes/image/image.controller.ts @@ -1,8 +1,9 @@ import { Controller, Get, Head, Logger, Query, Res } from '@nestjs/common'; +import { SkipThrottle } from '@nestjs/throttler'; import type { FastifyReply } from 'fastify'; import { ImageMetaResponse, - ImageRequestParams, + ImageRequestParams } from 'picsur-shared/dist/dto/api/image.dto'; import { ImageEntryVariant } from 'picsur-shared/dist/dto/image-entry-variant.enum'; import { FileType2Mime } from 'picsur-shared/dist/dto/mimes.dto'; @@ -21,6 +22,7 @@ import { BrandMessageType, GetBrandMessage } from '../../util/branding'; // This is the only controller with CORS enabled @Controller('i') @RequiredPermissions(Permission.ImageView) +@SkipThrottle() export class ImageController { private readonly logger = new Logger(ImageController.name); diff --git a/backend/src/util/iterator.ts b/backend/src/util/iterator.ts new file mode 100644 index 0000000..58502cd --- /dev/null +++ b/backend/src/util/iterator.ts @@ -0,0 +1,9 @@ +import { AsyncFailable, Fail, FT } from 'picsur-shared/dist/types'; + +export async function GetNextAsync( + iterator: AsyncIterableIterator, +): AsyncFailable { + const { done, value } = await iterator.next(); + if (done) return Fail(FT.BadRequest); + return value; +} diff --git a/backend/src/workers/sharp/universal-sharp.ts b/backend/src/workers/sharp/universal-sharp.ts index ba01820..5aad601 100644 --- a/backend/src/workers/sharp/universal-sharp.ts +++ b/backend/src/workers/sharp/universal-sharp.ts @@ -2,7 +2,7 @@ import { BMPdecode, BMPencode } from 'bmp-img'; import { AnimFileType, FileType, - ImageFileType, + ImageFileType } from 'picsur-shared/dist/dto/mimes.dto'; import { QOIdecode, QOIencode } from 'qoi-img'; import sharp, { Sharp, SharpOptions } from 'sharp'; diff --git a/frontend/src/app/models/dto/processing-view-meta.dto.ts b/frontend/src/app/models/dto/processing-view-meta.dto.ts index 6a402fa..681ea1d 100644 --- a/frontend/src/app/models/dto/processing-view-meta.dto.ts +++ b/frontend/src/app/models/dto/processing-view-meta.dto.ts @@ -1,3 +1,9 @@ -export interface ProcessingViewMeta { - imageFile: File; +export class ProcessingViewMeta { + private _tag = 'ProcessingViewMeta'; + + constructor(public imageFiles: File[]) {} + + static is(value: any): value is ProcessingViewMeta { + return (value ?? {})._tag === 'ProcessingViewMeta'; + } } diff --git a/frontend/src/app/routes/processing/processing.component.ts b/frontend/src/app/routes/processing/processing.component.ts index 0f6e3c2..114ec27 100644 --- a/frontend/src/app/routes/processing/processing.component.ts +++ b/frontend/src/app/routes/processing/processing.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { Fail, FT, HasFailed } from 'picsur-shared/dist/types'; import { ProcessingViewMeta } from 'src/app/models/dto/processing-view-meta.dto'; +import { ApiService } from 'src/app/services/api/api.service'; import { ImageService } from 'src/app/services/api/image.service'; import { Logger } from 'src/app/services/logger/logger.service'; import { ErrorService } from 'src/app/util/error-manager/error.service'; @@ -16,11 +17,12 @@ export class ProcessingComponent implements OnInit { private readonly router: Router, private readonly imageService: ImageService, private readonly errorService: ErrorService, + private readonly apiService: ApiService, ) {} async ngOnInit() { const state = history.state as ProcessingViewMeta; - if (!state) { + if (!ProcessingViewMeta.is(state)) { return this.errorService.quitFailure( Fail(FT.UsrValidation, 'No state provided'), this.logger, @@ -29,7 +31,8 @@ export class ProcessingComponent implements OnInit { history.replaceState(null, ''); - const id = await this.imageService.UploadImage(state.imageFile); + const id = await this.imageService.UploadImage(state.imageFiles[0]); + if (HasFailed(id)) return this.errorService.quitFailure(id, this.logger); this.router.navigate([`/view/`, id], { replaceUrl: true }); diff --git a/frontend/src/app/routes/upload/upload.component.ts b/frontend/src/app/routes/upload/upload.component.ts index 3b1e393..4c29952 100644 --- a/frontend/src/app/routes/upload/upload.component.ts +++ b/frontend/src/app/routes/upload/upload.component.ts @@ -39,14 +39,9 @@ export class UploadComponent implements OnInit { } onSelect(event: NgxDropzoneChangeEvent) { - if (event.addedFiles.length > 1) - this.errorService.log( - 'You uploaded multiple images, only one has been uploaded', - ); - - const metadata: ProcessingViewMeta = { - imageFile: event.addedFiles[0], - }; + const metadata: ProcessingViewMeta = new ProcessingViewMeta( + event.addedFiles, + ); this.router.navigate(['/processing'], { state: metadata }); } @@ -64,21 +59,16 @@ export class UploadComponent implements OnInit { 'Your clipboard does not contain any images', ); - const blob = filteredItems[0].getAsFile(); - if (!blob) + const blobs = filteredItems.map((item) => item.getAsFile()); + if (blobs.some((blob) => blob === null)) return this.errorService.showFailure( Fail(FT.Internal, 'Error getting image from clipboard'), this.logger, ); - if (filteredItems.length > 1) - this.errorService.log( - 'You pasted multiple images, only one has been uploaded', - ); + const safeBlob = blobs as File[]; - const metadata: ProcessingViewMeta = { - imageFile: blob, - }; + const metadata: ProcessingViewMeta = new ProcessingViewMeta(safeBlob); this.router.navigate(['/processing'], { state: metadata }); } diff --git a/shared/src/types/failable.ts b/shared/src/types/failable.ts index a7d3ad0..1260876 100644 --- a/shared/src/types/failable.ts +++ b/shared/src/types/failable.ts @@ -9,6 +9,7 @@ export enum FT { Database = 'database', SysValidation = 'sysvalidation', UsrValidation = 'usrvalidation', + BadRequest = 'badrequest', Permission = 'permission', RateLimit = 'ratelimit', NotFound = 'notfound', @@ -59,6 +60,11 @@ const FTProps: { code: 400, message: 'Validation of user input failed', }, + [FT.BadRequest]: { + important: false, + code: 400, + message: 'Bad request', + }, [FT.Permission]: { important: false, code: 403, diff --git a/yarn.lock b/yarn.lock index 50c2124..8007911 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2530,6 +2530,20 @@ __metadata: languageName: node linkType: hard +"@nestjs/platform-socket.io@npm:^9.0.11": + version: 9.0.11 + resolution: "@nestjs/platform-socket.io@npm:9.0.11" + dependencies: + socket.io: 4.5.1 + tslib: 2.4.0 + peerDependencies: + "@nestjs/common": ^9.0.0 + "@nestjs/websockets": ^9.0.0 + rxjs: ^7.1.0 + checksum: 0b7eba29e51bd6cedb5613144a725dae7d41bfed65e719a58474912fb111669c4ba4c6e43bc153408d8a3b6218d3e79651c785603ff526bab61170a7782fa71d + languageName: node + linkType: hard + "@nestjs/schematics@npm:^9.0.0, @nestjs/schematics@npm:^9.0.3": version: 9.0.3 resolution: "@nestjs/schematics@npm:9.0.3" @@ -2604,6 +2618,26 @@ __metadata: languageName: node linkType: hard +"@nestjs/websockets@npm:^9.0.11": + version: 9.0.11 + resolution: "@nestjs/websockets@npm:9.0.11" + dependencies: + iterare: 1.2.1 + object-hash: 3.0.0 + tslib: 2.4.0 + peerDependencies: + "@nestjs/common": ^9.0.0 + "@nestjs/core": ^9.0.0 + "@nestjs/platform-socket.io": ^9.0.0 + reflect-metadata: ^0.1.12 + rxjs: ^7.1.0 + peerDependenciesMeta: + "@nestjs/platform-socket.io": + optional: true + checksum: 1e00f5186408bc0896a5ac6f7fc17379fdc627ea6ae2c39eb321f083ad156d07d2279a91828bae1bfcc07a986ec60fbfa3f803e934a09696ce9cc7673e245af8 + languageName: node + linkType: hard + "@ng-web-apis/common@npm:^2.0.1": version: 2.0.1 resolution: "@ng-web-apis/common@npm:2.0.1" @@ -2890,6 +2924,13 @@ __metadata: languageName: node linkType: hard +"@types/component-emitter@npm:^1.2.10": + version: 1.2.11 + resolution: "@types/component-emitter@npm:1.2.11" + checksum: 0e081c5f7a4b113af3732f67ad9ebb487d5c239d440d96938ff9a679d18bb9337a513638e12b5b02a7a921494eef18c5a4d78f1188bc43a12290edd74c42a9c7 + languageName: node + linkType: hard + "@types/connect-history-api-fallback@npm:^1.3.5": version: 1.3.5 resolution: "@types/connect-history-api-fallback@npm:1.3.5" @@ -2909,6 +2950,13 @@ __metadata: languageName: node linkType: hard +"@types/cookie@npm:^0.4.1": + version: 0.4.1 + resolution: "@types/cookie@npm:0.4.1" + checksum: 3275534ed69a76c68eb1a77d547d75f99fedc80befb75a3d1d03662fb08d697e6f8b1274e12af1a74c6896071b11510631ba891f64d30c78528d0ec45a9c1a18 + languageName: node + linkType: hard + "@types/cookiejar@npm:*": version: 2.1.2 resolution: "@types/cookiejar@npm:2.1.2" @@ -3035,7 +3083,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^18.7.18": +"@types/node@npm:>=10.0.0, @types/node@npm:^18.7.18": version: 18.7.18 resolution: "@types/node@npm:18.7.18" checksum: 8aec61f0f96e2a69ce51f1f40f949ca578bbb4fe05d7c0b8ce3aeeb848e90f755837f17f6ac132ca404d974fe9b2974150ad3b4984fc9dc7c3ceddb10bae0167 @@ -3937,6 +3985,13 @@ __metadata: languageName: node linkType: hard +"base64id@npm:2.0.0, base64id@npm:~2.0.0": + version: 2.0.0 + resolution: "base64id@npm:2.0.0" + checksum: 581b1d37e6cf3738b7ccdd4d14fe2bfc5c238e696e2720ee6c44c183b838655842e22034e53ffd783f872a539915c51b0d4728a49c7cc678ac5a758e00d62168 + languageName: node + linkType: hard + "batch@npm:0.6.1": version: 0.6.1 resolution: "batch@npm:0.6.1" @@ -4471,6 +4526,13 @@ __metadata: languageName: node linkType: hard +"component-emitter@npm:~1.3.0": + version: 1.3.0 + resolution: "component-emitter@npm:1.3.0" + checksum: b3c46de38ffd35c57d1c02488355be9f218e582aec72d72d1b8bbec95a3ac1b38c96cd6e03ff015577e68f550fbb361a3bfdbd9bb248be9390b7b3745691be6b + languageName: node + linkType: hard + "compressible@npm:~2.0.16": version: 2.0.18 resolution: "compressible@npm:2.0.18" @@ -4562,6 +4624,13 @@ __metadata: languageName: node linkType: hard +"cookie@npm:~0.4.1": + version: 0.4.2 + resolution: "cookie@npm:0.4.2" + checksum: a00833c998bedf8e787b4c342defe5fa419abd96b32f4464f718b91022586b8f1bafbddd499288e75c037642493c83083da426c6a9080d309e3bd90fd11baa9b + languageName: node + linkType: hard + "copy-anything@npm:^2.0.1": version: 2.0.6 resolution: "copy-anything@npm:2.0.6" @@ -4604,7 +4673,7 @@ __metadata: languageName: node linkType: hard -"cors@npm:^2.8.5": +"cors@npm:^2.8.5, cors@npm:~2.8.5": version: 2.8.5 resolution: "cors@npm:2.8.5" dependencies: @@ -4780,7 +4849,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:~4.3.1, debug@npm:~4.3.2": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -5077,6 +5146,31 @@ __metadata: languageName: node linkType: hard +"engine.io-parser@npm:~5.0.3": + version: 5.0.4 + resolution: "engine.io-parser@npm:5.0.4" + checksum: d4ad0cef6ff63c350e35696da9bb3dbd180f67b56e93e90375010cc40393e6c0639b780d5680807e1d93a7e2e3d7b4a1c3b27cf75db28eb8cbf605bc1497da03 + languageName: node + linkType: hard + +"engine.io@npm:~6.2.0": + version: 6.2.0 + resolution: "engine.io@npm:6.2.0" + dependencies: + "@types/cookie": ^0.4.1 + "@types/cors": ^2.8.12 + "@types/node": ">=10.0.0" + accepts: ~1.3.4 + base64id: 2.0.0 + cookie: ~0.4.1 + cors: ~2.8.5 + debug: ~4.3.1 + engine.io-parser: ~5.0.3 + ws: ~8.2.3 + checksum: cc485c5ba2e0c4f6ca02dcafd192b22f9dad89d01dc815005298780d3fb910db4cebab4696e8615290c473c2eeb259e8bee2a1fb7ab594d9c80f9f3485771911 + languageName: node + linkType: hard + "enhanced-resolve@npm:^5.0.0, enhanced-resolve@npm:^5.10.0, enhanced-resolve@npm:^5.7.0": version: 5.10.0 resolution: "enhanced-resolve@npm:5.10.0" @@ -8800,11 +8894,13 @@ __metadata: "@nestjs/jwt": ^9.0.0 "@nestjs/passport": ^9.0.0 "@nestjs/platform-fastify": ^9.0.11 + "@nestjs/platform-socket.io": ^9.0.11 "@nestjs/schematics": ^9.0.3 "@nestjs/serve-static": ^3.0.0 "@nestjs/testing": ^9.0.11 "@nestjs/throttler": ^3.0.0 "@nestjs/typeorm": ^9.0.1 + "@nestjs/websockets": ^9.0.11 "@types/bcrypt": ^5.0.0 "@types/cors": ^2.8.12 "@types/multer": ^1.4.7 @@ -10481,6 +10577,38 @@ __metadata: languageName: node linkType: hard +"socket.io-adapter@npm:~2.4.0": + version: 2.4.0 + resolution: "socket.io-adapter@npm:2.4.0" + checksum: a84639946dce13547b95f6e09fe167cdcd5d80941afc2e46790cc23384e0fd3c901e690ecc9bdd600939ce6292261ee15094a0b486f797ed621cfc8783d87a0c + languageName: node + linkType: hard + +"socket.io-parser@npm:~4.0.4": + version: 4.0.5 + resolution: "socket.io-parser@npm:4.0.5" + dependencies: + "@types/component-emitter": ^1.2.10 + component-emitter: ~1.3.0 + debug: ~4.3.1 + checksum: 8b60cf3abb9c3571f90cf894d40f41459ab007e6cee7ca8ee28ab107d76ded4a72ca5c4e5dcb82d996d4f78b3689dd3eb36ba0b39a66e25e2e9a9afa276c81c5 + languageName: node + linkType: hard + +"socket.io@npm:4.5.1": + version: 4.5.1 + resolution: "socket.io@npm:4.5.1" + dependencies: + accepts: ~1.3.4 + base64id: ~2.0.0 + debug: ~4.3.2 + engine.io: ~6.2.0 + socket.io-adapter: ~2.4.0 + socket.io-parser: ~4.0.4 + checksum: 86afd6dcce0c96de85b20a0e37fa4a21e2e96bd6e36d2518acfad37597bcb5208feafbbac20cd34ee4b9356d40418a43938bcf4a206ba693ba3c771ffcef724f + languageName: node + linkType: hard + "sockjs@npm:^0.3.24": version: 0.3.24 resolution: "sockjs@npm:0.3.24" @@ -11808,6 +11936,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:~8.2.3": + version: 8.2.3 + resolution: "ws@npm:8.2.3" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: c869296ccb45f218ac6d32f8f614cd85b50a21fd434caf11646008eef92173be53490810c5c23aea31bc527902261fbfd7b062197eea341b26128d4be56a85e4 + languageName: node + linkType: hard + "xml2js@npm:^0.4.23": version: 0.4.23 resolution: "xml2js@npm:0.4.23"