mirror of
https://github.com/CaramelFur/Picsur.git
synced 2026-05-06 12:56:19 +02:00
change upload mechanisms
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<T extends Object>(
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
37
backend/src/decorators/multipart/postfiles.pipe.ts
Normal file
37
backend/src/decorators/multipart/postfiles.pipe.ts
Normal file
@@ -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<MultipartFile>;
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class MultiPartPipe implements PipeTransform {
|
||||
private readonly logger = new Logger(MultiPartPipe.name);
|
||||
|
||||
constructor(
|
||||
private readonly multipartConfigService: MultipartConfigService,
|
||||
) {}
|
||||
|
||||
async transform<T extends Object>(
|
||||
{ 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;
|
||||
}
|
||||
}
|
||||
@@ -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) {}
|
||||
@@ -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<typeof MultiPartFileDtoSchema>;
|
||||
|
||||
export async function CreateMultiPartFileDto(
|
||||
file: MultipartFile,
|
||||
): AsyncFailable<MultiPartFileDto> {
|
||||
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<typeof MultiPartFieldDtoSchema>;
|
||||
|
||||
export function CreateMultiPartFieldDto(
|
||||
file: MultipartFile,
|
||||
): MultiPartFieldDto {
|
||||
return {
|
||||
fieldname: file.fieldname,
|
||||
encoding: file.encoding,
|
||||
value: (file as any).value,
|
||||
};
|
||||
}
|
||||
@@ -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<UserInfoResponse> {
|
||||
// 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<WsResponse> {
|
||||
// console.log('testRoute', data);
|
||||
// return Promise.resolve({
|
||||
// event: 'test',
|
||||
// data: Buffer.from('Hello'),
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ import { ExperimentController } from './experiment.controller';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [ExperimentController],
|
||||
providers: [ExperimentController]
|
||||
})
|
||||
export class ExperimentModule {}
|
||||
|
||||
@@ -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<ImageUploadResponse> {
|
||||
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,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
9
backend/src/util/iterator.ts
Normal file
9
backend/src/util/iterator.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { AsyncFailable, Fail, FT } from 'picsur-shared/dist/types';
|
||||
|
||||
export async function GetNextAsync<T>(
|
||||
iterator: AsyncIterableIterator<T>,
|
||||
): AsyncFailable<T> {
|
||||
const { done, value } = await iterator.next();
|
||||
if (done) return Fail(FT.BadRequest);
|
||||
return value;
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
149
yarn.lock
149
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"
|
||||
|
||||
Reference in New Issue
Block a user