diff --git a/backend/src/collections/imagedb/imagedb.service.ts b/backend/src/collections/imagedb/imagedb.service.ts index 0df0c45..ab5a827 100644 --- a/backend/src/collections/imagedb/imagedb.service.ts +++ b/backend/src/collections/imagedb/imagedb.service.ts @@ -7,7 +7,6 @@ import { Fail, HasSuccess } from 'picsur-shared/dist/types'; import { Repository } from 'typeorm'; -import { SupportedMime } from '../../models/dto/mimes.dto'; import { EImageBackend } from '../../models/entities/image.entity'; import { GetCols } from '../../models/util/collection'; @@ -20,7 +19,7 @@ export class ImageDBService { public async create( image: Buffer, - type: SupportedMime, + type: string, ): AsyncFailable { const hash = this.hash(image); const find = await this.findOne(hash); diff --git a/backend/src/collections/imagedb/mimes.service.ts b/backend/src/collections/imagedb/mimes.service.ts index 1532177..99227a4 100644 --- a/backend/src/collections/imagedb/mimes.service.ts +++ b/backend/src/collections/imagedb/mimes.service.ts @@ -1,16 +1,19 @@ import { Injectable } from '@nestjs/common'; import { Fail, Failable } from 'picsur-shared/dist/types'; -import { FullMime, SupportedAnimMimes, SupportedImageMimes, SupportedMime } from '../../models/dto/mimes.dto'; - +import { + FullMime, + SupportedAnimMimes, + SupportedImageMimes +} from '../../models/dto/mimes.dto'; @Injectable() export class MimesService { public getFullMime(mime: string): Failable { if (SupportedImageMimes.includes(mime)) { - return { mime: mime as SupportedMime, type: 'image' }; + return { mime, type: 'image' }; } if (SupportedAnimMimes.includes(mime)) { - return { mime: mime as SupportedMime, type: 'anim' }; + return { mime, type: 'anim' }; } return Fail('Unsupported mime type'); } diff --git a/backend/src/managers/auth/guards/main.guard.ts b/backend/src/managers/auth/guards/main.guard.ts index 78bca58..2ce4538 100644 --- a/backend/src/managers/auth/guards/main.guard.ts +++ b/backend/src/managers/auth/guards/main.guard.ts @@ -13,7 +13,7 @@ import { strictValidate } from 'picsur-shared/dist/util/validate'; import { UserRolesService } from '../../../collections/userdb/userrolesdb.service'; import { Permissions } from '../../../models/dto/permissions.dto'; import { EUserBackend } from '../../../models/entities/user.entity'; -import { isPermissionsArray } from '../../../models/util/permissions'; +import { isPermissionsArray } from '../../../models/util/permissions.validator'; // This guard extends both the jwt authenticator and the guest authenticator // The order matters here, because this results in the guest authenticator being used as a fallback diff --git a/backend/src/models/dto/mimes.dto.ts b/backend/src/models/dto/mimes.dto.ts index 0c53d70..2b3c87d 100644 --- a/backend/src/models/dto/mimes.dto.ts +++ b/backend/src/models/dto/mimes.dto.ts @@ -1,33 +1,30 @@ -import tuple from 'picsur-shared/dist/types/tuple'; // Config +export enum ImageMime { + JPEG = 'image/jpeg', + PNG = 'image/png', + WEBP = 'image/webp', + TIFF = 'image/tiff', + BMP = 'image/bmp', + ICO = 'image/x-icon', +} -const SupportedImageMimesTuple = tuple( - 'image/jpeg', - 'image/png', - 'image/webp', - 'image/tiff', - 'image/bmp', - 'image/x-icon', -); +export enum AnimMime { + APNG = 'image/apng', + GIF = 'image/gif', +} -const SupportedAnimMimesTuple = tuple('image/apng', 'image/gif'); - -const SupportedMimesTuple = [ - ...SupportedImageMimesTuple, - ...SupportedAnimMimesTuple, -]; +export const SupportedMime = {...ImageMime, ...AnimMime}; // Derivatives -export const SupportedImageMimes: string[] = SupportedImageMimesTuple; -export const SupportedAnimMimes: string[] = SupportedAnimMimesTuple; +export const SupportedImageMimes: string[] = Object.values(ImageMime); +export const SupportedAnimMimes: string[] = Object.values(AnimMime); +export const SupportedMimes: string[] = Object.values(SupportedMime); -export const SupportedMimes: string[] = SupportedMimesTuple; -export type SupportedMime = typeof SupportedMimesTuple[number]; export type SupportedMimeCategory = 'image' | 'anim'; export interface FullMime { - mime: SupportedMime; + mime: string; type: SupportedMimeCategory; } diff --git a/backend/src/models/dto/syspreferences.dto.ts b/backend/src/models/dto/syspreferences.dto.ts index c2792e1..2ce7265 100644 --- a/backend/src/models/dto/syspreferences.dto.ts +++ b/backend/src/models/dto/syspreferences.dto.ts @@ -6,8 +6,7 @@ import { export type SysPreferences = SysPreference[]; export const SysPreferenceList: string[] = Object.values(SysPreference); -// Syspref Values - +// Syspref Value types export const SysPreferenceValueTypes: { [key in SysPreference]: SysPrefValueTypeStrings; } = { diff --git a/backend/src/models/entities/image.entity.ts b/backend/src/models/entities/image.entity.ts index 2ac7d6d..053be3e 100644 --- a/backend/src/models/entities/image.entity.ts +++ b/backend/src/models/entities/image.entity.ts @@ -1,7 +1,5 @@ -import { IsEnum } from 'class-validator'; import { EImage } from 'picsur-shared/dist/entities/image.entity'; import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'; -import { SupportedMime, SupportedMimes } from '../dto/mimes.dto'; @Entity() export class EImageBackend extends EImage { @@ -16,7 +14,6 @@ export class EImageBackend extends EImage { @Column({ type: 'bytea', nullable: false, select: false }) override data?: Buffer; - @Column({ enum: SupportedMimes, nullable: false }) - @IsEnum(SupportedMimes) - override mime: SupportedMime; + @Column({ nullable: false }) + override mime: string; } diff --git a/backend/src/models/entities/syspreference.entity.ts b/backend/src/models/entities/syspreference.entity.ts index 286185d..a1ca470 100644 --- a/backend/src/models/entities/syspreference.entity.ts +++ b/backend/src/models/entities/syspreference.entity.ts @@ -1,11 +1,12 @@ import { ESysPreference } from 'picsur-shared/dist/entities/syspreference.entity'; -import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class ESysPreferenceBackend extends ESysPreference { @PrimaryGeneratedColumn() override id?: number; + @Index() @Column({ nullable: false, unique: true }) override key: string; diff --git a/backend/src/models/requests/authrequest.dto.ts b/backend/src/models/requests/authrequest.dto.ts index 4f3c942..454d45d 100644 --- a/backend/src/models/requests/authrequest.dto.ts +++ b/backend/src/models/requests/authrequest.dto.ts @@ -1,6 +1,7 @@ import { FastifyRequest } from 'fastify'; import { EUserBackend } from '../entities/user.entity'; +// Add typing to FastifyRequest to make using the user object easier export default interface AuthFasityRequest extends FastifyRequest { user: EUserBackend; } diff --git a/backend/src/models/requests/imageroute.dto.ts b/backend/src/models/requests/imageroute.dto.ts index 3960438..2a18dd5 100644 --- a/backend/src/models/requests/imageroute.dto.ts +++ b/backend/src/models/requests/imageroute.dto.ts @@ -1,10 +1,8 @@ -import { Type } from 'class-transformer'; -import { IsDefined, ValidateNested } from 'class-validator'; import { MultiPartFileDto } from './multipart.dto'; +import { IsMultiPartFile } from './multipart.validator'; +// A validation class for form based file upload of an image export class ImageUploadDto { - @IsDefined() - @ValidateNested() - @Type(() => MultiPartFileDto) + @IsMultiPartFile() image: MultiPartFileDto; } diff --git a/backend/src/models/requests/multipart.validator.ts b/backend/src/models/requests/multipart.validator.ts new file mode 100644 index 0000000..47dd07a --- /dev/null +++ b/backend/src/models/requests/multipart.validator.ts @@ -0,0 +1,16 @@ +import { Type } from 'class-transformer'; +import { IsDefined, ValidateNested } from 'class-validator'; +import { CombinePDecorators } from 'picsur-shared/dist/util/decorator'; +import { MultiPartFieldDto, MultiPartFileDto } from './multipart.dto'; + +export const IsMultiPartFile = CombinePDecorators( + IsDefined(), + ValidateNested(), + Type(() => MultiPartFileDto), +); + +export const IsMultiPartField = CombinePDecorators( + IsDefined(), + ValidateNested(), + Type(() => MultiPartFieldDto), +); diff --git a/backend/src/models/util/collection.ts b/backend/src/models/util/collection.ts index 6675df1..7affece 100644 --- a/backend/src/models/util/collection.ts +++ b/backend/src/models/util/collection.ts @@ -1,5 +1,7 @@ import { Repository } from 'typeorm'; +// This is a function that returns an array of all available columns in a database table +// It is used to fetch hidden columns from the database export function GetCols(repository: Repository): (keyof T)[] { return repository.metadata.columns.map( (col) => col.propertyName, diff --git a/backend/src/models/util/permissions.ts b/backend/src/models/util/permissions.validator.ts similarity index 100% rename from backend/src/models/util/permissions.ts rename to backend/src/models/util/permissions.validator.ts diff --git a/backend/src/routes/api/roles/roles.controller.ts b/backend/src/routes/api/roles/roles.controller.ts index 77df76d..67a5afd 100644 --- a/backend/src/routes/api/roles/roles.controller.ts +++ b/backend/src/routes/api/roles/roles.controller.ts @@ -31,7 +31,7 @@ import { SoulBoundRolesList, UndeletableRolesList } from '../../../models/dto/roles.dto'; -import { isPermissionsArray } from '../../../models/util/permissions'; +import { isPermissionsArray } from '../../../models/util/permissions.validator'; @Controller('api/roles') @RequiredPermissions(Permission.RoleManage)