mirror of
https://github.com/immich-app/immich.git
synced 2026-05-24 08:32:28 -04:00
013ea37a0d
* refactor!: change number to integer types * fix oversight
229 lines
10 KiB
TypeScript
229 lines
10 KiB
TypeScript
import { createZodDto } from 'nestjs-zod';
|
|
import { Place } from 'src/database';
|
|
import { HistoryBuilder } from 'src/decorators';
|
|
import { AlbumResponseSchema } from 'src/dtos/album.dto';
|
|
import { AssetResponseSchema } from 'src/dtos/asset-response.dto';
|
|
import { AssetOrder, AssetOrderSchema, AssetTypeSchema, AssetVisibilitySchema } from 'src/enum';
|
|
import { emptyStringToNull, isoDatetimeToDate, stringToBool } from 'src/validation';
|
|
import z from 'zod';
|
|
|
|
const BaseSearchSchema = z.object({
|
|
libraryId: z.uuidv4().nullish().describe('Library ID to filter by'),
|
|
type: AssetTypeSchema.optional(),
|
|
isEncoded: z.boolean().optional().describe('Filter by encoded status'),
|
|
isFavorite: z.boolean().optional().describe('Filter by favorite status'),
|
|
isMotion: z.boolean().optional().describe('Filter by motion photo status'),
|
|
isOffline: z.boolean().optional().describe('Filter by offline status'),
|
|
visibility: AssetVisibilitySchema.optional(),
|
|
createdBefore: isoDatetimeToDate.optional().describe('Filter by creation date (before)'),
|
|
createdAfter: isoDatetimeToDate.optional().describe('Filter by creation date (after)'),
|
|
updatedBefore: isoDatetimeToDate.optional().describe('Filter by update date (before)'),
|
|
updatedAfter: isoDatetimeToDate.optional().describe('Filter by update date (after)'),
|
|
trashedBefore: isoDatetimeToDate.optional().describe('Filter by trash date (before)'),
|
|
trashedAfter: isoDatetimeToDate.optional().describe('Filter by trash date (after)'),
|
|
takenBefore: isoDatetimeToDate.optional().describe('Filter by taken date (before)'),
|
|
takenAfter: isoDatetimeToDate.optional().describe('Filter by taken date (after)'),
|
|
city: emptyStringToNull(z.string().nullable()).optional().describe('Filter by city name'),
|
|
state: emptyStringToNull(z.string().nullable()).optional().describe('Filter by state/province name'),
|
|
country: emptyStringToNull(z.string().nullable()).optional().describe('Filter by country name'),
|
|
make: emptyStringToNull(z.string().nullable()).optional().describe('Filter by camera make'),
|
|
model: emptyStringToNull(z.string().nullable()).optional().describe('Filter by camera model'),
|
|
lensModel: emptyStringToNull(z.string().nullable()).optional().describe('Filter by lens model'),
|
|
isNotInAlbum: z.boolean().optional().describe('Filter assets not in any album'),
|
|
personIds: z.array(z.uuidv4()).optional().describe('Filter by person IDs'),
|
|
tagIds: z.array(z.uuidv4()).nullish().describe('Filter by tag IDs'),
|
|
albumIds: z.array(z.uuidv4()).optional().describe('Filter by album IDs'),
|
|
rating: z
|
|
.int()
|
|
.min(-1)
|
|
.max(5)
|
|
.nullish()
|
|
.describe('Filter by rating [1-5], or null for unrated')
|
|
.meta({
|
|
...new HistoryBuilder()
|
|
.added('v1')
|
|
.stable('v2')
|
|
.updated('v2.6.0', 'Using -1 as a rating is deprecated and will be removed in the next major version.')
|
|
.getExtensions(),
|
|
}),
|
|
ocr: z.string().optional().describe('Filter by OCR text content'),
|
|
});
|
|
|
|
const BaseSearchWithResultsSchema = BaseSearchSchema.extend({
|
|
withDeleted: z.boolean().optional().describe('Include deleted assets'),
|
|
withExif: z.boolean().optional().describe('Include EXIF data in response'),
|
|
size: z.int().min(1).max(1000).optional().describe('Number of results to return'),
|
|
});
|
|
|
|
const RandomSearchSchema = BaseSearchWithResultsSchema.extend({
|
|
withStacked: z.boolean().optional().describe('Include stacked assets'),
|
|
withPeople: z.boolean().optional().describe('Include people data in response'),
|
|
}).meta({ id: 'RandomSearchDto' });
|
|
|
|
const LargeAssetSearchSchema = BaseSearchWithResultsSchema.extend({
|
|
minFileSize: z.coerce.number().int().min(0).optional().describe('Minimum file size in bytes'),
|
|
size: z.coerce.number().int().min(1).max(1000).optional().describe('Number of results to return'),
|
|
}).meta({ id: 'LargeAssetSearchDto' });
|
|
|
|
const MetadataSearchSchema = RandomSearchSchema.extend({
|
|
id: z.uuidv4().optional().describe('Filter by asset ID'),
|
|
description: z.string().trim().optional().describe('Filter by description text'),
|
|
checksum: z.string().optional().describe('Filter by file checksum'),
|
|
originalFileName: z.string().trim().optional().describe('Filter by original file name'),
|
|
originalPath: z.string().optional().describe('Filter by original file path'),
|
|
previewPath: z.string().optional().describe('Filter by preview file path'),
|
|
thumbnailPath: z.string().optional().describe('Filter by thumbnail file path'),
|
|
encodedVideoPath: z.string().optional().describe('Filter by encoded video file path'),
|
|
order: AssetOrderSchema.default(AssetOrder.Desc).optional().describe('Sort order'),
|
|
page: z.int().min(1).optional().describe('Page number'),
|
|
}).meta({ id: 'MetadataSearchDto' });
|
|
|
|
const StatisticsSearchSchema = BaseSearchSchema.extend({
|
|
description: z.string().trim().optional().describe('Filter by description text'),
|
|
}).meta({ id: 'StatisticsSearchDto' });
|
|
|
|
const SmartSearchSchema = BaseSearchWithResultsSchema.extend({
|
|
query: z.string().trim().optional().describe('Natural language search query'),
|
|
queryAssetId: z.uuidv4().optional().describe('Asset ID to use as search reference'),
|
|
language: z.string().optional().describe('Search language code'),
|
|
page: z.int().min(1).optional().describe('Page number'),
|
|
}).meta({ id: 'SmartSearchDto' });
|
|
|
|
const SearchPlacesSchema = z
|
|
.object({
|
|
name: z.string().describe('Place name to search for'),
|
|
})
|
|
.meta({ id: 'SearchPlacesDto' });
|
|
|
|
const SearchPeopleSchema = z
|
|
.object({
|
|
name: z.string().describe('Person name to search for'),
|
|
withHidden: stringToBool.optional().describe('Include hidden people'),
|
|
})
|
|
.meta({ id: 'SearchPeopleDto' });
|
|
|
|
const PlacesResponseSchema = z
|
|
.object({
|
|
name: z.string().describe('Place name'),
|
|
latitude: z.number().describe('Latitude coordinate'),
|
|
longitude: z.number().describe('Longitude coordinate'),
|
|
admin1name: z.string().optional().describe('Administrative level 1 name (state/province)'),
|
|
admin2name: z.string().optional().describe('Administrative level 2 name (county/district)'),
|
|
})
|
|
.meta({ id: 'PlacesResponseDto' });
|
|
|
|
export enum SearchSuggestionType {
|
|
COUNTRY = 'country',
|
|
STATE = 'state',
|
|
CITY = 'city',
|
|
CAMERA_MAKE = 'camera-make',
|
|
CAMERA_MODEL = 'camera-model',
|
|
CAMERA_LENS_MODEL = 'camera-lens-model',
|
|
}
|
|
|
|
const SearchSuggestionTypeSchema = z
|
|
.enum(SearchSuggestionType)
|
|
.describe('Suggestion type')
|
|
.meta({ id: 'SearchSuggestionType' });
|
|
|
|
const SearchSuggestionRequestSchema = z
|
|
.object({
|
|
type: SearchSuggestionTypeSchema,
|
|
country: z.string().optional().describe('Filter by country'),
|
|
state: z.string().optional().describe('Filter by state/province'),
|
|
make: z.string().optional().describe('Filter by camera make'),
|
|
model: z.string().optional().describe('Filter by camera model'),
|
|
lensModel: z.string().optional().describe('Filter by lens model'),
|
|
includeNull: stringToBool
|
|
.optional()
|
|
.describe('Include null values in suggestions')
|
|
.meta(new HistoryBuilder().added('v1.111.0').stable('v2').getExtensions()),
|
|
})
|
|
.meta({ id: 'SearchSuggestionRequestDto' });
|
|
|
|
export class RandomSearchDto extends createZodDto(RandomSearchSchema) {}
|
|
export class LargeAssetSearchDto extends createZodDto(LargeAssetSearchSchema) {}
|
|
export class MetadataSearchDto extends createZodDto(MetadataSearchSchema) {}
|
|
export class StatisticsSearchDto extends createZodDto(StatisticsSearchSchema) {}
|
|
export class SmartSearchDto extends createZodDto(SmartSearchSchema) {}
|
|
export class SearchPlacesDto extends createZodDto(SearchPlacesSchema) {}
|
|
export class SearchPeopleDto extends createZodDto(SearchPeopleSchema) {}
|
|
export class PlacesResponseDto extends createZodDto(PlacesResponseSchema) {}
|
|
export class SearchSuggestionRequestDto extends createZodDto(SearchSuggestionRequestSchema) {}
|
|
|
|
export function mapPlaces(place: Place): PlacesResponseDto {
|
|
return {
|
|
name: place.name,
|
|
latitude: place.latitude,
|
|
longitude: place.longitude,
|
|
admin1name: place.admin1Name ?? undefined,
|
|
admin2name: place.admin2Name ?? undefined,
|
|
};
|
|
}
|
|
|
|
const SearchFacetCountResponseSchema = z
|
|
.object({
|
|
count: z.int().min(0).describe('Number of assets with this facet value'),
|
|
value: z.string().describe('Facet value'),
|
|
})
|
|
.meta({ id: 'SearchFacetCountResponseDto' });
|
|
|
|
const SearchFacetResponseSchema = z
|
|
.object({
|
|
fieldName: z.string().describe('Facet field name'),
|
|
counts: z.array(SearchFacetCountResponseSchema),
|
|
})
|
|
.meta({ id: 'SearchFacetResponseDto' });
|
|
|
|
const SearchAlbumResponseSchema = z
|
|
.object({
|
|
total: z.int().min(0).describe('Total number of matching albums'),
|
|
count: z.int().min(0).describe('Number of albums in this page'),
|
|
items: z.array(AlbumResponseSchema),
|
|
facets: z.array(SearchFacetResponseSchema),
|
|
})
|
|
.meta({ id: 'SearchAlbumResponseDto' });
|
|
|
|
const SearchAssetResponseSchema = z
|
|
.object({
|
|
total: z.int().min(0).describe('Total number of matching assets'),
|
|
count: z.int().min(0).describe('Number of assets in this page'),
|
|
items: z.array(AssetResponseSchema),
|
|
facets: z.array(SearchFacetResponseSchema),
|
|
nextPage: z.string().nullable().describe('Next page token'),
|
|
})
|
|
.meta({ id: 'SearchAssetResponseDto' });
|
|
|
|
const SearchResponseSchema = z
|
|
.object({
|
|
albums: SearchAlbumResponseSchema,
|
|
assets: SearchAssetResponseSchema,
|
|
})
|
|
.meta({ id: 'SearchResponseDto' });
|
|
|
|
export class SearchResponseDto extends createZodDto(SearchResponseSchema) {}
|
|
|
|
const SearchStatisticsResponseSchema = z
|
|
.object({
|
|
total: z.int().describe('Total number of matching assets'),
|
|
})
|
|
.meta({ id: 'SearchStatisticsResponseDto' });
|
|
|
|
export class SearchStatisticsResponseDto extends createZodDto(SearchStatisticsResponseSchema) {}
|
|
|
|
const SearchExploreItemSchema = z
|
|
.object({
|
|
value: z.string().describe('Explore value'),
|
|
data: AssetResponseSchema,
|
|
})
|
|
.meta({ id: 'SearchExploreItem' });
|
|
|
|
const SearchExploreResponseSchema = z
|
|
.object({
|
|
fieldName: z.string().describe('Explore field name'),
|
|
items: z.array(SearchExploreItemSchema),
|
|
})
|
|
.meta({ id: 'SearchExploreResponseDto' });
|
|
|
|
export class SearchExploreResponseDto extends createZodDto(SearchExploreResponseSchema) {}
|