feat(server): asset search endpoint (#4931)

* feat(server): GET /assets endpoint

* chore: open api

* chore: use dumb name

* feat: search by make, model, lens, city, state, country

* chore: open api

* chore: pagination validation and tests

* chore: pr feedback
This commit is contained in:
Jason Rasmussen
2023-11-14 17:47:15 -05:00
committed by GitHub
parent 7a8f8e5472
commit 753dab8b3c
24 changed files with 3151 additions and 94 deletions
+30
View File
@@ -30,6 +30,8 @@ import {
AssetIdsDto,
AssetJobName,
AssetJobsDto,
AssetOrder,
AssetSearchDto,
AssetStatsDto,
DownloadArchiveInfo,
DownloadInfoDto,
@@ -91,6 +93,34 @@ export class AssetService {
this.configCore = SystemConfigCore.create(configRepository);
}
search(authUser: AuthUserDto, dto: AssetSearchDto) {
let checksum: Buffer | undefined = undefined;
if (dto.checksum) {
const encoding = dto.checksum.length === 28 ? 'base64' : 'hex';
checksum = Buffer.from(dto.checksum, encoding);
}
const enumToOrder = { [AssetOrder.ASC]: 'ASC', [AssetOrder.DESC]: 'DESC' } as const;
const order = dto.order ? enumToOrder[dto.order] : undefined;
return this.assetRepository
.search({
...dto,
order,
checksum,
ownerId: authUser.id,
})
.then((assets) =>
assets.map((asset) =>
mapAsset(asset, {
stripMetadata: false,
withStack: true,
}),
),
);
}
canUploadFile({ authUser, fieldName, file }: UploadRequest): true {
this.access.requireUploadAccess(authUser);
+155 -2
View File
@@ -1,8 +1,161 @@
import { AssetType } from '@app/infra/entities';
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IsBoolean, IsInt, IsPositive, IsString } from 'class-validator';
import { Optional, ValidateUUID } from '../../domain.util';
import { IsBoolean, IsEnum, IsInt, IsPositive, IsString, Min } from 'class-validator';
import { Optional, QueryBoolean, QueryDate, ValidateUUID } from '../../domain.util';
import { BulkIdsDto } from '../response-dto';
export enum AssetOrder {
ASC = 'asc',
DESC = 'desc',
}
export class AssetSearchDto {
@ValidateUUID({ optional: true })
id?: string;
@ValidateUUID({ optional: true })
libraryId?: string;
@IsString()
@Optional()
deviceAssetId?: string;
@IsString()
@Optional()
deviceId?: string;
@IsEnum(AssetType)
@Optional()
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
type?: AssetType;
@IsString()
@Optional()
checksum?: string;
@QueryBoolean({ optional: true })
isArchived?: boolean;
@QueryBoolean({ optional: true })
isEncoded?: boolean;
@QueryBoolean({ optional: true })
isExternal?: boolean;
@QueryBoolean({ optional: true })
isFavorite?: boolean;
@QueryBoolean({ optional: true })
isMotion?: boolean;
@QueryBoolean({ optional: true })
isOffline?: boolean;
@QueryBoolean({ optional: true })
isReadOnly?: boolean;
@QueryBoolean({ optional: true })
isVisible?: boolean;
@QueryBoolean({ optional: true })
withDeleted?: boolean;
@QueryBoolean({ optional: true })
withStacked?: boolean;
@QueryBoolean({ optional: true })
withExif?: boolean;
@QueryBoolean({ optional: true })
withPeople?: boolean;
@QueryDate({ optional: true })
createdBefore?: Date;
@QueryDate({ optional: true })
createdAfter?: Date;
@QueryDate({ optional: true })
updatedBefore?: Date;
@QueryDate({ optional: true })
updatedAfter?: Date;
@QueryDate({ optional: true })
trashedBefore?: Date;
@QueryDate({ optional: true })
trashedAfter?: Date;
@QueryDate({ optional: true })
takenBefore?: Date;
@QueryDate({ optional: true })
takenAfter?: Date;
@IsString()
@Optional()
originalFileName?: string;
@IsString()
@Optional()
originalPath?: string;
@IsString()
@Optional()
resizePath?: string;
@IsString()
@Optional()
webpPath?: string;
@IsString()
@Optional()
encodedVideoPath?: string;
@IsString()
@Optional()
city?: string;
@IsString()
@Optional()
state?: string;
@IsString()
@Optional()
country?: string;
@IsString()
@Optional()
make?: string;
@IsString()
@Optional()
model?: string;
@IsString()
@Optional()
lensModel?: string;
@IsEnum(AssetOrder)
@Optional()
@ApiProperty({ enumName: 'AssetOrder', enum: AssetOrder })
order?: AssetOrder;
@IsInt()
@Min(1)
@Type(() => Number)
@Optional()
page?: number;
@IsInt()
@Min(1)
@Type(() => Number)
@Optional()
size?: number;
}
export class AssetBulkUpdateDto extends BulkIdsDto {
@Optional()
@IsBoolean()