refactor(server): zod int validation

pull/28804/head
timonrieger 2026-06-03 18:00:40 +02:00
parent 911dde39c9
commit dc2f64ff6d
No known key found for this signature in database
3 changed files with 18 additions and 11 deletions

View File

@ -24,7 +24,7 @@ import { DB } from 'src/schema';
import { immich_uuid_v7 } from 'src/schema/functions';
import { ExtensionVersion, VectorExtension } from 'src/types';
import { vectorIndexQuery } from 'src/utils/database';
import { isValidInteger } from 'src/validation';
import z from 'zod';
export let cachedVectorExtension: VectorExtension | undefined;
export async function getVectorExtension(runner: Kysely<DB>): Promise<VectorExtension> {
@ -292,7 +292,13 @@ export class DatabaseRepository {
`.execute(this.db);
const dimSize = rows[0]?.dimsize;
if (!isValidInteger(dimSize, { min: 1, max: 2 ** 16 })) {
if (
!z
.int()
.min(1)
.max(2 ** 16)
.safeParse(dimSize).success
) {
this.logger.warn(`Could not retrieve dimension size of column '${column}' in table '${table}', assuming 512`);
return 512;
}
@ -300,7 +306,13 @@ export class DatabaseRepository {
}
async setDimensionSize(dimSize: number): Promise<void> {
if (!isValidInteger(dimSize, { min: 1, max: 2 ** 16 })) {
if (
!z
.int()
.min(1)
.max(2 ** 16)
.safeParse(dimSize).success
) {
throw new Error(`Invalid CLIP dimension size: ${dimSize}`);
}

View File

@ -8,7 +8,7 @@ import { DB } from 'src/schema';
import { AssetExifTable } from 'src/schema/tables/asset-exif.table';
import { anyUuid, searchAssetBuilder, withExifInner } from 'src/utils/database';
import { paginationHelper } from 'src/utils/pagination';
import { isValidInteger } from 'src/validation';
import z from 'zod';
export interface SearchAssetIdOptions {
checksum?: Buffer;
@ -278,7 +278,7 @@ export class SearchRepository {
],
})
searchSmart(pagination: SearchPaginationOptions, options: SmartSearchOptions) {
if (!isValidInteger(pagination.size, { min: 1, max: 1000 })) {
if (!z.int().min(1).max(1000).safeParse(pagination.size).success) {
throw new Error(`Invalid value for 'size': ${pagination.size}`);
}
@ -313,7 +313,7 @@ export class SearchRepository {
],
})
searchFaces({ userIds, embedding, numResults, maxDistance, hasPerson, minBirthDate }: FaceEmbeddingSearch) {
if (!isValidInteger(numResults, { min: 1, max: 1000 })) {
if (!z.int().min(1).max(1000).safeParse(numResults).success) {
throw new Error(`Invalid value for 'numResults': ${numResults}`);
}

View File

@ -125,11 +125,6 @@ const FilenameParamSchema = z.object({
export class FilenameParamDto extends createZodDto(FilenameParamSchema) {}
export const isValidInteger = (value: number, options: { min?: number; max?: number }): value is number => {
const { min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER } = options;
return Number.isInteger(value) && value >= min && value <= max;
};
/**
* Unified email validation
* Converts email strings to lowercase and validates against HTML5 email regex