fix(server): more asset upload validation and docs

This commit is contained in:
Michel Heusschen
2023-02-10 09:54:16 +01:00
parent e6f9d9a31a
commit 22a973e6e0
13 changed files with 301 additions and 28 deletions
@@ -16,6 +16,7 @@ import {
UploadedFiles,
Patch,
StreamableFile,
ParseFilePipe,
} from '@nestjs/common';
import { Authenticated } from '../../decorators/authenticated.decorator';
import { AssetService } from './asset.service';
@@ -31,7 +32,6 @@ import { CuratedObjectsResponseDto } from './response-dto/curated-objects-respon
import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto';
import { AssetResponseDto, ImmichReadStream } from '@app/domain';
import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-asset-response.dto';
import { AssetFileUploadDto } from './dto/asset-file-upload.dto';
import { CreateAssetDto, mapToUploadFile } from './dto/create-asset.dto';
import { AssetFileUploadResponseDto } from './response-dto/asset-file-upload-response.dto';
import { DeleteAssetResponseDto } from './response-dto/delete-asset-response.dto';
@@ -55,6 +55,7 @@ import { SharedLinkResponseDto } from '@app/domain';
import { UpdateAssetsToSharedLinkDto } from './dto/add-assets-to-shared-link.dto';
import { AssetSearchDto } from './dto/asset-search.dto';
import { assetUploadOption, ImmichFile } from '../../config/asset-upload.config';
import FileNotEmptyValidator from '../validation/file-not-empty-validator';
function asStreamableFile({ stream, type, length }: ImmichReadStream) {
return new StreamableFile(stream, { type, length });
@@ -80,12 +81,13 @@ export class AssetController {
@ApiConsumes('multipart/form-data')
@ApiBody({
description: 'Asset Upload Information',
type: AssetFileUploadDto,
type: CreateAssetDto,
})
async uploadFile(
@GetAuthUser() authUser: AuthUserDto,
@UploadedFiles() files: { assetData: ImmichFile[]; livePhotoData?: ImmichFile[] },
@Body(ValidationPipe) dto: CreateAssetDto,
@UploadedFiles(new ParseFilePipe({ validators: [new FileNotEmptyValidator(['assetData'])] }))
files: { assetData: ImmichFile[]; livePhotoData?: ImmichFile[] },
@Body(new ValidationPipe()) dto: CreateAssetDto,
@Response({ passthrough: true }) res: Res,
): Promise<AssetFileUploadResponseDto> {
const file = mapToUploadFile(files.assetData[0]);
@@ -1,6 +1,6 @@
import { AssetType } from '@app/infra';
import { ApiProperty } from '@nestjs/swagger';
import { IsBoolean, IsNotEmpty, IsOptional } from 'class-validator';
import { IsBoolean, IsEnum, IsNotEmpty, IsOptional } from 'class-validator';
import { ImmichFile } from '../../../config/asset-upload.config';
export class CreateAssetDto {
@@ -11,7 +11,8 @@ export class CreateAssetDto {
deviceId!: string;
@IsNotEmpty()
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
@IsEnum(AssetType)
@ApiProperty({ enum: Object.keys(AssetType) })
assetType!: AssetType;
@IsNotEmpty()
@@ -32,6 +33,12 @@ export class CreateAssetDto {
@IsOptional()
duration?: string;
@ApiProperty({ type: 'string', format: 'binary' })
assetData!: any;
@ApiProperty({ type: 'string', format: 'binary' })
livePhotoData?: any;
}
export interface UploadFile {
@@ -0,0 +1,25 @@
import { FileValidator, Injectable } from '@nestjs/common';
@Injectable()
export default class FileNotEmptyValidator extends FileValidator {
requiredFields: string[];
constructor(requiredFields: string[]) {
super({});
this.requiredFields = requiredFields;
}
isValid(files?: any): boolean {
if (!files) {
return false;
}
return this.requiredFields.every((field) => {
return files[field];
});
}
buildErrorMessage(): string {
return `Field(s) ${this.requiredFields.join(', ')} should not be empty`;
}
}
+47 -3
View File
@@ -1077,7 +1077,7 @@
"content": {
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/AssetFileUploadDto"
"$ref": "#/components/schemas/CreateAssetDto"
}
}
}
@@ -3758,16 +3758,60 @@
"profileImagePath"
]
},
"AssetFileUploadDto": {
"CreateAssetDto": {
"type": "object",
"properties": {
"assetType": {
"enum": [
"IMAGE",
"VIDEO",
"AUDIO",
"OTHER"
],
"type": "string"
},
"assetData": {
"type": "string",
"format": "binary"
},
"livePhotoData": {
"type": "string",
"format": "binary"
},
"deviceAssetId": {
"type": "string"
},
"deviceId": {
"type": "string"
},
"createdAt": {
"type": "string"
},
"modifiedAt": {
"type": "string"
},
"isFavorite": {
"type": "boolean"
},
"isVisible": {
"type": "boolean"
},
"fileExtension": {
"type": "string"
},
"duration": {
"type": "string"
}
},
"required": [
"assetData"
"assetType",
"assetData",
"deviceAssetId",
"deviceId",
"createdAt",
"modifiedAt",
"isFavorite",
"fileExtension"
]
},
"AssetFileUploadResponseDto": {