cleanup decorators
This commit is contained in:
parent
d7d44b0147
commit
a30cd8249a
|
@ -6,7 +6,14 @@ import { PostFilePipe } from './postfile.pipe';
|
||||||
@Module({
|
@Module({
|
||||||
imports: [EarlyConfigModule],
|
imports: [EarlyConfigModule],
|
||||||
providers: [MultiPartPipe, PostFilePipe],
|
providers: [MultiPartPipe, PostFilePipe],
|
||||||
exports: [MultiPartPipe, PostFilePipe, EarlyConfigModule],
|
|
||||||
|
exports: [
|
||||||
|
MultiPartPipe,
|
||||||
|
PostFilePipe,
|
||||||
|
// EarlyConfigModule is exported here because the pipes are dependedant on the config
|
||||||
|
// But these pipes dont resolve their dependencies via this module
|
||||||
|
// So this way we force it to be "global"
|
||||||
|
EarlyConfigModule,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class DecoratorsModule {
|
export class DecoratorsModule {}
|
||||||
}
|
|
||||||
|
|
12
backend/src/decorators/injectrequest.decorator.ts
Normal file
12
backend/src/decorators/injectrequest.decorator.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
||||||
|
import { Newable } from 'picsur-shared/dist/types';
|
||||||
|
|
||||||
|
// Since pipes dont have direct access to the request object, we need this decorator to inject it
|
||||||
|
export const InjectRequest = createParamDecorator(
|
||||||
|
async <T extends Object>(data: Newable<T>, ctx: ExecutionContext) => {
|
||||||
|
return {
|
||||||
|
req: ctx.switchToHttp().getRequest(),
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
|
@ -1,22 +1,9 @@
|
||||||
import {
|
|
||||||
createParamDecorator,
|
|
||||||
ExecutionContext
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { Newable } from 'picsur-shared/dist/types';
|
import { Newable } from 'picsur-shared/dist/types';
|
||||||
|
import { InjectRequest } from './injectrequest.decorator';
|
||||||
import { MultiPartPipe } from './multipart.pipe';
|
import { MultiPartPipe } from './multipart.pipe';
|
||||||
import { PostFilePipe } from './postfile.pipe';
|
import { PostFilePipe } from './postfile.pipe';
|
||||||
|
|
||||||
const InjectRequest = createParamDecorator(
|
export const PostFile = () => InjectRequest(PostFilePipe);
|
||||||
async <T extends Object>(data: Newable<T>, ctx: ExecutionContext) => {
|
|
||||||
return {
|
|
||||||
req: ctx.switchToHttp().getRequest(),
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export const PostFile = () =>
|
|
||||||
InjectRequest(PostFilePipe);
|
|
||||||
|
|
||||||
export const MultiPart = <T extends Object>(data: Newable<T>) =>
|
export const MultiPart = <T extends Object>(data: Newable<T>) =>
|
||||||
InjectRequest(data, MultiPartPipe);
|
InjectRequest(data, MultiPartPipe);
|
||||||
|
|
|
@ -28,10 +28,12 @@ export class MultiPartPipe implements PipeTransform {
|
||||||
req: FastifyRequest;
|
req: FastifyRequest;
|
||||||
data: Newable<T>;
|
data: Newable<T>;
|
||||||
}) {
|
}) {
|
||||||
|
// Data should be a validatable class constructor
|
||||||
const dtoClass = new data();
|
const dtoClass = new data();
|
||||||
|
|
||||||
if (!req.isMultipart()) throw new BadRequestException('Invalid file');
|
if (!req.isMultipart()) throw new BadRequestException('Invalid file');
|
||||||
|
|
||||||
|
// Fetch all fields from the request
|
||||||
let fields: MultipartFields | null = null;
|
let fields: MultipartFields | null = null;
|
||||||
try {
|
try {
|
||||||
fields = (
|
fields = (
|
||||||
|
@ -44,11 +46,15 @@ export class MultiPartPipe implements PipeTransform {
|
||||||
}
|
}
|
||||||
if (!fields) throw new BadRequestException('Invalid file');
|
if (!fields) throw new BadRequestException('Invalid file');
|
||||||
|
|
||||||
|
// Loop over every formfield that was sent
|
||||||
for (const key of Object.keys(fields)) {
|
for (const key of Object.keys(fields)) {
|
||||||
|
// Ignore duplicate fields
|
||||||
if (Array.isArray(fields[key])) {
|
if (Array.isArray(fields[key])) {
|
||||||
continue;
|
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) {
|
if ((fields[key] as any).value) {
|
||||||
(dtoClass as any)[key] = new MultiPartFieldDto(
|
(dtoClass as any)[key] = new MultiPartFieldDto(
|
||||||
fields[key] as MultipartFile,
|
fields[key] as MultipartFile,
|
||||||
|
@ -61,6 +67,7 @@ export class MultiPartPipe implements PipeTransform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now validate the class we made, if any properties were invalid, it will error here
|
||||||
const errors = await strictValidate(dtoClass);
|
const errors = await strictValidate(dtoClass);
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
this.logger.warn(errors);
|
this.logger.warn(errors);
|
||||||
|
|
|
@ -7,9 +7,10 @@ export const RequiredPermissions = (...permissions: Permissions) => {
|
||||||
return SetMetadata('permissions', permissions);
|
return SetMetadata('permissions', permissions);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Easy to read roles
|
// Just a verbose wrapper
|
||||||
export const NoPermissions = () => RequiredPermissions();
|
export const NoPermissions = () => RequiredPermissions();
|
||||||
|
|
||||||
|
// This still requires permissions, but also allows the client to use user/pass authentication instead of JWT
|
||||||
export const UseLocalAuth = (...permissions: Permissions) =>
|
export const UseLocalAuth = (...permissions: Permissions) =>
|
||||||
CombineFCDecorators(
|
CombineFCDecorators(
|
||||||
RequiredPermissions(...permissions),
|
RequiredPermissions(...permissions),
|
||||||
|
|
|
@ -18,6 +18,7 @@ export class PostFilePipe implements PipeTransform {
|
||||||
async transform({ req }: { req: FastifyRequest }) {
|
async transform({ req }: { req: FastifyRequest }) {
|
||||||
if (!req.isMultipart()) throw new BadRequestException('Invalid file');
|
if (!req.isMultipart()) throw new BadRequestException('Invalid file');
|
||||||
|
|
||||||
|
// Only one file is allowed
|
||||||
const file = await req.file({
|
const file = await req.file({
|
||||||
limits: {
|
limits: {
|
||||||
...this.multipartConfigService.getLimits(),
|
...this.multipartConfigService.getLimits(),
|
||||||
|
@ -26,14 +27,17 @@ export class PostFilePipe implements PipeTransform {
|
||||||
});
|
});
|
||||||
if (file === undefined) throw new BadRequestException('Invalid file');
|
if (file === undefined) throw new BadRequestException('Invalid file');
|
||||||
|
|
||||||
|
// Remove empty fields
|
||||||
const allFields: Multipart[] = Object.values(file.fields).filter(
|
const allFields: Multipart[] = Object.values(file.fields).filter(
|
||||||
(entry) => entry,
|
(entry) => entry,
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
|
// Remove non-file fields
|
||||||
const files = allFields.filter((entry) => entry.file !== undefined);
|
const files = allFields.filter((entry) => entry.file !== undefined);
|
||||||
|
|
||||||
if (files.length !== 1) throw new BadRequestException('Invalid file');
|
if (files.length !== 1) throw new BadRequestException('Invalid file');
|
||||||
|
|
||||||
|
// Return a buffer of the file
|
||||||
try {
|
try {
|
||||||
return await files[0].toBuffer();
|
return await files[0].toBuffer();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
Loading…
Reference in a new issue