mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
013ea37a0d
* refactor!: change number to integer types * fix oversight
389 lines
14 KiB
TypeScript
389 lines
14 KiB
TypeScript
import { createZodDto } from 'nestjs-zod';
|
|
import { SystemConfig } from 'src/config';
|
|
import {
|
|
CLIPConfigSchema,
|
|
DuplicateDetectionConfigSchema,
|
|
FacialRecognitionConfigSchema,
|
|
OcrConfigSchema,
|
|
} from 'src/dtos/model-config.dto';
|
|
import {
|
|
AudioCodec,
|
|
AudioCodecSchema,
|
|
ColorspaceSchema,
|
|
CQModeSchema,
|
|
ImageFormatSchema,
|
|
LogLevelSchema,
|
|
OAuthTokenEndpointAuthMethodSchema,
|
|
ToneMappingSchema,
|
|
TranscodeHardwareAccelerationSchema,
|
|
TranscodePolicySchema,
|
|
VideoCodecSchema,
|
|
VideoContainerSchema,
|
|
} from 'src/enum';
|
|
import { isValidTime } from 'src/validation';
|
|
import z from 'zod';
|
|
|
|
/** Coerces 'true'/'false' strings to boolean, but also allows booleans. */
|
|
const configBool = z
|
|
.preprocess((val) => {
|
|
if (val === 'true') {
|
|
return true;
|
|
}
|
|
if (val === 'false') {
|
|
return false;
|
|
}
|
|
return val;
|
|
}, z.boolean())
|
|
.meta({ type: 'boolean' });
|
|
|
|
const JobSettingsSchema = z
|
|
.object({
|
|
concurrency: z.int().min(1).describe('Concurrency'),
|
|
})
|
|
.meta({ id: 'JobSettingsDto' });
|
|
|
|
const cronExpressionSchema = z
|
|
.string()
|
|
.regex(/(((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7}/, 'Invalid cron expression')
|
|
.describe('Cron expression');
|
|
|
|
const DatabaseBackupSchema = z
|
|
.object({
|
|
enabled: configBool.describe('Enabled'),
|
|
cronExpression: cronExpressionSchema,
|
|
keepLastAmount: z.int().min(1).describe('Keep last amount'),
|
|
})
|
|
.meta({ id: 'DatabaseBackupConfig' });
|
|
|
|
const SystemConfigBackupsSchema = z.object({ database: DatabaseBackupSchema }).meta({ id: 'SystemConfigBackupsDto' });
|
|
|
|
const SystemConfigFFmpegSchema = z
|
|
.object({
|
|
crf: z.coerce.number().int().min(0).max(51).describe('CRF'),
|
|
threads: z.coerce.number().int().min(0).describe('Threads'),
|
|
preset: z.string().describe('Preset'),
|
|
targetVideoCodec: VideoCodecSchema,
|
|
acceptedVideoCodecs: z.array(VideoCodecSchema).describe('Accepted video codecs'),
|
|
targetAudioCodec: AudioCodecSchema,
|
|
acceptedAudioCodecs: z
|
|
.array(AudioCodecSchema)
|
|
.transform((value): AudioCodec[] => value.map((v) => (v === AudioCodec.Libopus ? AudioCodec.Opus : v)))
|
|
.describe('Accepted audio codecs'),
|
|
acceptedContainers: z.array(VideoContainerSchema).describe('Accepted containers'),
|
|
targetResolution: z.string().describe('Target resolution'),
|
|
maxBitrate: z.string().describe('Max bitrate'),
|
|
bframes: z.coerce.number().int().min(-1).max(16).describe('B-frames'),
|
|
refs: z.coerce.number().int().min(0).max(6).describe('References'),
|
|
gopSize: z.coerce.number().int().min(0).describe('GOP size'),
|
|
temporalAQ: configBool.describe('Temporal AQ'),
|
|
cqMode: CQModeSchema,
|
|
twoPass: configBool.describe('Two pass'),
|
|
preferredHwDevice: z.string().describe('Preferred hardware device'),
|
|
transcode: TranscodePolicySchema,
|
|
accel: TranscodeHardwareAccelerationSchema,
|
|
accelDecode: configBool.describe('Accelerated decode'),
|
|
tonemap: ToneMappingSchema,
|
|
})
|
|
.meta({ id: 'SystemConfigFFmpegDto' });
|
|
|
|
const SystemConfigJobSchema = z
|
|
.object({
|
|
thumbnailGeneration: JobSettingsSchema,
|
|
metadataExtraction: JobSettingsSchema,
|
|
videoConversion: JobSettingsSchema,
|
|
faceDetection: JobSettingsSchema,
|
|
smartSearch: JobSettingsSchema,
|
|
backgroundTask: JobSettingsSchema,
|
|
migration: JobSettingsSchema,
|
|
search: JobSettingsSchema,
|
|
sidecar: JobSettingsSchema,
|
|
library: JobSettingsSchema,
|
|
notifications: JobSettingsSchema,
|
|
ocr: JobSettingsSchema,
|
|
workflow: JobSettingsSchema,
|
|
editor: JobSettingsSchema,
|
|
})
|
|
.meta({ id: 'SystemConfigJobDto' });
|
|
|
|
const SystemConfigLibraryScanSchema = z
|
|
.object({
|
|
enabled: configBool.describe('Enabled'),
|
|
cronExpression: cronExpressionSchema,
|
|
})
|
|
.meta({ id: 'SystemConfigLibraryScanDto' });
|
|
|
|
const SystemConfigLibraryWatchSchema = z
|
|
.object({ enabled: configBool.describe('Enabled') })
|
|
.meta({ id: 'SystemConfigLibraryWatchDto' });
|
|
|
|
const SystemConfigLibrarySchema = z
|
|
.object({ scan: SystemConfigLibraryScanSchema, watch: SystemConfigLibraryWatchSchema })
|
|
.meta({ id: 'SystemConfigLibraryDto' });
|
|
|
|
const SystemConfigLoggingSchema = z
|
|
.object({
|
|
enabled: configBool.describe('Enabled'),
|
|
level: LogLevelSchema,
|
|
})
|
|
.meta({ id: 'SystemConfigLoggingDto' });
|
|
|
|
const MachineLearningAvailabilityChecksSchema = z
|
|
.object({
|
|
enabled: configBool.describe('Enabled'),
|
|
timeout: z.int(),
|
|
interval: z.int(),
|
|
})
|
|
.meta({ id: 'MachineLearningAvailabilityChecksDto' });
|
|
|
|
const SystemConfigMachineLearningSchema = z
|
|
.object({
|
|
enabled: configBool.describe('Enabled'),
|
|
urls: z.array(z.string()).min(1).describe('ML service URLs'),
|
|
availabilityChecks: MachineLearningAvailabilityChecksSchema,
|
|
clip: CLIPConfigSchema,
|
|
duplicateDetection: DuplicateDetectionConfigSchema,
|
|
facialRecognition: FacialRecognitionConfigSchema,
|
|
ocr: OcrConfigSchema,
|
|
})
|
|
.meta({ id: 'SystemConfigMachineLearningDto' });
|
|
|
|
const SystemConfigMapSchema = z
|
|
.object({
|
|
enabled: configBool.describe('Enabled'),
|
|
lightStyle: z.url().describe('Light map style URL'),
|
|
darkStyle: z.url().describe('Dark map style URL'),
|
|
})
|
|
.meta({ id: 'SystemConfigMapDto' });
|
|
|
|
const SystemConfigNewVersionCheckSchema = z
|
|
.object({ enabled: configBool.describe('Enabled') })
|
|
.meta({ id: 'SystemConfigNewVersionCheckDto' });
|
|
|
|
const SystemConfigNightlyTasksSchema = z
|
|
.object({
|
|
startTime: isValidTime.describe('Start time'),
|
|
databaseCleanup: configBool.describe('Database cleanup'),
|
|
missingThumbnails: configBool.describe('Missing thumbnails'),
|
|
clusterNewFaces: configBool.describe('Cluster new faces'),
|
|
generateMemories: configBool.describe('Generate memories'),
|
|
syncQuotaUsage: configBool.describe('Sync quota usage'),
|
|
})
|
|
.meta({ id: 'SystemConfigNightlyTasksDto' });
|
|
|
|
const SystemConfigOAuthSchema = z
|
|
.object({
|
|
autoLaunch: configBool.describe('Auto launch'),
|
|
autoRegister: configBool.describe('Auto register'),
|
|
buttonText: z.string().describe('Button text'),
|
|
clientId: z.string().describe('Client ID'),
|
|
clientSecret: z.string().describe('Client secret'),
|
|
tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethodSchema,
|
|
timeout: z.int().min(1).describe('Timeout'),
|
|
allowInsecureRequests: configBool.describe('Allow insecure requests'),
|
|
defaultStorageQuota: z.int().min(0).nullable().describe('Default storage quota'),
|
|
enabled: configBool.describe('Enabled'),
|
|
issuerUrl: z
|
|
.string()
|
|
.refine((url) => url.length === 0 || z.url().safeParse(url).success, {
|
|
error: 'Issuer URL must be an empty string or a valid URL',
|
|
})
|
|
.describe('Issuer URL'),
|
|
scope: z.string().describe('Scope'),
|
|
prompt: z.string().describe('OAuth prompt parameter (e.g. select_account, login, consent)'),
|
|
endSessionEndpoint: z
|
|
.string()
|
|
.refine((url) => url.length === 0 || z.url().safeParse(url).success, {
|
|
error: 'endSessionEndpoint must be an empty string or a valid URL',
|
|
})
|
|
.describe('End session endpoint'),
|
|
signingAlgorithm: z.string().describe('Signing algorithm'),
|
|
profileSigningAlgorithm: z.string().describe('Profile signing algorithm'),
|
|
storageLabelClaim: z.string().describe('Storage label claim'),
|
|
storageQuotaClaim: z.string().describe('Storage quota claim'),
|
|
roleClaim: z.string().describe('Role claim'),
|
|
mobileOverrideEnabled: configBool.describe('Mobile override enabled'),
|
|
mobileRedirectUri: z.string().describe('Mobile redirect URI (set to empty string to disable)'),
|
|
})
|
|
.transform((value, ctx) => {
|
|
if (!value.mobileOverrideEnabled || value.mobileRedirectUri === '') {
|
|
return value;
|
|
}
|
|
|
|
if (!z.url().safeParse(value.mobileRedirectUri).success) {
|
|
ctx.issues.push({
|
|
code: 'custom',
|
|
message: 'Mobile redirect URI must be an empty string or a valid URL',
|
|
input: value.mobileRedirectUri,
|
|
});
|
|
return z.NEVER;
|
|
}
|
|
|
|
return value;
|
|
})
|
|
.meta({
|
|
id: 'SystemConfigOAuthDto',
|
|
});
|
|
|
|
const SystemConfigPasswordLoginSchema = z
|
|
.object({ enabled: configBool.describe('Enabled') })
|
|
.meta({ id: 'SystemConfigPasswordLoginDto' });
|
|
|
|
const SystemConfigReverseGeocodingSchema = z
|
|
.object({ enabled: configBool.describe('Enabled') })
|
|
.meta({ id: 'SystemConfigReverseGeocodingDto' });
|
|
|
|
const SystemConfigFacesSchema = z
|
|
.object({ import: configBool.describe('Import') })
|
|
.meta({ id: 'SystemConfigFacesDto' });
|
|
const SystemConfigMetadataSchema = z.object({ faces: SystemConfigFacesSchema }).meta({ id: 'SystemConfigMetadataDto' });
|
|
|
|
const SystemConfigServerSchema = z
|
|
.object({
|
|
externalDomain: z
|
|
.string()
|
|
.refine((url) => url.length === 0 || z.url().safeParse(url).success, {
|
|
error: 'External domain must be an empty string or a valid URL',
|
|
})
|
|
.describe('External domain'),
|
|
loginPageMessage: z.string().describe('Login page message'),
|
|
publicUsers: configBool.describe('Public users'),
|
|
})
|
|
.meta({ id: 'SystemConfigServerDto' });
|
|
|
|
const SystemConfigSmtpTransportSchema = z
|
|
.object({
|
|
ignoreCert: configBool.describe('Whether to ignore SSL certificate errors'),
|
|
host: z.string().describe('SMTP server hostname'),
|
|
port: z.int().min(0).max(65_535).describe('SMTP server port'),
|
|
secure: configBool.describe('Whether to use secure connection (TLS/SSL)'),
|
|
username: z.string().describe('SMTP username'),
|
|
password: z.string().describe('SMTP password'),
|
|
})
|
|
.meta({ id: 'SystemConfigSmtpTransportDto' });
|
|
|
|
const SystemConfigSmtpSchema = z
|
|
.object({
|
|
enabled: configBool.describe('Whether SMTP email notifications are enabled'),
|
|
from: z.string().describe('Email address to send from'),
|
|
replyTo: z.string().describe('Email address for replies'),
|
|
transport: SystemConfigSmtpTransportSchema,
|
|
})
|
|
.meta({ id: 'SystemConfigSmtpDto' });
|
|
|
|
const SystemConfigNotificationsSchema = z
|
|
.object({ smtp: SystemConfigSmtpSchema })
|
|
.meta({ id: 'SystemConfigNotificationsDto' });
|
|
|
|
const SystemConfigTemplateEmailsSchema = z
|
|
.object({
|
|
albumInviteTemplate: z.string().describe('Album invite template'),
|
|
welcomeTemplate: z.string().describe('Welcome template'),
|
|
albumUpdateTemplate: z.string().describe('Album update template'),
|
|
})
|
|
.meta({ id: 'SystemConfigTemplateEmailsDto' });
|
|
const SystemConfigTemplatesSchema = z
|
|
.object({ email: SystemConfigTemplateEmailsSchema })
|
|
.meta({ id: 'SystemConfigTemplatesDto' });
|
|
|
|
const SystemConfigStorageTemplateSchema = z
|
|
.object({
|
|
enabled: configBool.describe('Enabled'),
|
|
hashVerificationEnabled: configBool.describe('Hash verification enabled'),
|
|
template: z.string().describe('Template'),
|
|
})
|
|
.meta({ id: 'SystemConfigStorageTemplateDto' });
|
|
|
|
const SystemConfigTemplateStorageOptionSchema = z
|
|
.object({
|
|
yearOptions: z.array(z.string()).describe('Available year format options for storage template'),
|
|
monthOptions: z.array(z.string()).describe('Available month format options for storage template'),
|
|
weekOptions: z.array(z.string()).describe('Available week format options for storage template'),
|
|
dayOptions: z.array(z.string()).describe('Available day format options for storage template'),
|
|
hourOptions: z.array(z.string()).describe('Available hour format options for storage template'),
|
|
minuteOptions: z.array(z.string()).describe('Available minute format options for storage template'),
|
|
secondOptions: z.array(z.string()).describe('Available second format options for storage template'),
|
|
presetOptions: z.array(z.string()).describe('Available preset template options'),
|
|
})
|
|
.meta({ id: 'SystemConfigTemplateStorageOptionDto' });
|
|
|
|
const SystemConfigThemeSchema = z
|
|
.object({ customCss: z.string().describe('Custom CSS for theming') })
|
|
.meta({ id: 'SystemConfigThemeDto' });
|
|
|
|
const SystemConfigGeneratedImageSchema = z
|
|
.object({
|
|
format: ImageFormatSchema,
|
|
quality: z.int().min(1).max(100).describe('Quality'),
|
|
size: z.int().min(1).describe('Size'),
|
|
progressive: configBool.default(false).optional().describe('Progressive'),
|
|
})
|
|
.meta({ id: 'SystemConfigGeneratedImageDto' });
|
|
|
|
const SystemConfigGeneratedFullsizeImageSchema = z
|
|
.object({
|
|
enabled: configBool.describe('Enabled'),
|
|
format: ImageFormatSchema,
|
|
quality: z.int().min(1).max(100).describe('Quality'),
|
|
progressive: configBool.default(false).optional().describe('Progressive'),
|
|
})
|
|
.meta({ id: 'SystemConfigGeneratedFullsizeImageDto' });
|
|
|
|
const SystemConfigImageSchema = z
|
|
.object({
|
|
thumbnail: SystemConfigGeneratedImageSchema,
|
|
preview: SystemConfigGeneratedImageSchema,
|
|
fullsize: SystemConfigGeneratedFullsizeImageSchema,
|
|
colorspace: ColorspaceSchema,
|
|
extractEmbedded: configBool.describe('Extract embedded'),
|
|
})
|
|
.meta({ id: 'SystemConfigImageDto' });
|
|
|
|
const SystemConfigTrashSchema = z
|
|
.object({
|
|
enabled: configBool.describe('Enabled'),
|
|
days: z.int().min(0).describe('Days'),
|
|
})
|
|
.meta({ id: 'SystemConfigTrashDto' });
|
|
|
|
const SystemConfigUserSchema = z
|
|
.object({
|
|
deleteDelay: z.int().min(1).describe('Delete delay'),
|
|
})
|
|
.meta({ id: 'SystemConfigUserDto' });
|
|
|
|
export const SystemConfigSchema = z
|
|
.object({
|
|
backup: SystemConfigBackupsSchema,
|
|
ffmpeg: SystemConfigFFmpegSchema,
|
|
logging: SystemConfigLoggingSchema,
|
|
machineLearning: SystemConfigMachineLearningSchema,
|
|
map: SystemConfigMapSchema,
|
|
newVersionCheck: SystemConfigNewVersionCheckSchema,
|
|
nightlyTasks: SystemConfigNightlyTasksSchema,
|
|
oauth: SystemConfigOAuthSchema,
|
|
passwordLogin: SystemConfigPasswordLoginSchema,
|
|
reverseGeocoding: SystemConfigReverseGeocodingSchema,
|
|
metadata: SystemConfigMetadataSchema,
|
|
storageTemplate: SystemConfigStorageTemplateSchema,
|
|
job: SystemConfigJobSchema,
|
|
image: SystemConfigImageSchema,
|
|
trash: SystemConfigTrashSchema,
|
|
theme: SystemConfigThemeSchema,
|
|
library: SystemConfigLibrarySchema,
|
|
notifications: SystemConfigNotificationsSchema,
|
|
templates: SystemConfigTemplatesSchema,
|
|
server: SystemConfigServerSchema,
|
|
user: SystemConfigUserSchema,
|
|
})
|
|
.describe('System configuration')
|
|
.meta({ id: 'SystemConfigDto' });
|
|
|
|
export class SystemConfigFFmpegDto extends createZodDto(SystemConfigFFmpegSchema) {}
|
|
export class SystemConfigSmtpDto extends createZodDto(SystemConfigSmtpSchema) {}
|
|
export class SystemConfigTemplateStorageOptionDto extends createZodDto(SystemConfigTemplateStorageOptionSchema) {}
|
|
export class SystemConfigDto extends createZodDto(SystemConfigSchema) {}
|
|
|
|
export function mapConfig(config: SystemConfig): SystemConfigDto {
|
|
return config;
|
|
}
|