Files
immich/server/src/domain/search/dto/search.dto.ts
Jason Rasmussen 81f0265095 chore: organize config, validation, decorators (#8118)
* refactor: validation

* refactor: utilities

* refactor: config
2024-03-20 16:04:03 -04:00

267 lines
4.8 KiB
TypeScript

import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IsEnum, IsInt, IsNotEmpty, IsString, Max, Min } from 'class-validator';
import { AssetOrder } from 'src/infra/entities/album.entity';
import { AssetType } from 'src/infra/entities/asset.entity';
import { GeodataPlacesEntity } from 'src/infra/entities/geodata-places.entity';
import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation';
class BaseSearchDto {
@ValidateUUID({ optional: true })
libraryId?: string;
@IsString()
@IsNotEmpty()
@Optional()
deviceId?: string;
@IsEnum(AssetType)
@Optional()
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
type?: AssetType;
@ValidateBoolean({ optional: true })
isArchived?: boolean;
@ValidateBoolean({ optional: true })
@ApiProperty({ default: false })
withArchived?: boolean;
@ValidateBoolean({ optional: true })
isEncoded?: boolean;
@ValidateBoolean({ optional: true })
isExternal?: boolean;
@ValidateBoolean({ optional: true })
isFavorite?: boolean;
@ValidateBoolean({ optional: true })
isMotion?: boolean;
@ValidateBoolean({ optional: true })
isOffline?: boolean;
@ValidateBoolean({ optional: true })
isReadOnly?: boolean;
@ValidateBoolean({ optional: true })
isVisible?: boolean;
@ValidateBoolean({ optional: true })
withDeleted?: boolean;
@ValidateBoolean({ optional: true })
withExif?: boolean;
@ValidateDate({ optional: true })
createdBefore?: Date;
@ValidateDate({ optional: true })
createdAfter?: Date;
@ValidateDate({ optional: true })
updatedBefore?: Date;
@ValidateDate({ optional: true })
updatedAfter?: Date;
@ValidateDate({ optional: true })
trashedBefore?: Date;
@ValidateDate({ optional: true })
trashedAfter?: Date;
@ValidateDate({ optional: true })
takenBefore?: Date;
@ValidateDate({ optional: true })
takenAfter?: Date;
@IsString()
@IsNotEmpty()
@Optional()
city?: string;
@IsString()
@IsNotEmpty()
@Optional()
state?: string;
@IsString()
@IsNotEmpty()
@Optional()
country?: string;
@IsString()
@IsNotEmpty()
@Optional()
make?: string;
@IsString()
@IsNotEmpty()
@Optional()
model?: string;
@IsString()
@IsNotEmpty()
@Optional()
lensModel?: string;
@IsInt()
@Min(1)
@Type(() => Number)
@Optional()
page?: number;
@IsInt()
@Min(1)
@Max(1000)
@Type(() => Number)
@Optional()
size?: number;
@ValidateBoolean({ optional: true })
isNotInAlbum?: boolean;
@ValidateUUID({ each: true, optional: true })
personIds?: string[];
}
export class MetadataSearchDto extends BaseSearchDto {
@ValidateUUID({ optional: true })
id?: string;
@IsString()
@IsNotEmpty()
@Optional()
deviceAssetId?: string;
@IsString()
@IsNotEmpty()
@Optional()
checksum?: string;
@ValidateBoolean({ optional: true })
withStacked?: boolean;
@ValidateBoolean({ optional: true })
withPeople?: boolean;
@IsString()
@IsNotEmpty()
@Optional()
originalFileName?: string;
@IsString()
@IsNotEmpty()
@Optional()
originalPath?: string;
@IsString()
@IsNotEmpty()
@Optional()
resizePath?: string;
@IsString()
@IsNotEmpty()
@Optional()
webpPath?: string;
@IsString()
@IsNotEmpty()
@Optional()
encodedVideoPath?: string;
@IsEnum(AssetOrder)
@Optional()
@ApiProperty({ enumName: 'AssetOrder', enum: AssetOrder })
order?: AssetOrder;
}
export class SmartSearchDto extends BaseSearchDto {
@IsString()
@IsNotEmpty()
query!: string;
}
// TODO: remove after implementing new search filters
/** @deprecated */
export class SearchDto {
@IsString()
@IsNotEmpty()
@Optional()
q?: string;
@IsString()
@IsNotEmpty()
@Optional()
query?: string;
@ValidateBoolean({ optional: true })
smart?: boolean;
/** @deprecated */
@ValidateBoolean({ optional: true })
clip?: boolean;
@IsEnum(AssetType)
@Optional()
type?: AssetType;
@ValidateBoolean({ optional: true })
recent?: boolean;
@ValidateBoolean({ optional: true })
motion?: boolean;
@ValidateBoolean({ optional: true })
withArchived?: boolean;
@IsInt()
@Min(1)
@Type(() => Number)
@Optional()
page?: number;
@IsInt()
@Min(1)
@Max(1000)
@Type(() => Number)
@Optional()
size?: number;
}
export class SearchPlacesDto {
@IsString()
@IsNotEmpty()
name!: string;
}
export class SearchPeopleDto {
@IsString()
@IsNotEmpty()
name!: string;
@ValidateBoolean({ optional: true })
withHidden?: boolean;
}
export class PlacesResponseDto {
name!: string;
latitude!: number;
longitude!: number;
admin1name?: string;
admin2name?: string;
}
export function mapPlaces(place: GeodataPlacesEntity): PlacesResponseDto {
return {
name: place.name,
latitude: place.latitude,
longitude: place.longitude,
admin1name: place.admin1Name,
admin2name: place.admin2Name,
};
}