From 7b1112f3e356d1fd272948fbadbebc56664444ed Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Tue, 14 May 2024 14:43:49 -0400 Subject: [PATCH] refactor(server): system config (#9484) --- server/src/config.ts | 336 +++++++++++++++++- server/src/cores/storage.core.ts | 2 +- server/src/cores/system-config.core.ts | 170 +-------- server/src/dtos/system-config.dto.ts | 4 +- server/src/entities/system-config.entity.ts | 196 +--------- server/src/interfaces/event.interface.ts | 2 +- server/src/interfaces/logger.interface.ts | 2 +- server/src/interfaces/media.interface.ts | 2 +- server/src/main.ts | 2 +- server/src/repositories/logger.repository.ts | 2 +- server/src/repositories/media.repository.ts | 2 +- server/src/services/auth.service.ts | 2 +- server/src/services/job.service.spec.ts | 3 +- server/src/services/library.service.spec.ts | 3 +- server/src/services/media.service.spec.ts | 8 +- server/src/services/media.service.ts | 12 +- server/src/services/person.service.spec.ts | 3 +- server/src/services/person.service.ts | 2 +- server/src/services/server-info.service.ts | 2 +- .../services/storage-template.service.spec.ts | 5 +- .../src/services/storage-template.service.ts | 2 +- .../services/system-config.service.spec.ts | 7 +- server/src/services/system-config.service.ts | 5 +- server/src/utils/media.ts | 2 +- 24 files changed, 379 insertions(+), 397 deletions(-) diff --git a/server/src/config.ts b/server/src/config.ts index f6af82a94d..a9a9b2398c 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -1,12 +1,344 @@ import { RegisterQueueOptions } from '@nestjs/bullmq'; import { ConfigModuleOptions } from '@nestjs/config'; +import { CronExpression } from '@nestjs/schedule'; import { QueueOptions } from 'bullmq'; import { Request, Response } from 'express'; import { RedisOptions } from 'ioredis'; import Joi from 'joi'; import { CLS_ID, ClsModuleOptions } from 'nestjs-cls'; -import { LogLevel } from 'src/entities/system-config.entity'; -import { QueueName } from 'src/interfaces/job.interface'; +import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.interface'; + +export enum TranscodePolicy { + ALL = 'all', + OPTIMAL = 'optimal', + BITRATE = 'bitrate', + REQUIRED = 'required', + DISABLED = 'disabled', +} + +export enum TranscodeTarget { + NONE, + AUDIO, + VIDEO, + ALL, +} + +export enum VideoCodec { + H264 = 'h264', + HEVC = 'hevc', + VP9 = 'vp9', + AV1 = 'av1', +} + +export enum AudioCodec { + MP3 = 'mp3', + AAC = 'aac', + LIBOPUS = 'libopus', +} + +export enum TranscodeHWAccel { + NVENC = 'nvenc', + QSV = 'qsv', + VAAPI = 'vaapi', + RKMPP = 'rkmpp', + DISABLED = 'disabled', +} + +export enum ToneMapping { + HABLE = 'hable', + MOBIUS = 'mobius', + REINHARD = 'reinhard', + DISABLED = 'disabled', +} + +export enum CQMode { + AUTO = 'auto', + CQP = 'cqp', + ICQ = 'icq', +} + +export enum Colorspace { + SRGB = 'srgb', + P3 = 'p3', +} + +export enum ImageFormat { + JPEG = 'jpeg', + WEBP = 'webp', +} + +export enum LogLevel { + VERBOSE = 'verbose', + DEBUG = 'debug', + LOG = 'log', + WARN = 'warn', + ERROR = 'error', + FATAL = 'fatal', +} + +export interface SystemConfig { + ffmpeg: { + crf: number; + threads: number; + preset: string; + targetVideoCodec: VideoCodec; + acceptedVideoCodecs: VideoCodec[]; + targetAudioCodec: AudioCodec; + acceptedAudioCodecs: AudioCodec[]; + targetResolution: string; + maxBitrate: string; + bframes: number; + refs: number; + gopSize: number; + npl: number; + temporalAQ: boolean; + cqMode: CQMode; + twoPass: boolean; + preferredHwDevice: string; + transcode: TranscodePolicy; + accel: TranscodeHWAccel; + tonemap: ToneMapping; + }; + job: Record; + logging: { + enabled: boolean; + level: LogLevel; + }; + machineLearning: { + enabled: boolean; + url: string; + clip: { + enabled: boolean; + modelName: string; + }; + facialRecognition: { + enabled: boolean; + modelName: string; + minScore: number; + minFaces: number; + maxDistance: number; + }; + }; + map: { + enabled: boolean; + lightStyle: string; + darkStyle: string; + }; + reverseGeocoding: { + enabled: boolean; + }; + oauth: { + autoLaunch: boolean; + autoRegister: boolean; + buttonText: string; + clientId: string; + clientSecret: string; + defaultStorageQuota: number; + enabled: boolean; + issuerUrl: string; + mobileOverrideEnabled: boolean; + mobileRedirectUri: string; + scope: string; + signingAlgorithm: string; + storageLabelClaim: string; + storageQuotaClaim: string; + }; + passwordLogin: { + enabled: boolean; + }; + storageTemplate: { + enabled: boolean; + hashVerificationEnabled: boolean; + template: string; + }; + image: { + thumbnailFormat: ImageFormat; + thumbnailSize: number; + previewFormat: ImageFormat; + previewSize: number; + quality: number; + colorspace: Colorspace; + extractEmbedded: boolean; + }; + newVersionCheck: { + enabled: boolean; + }; + trash: { + enabled: boolean; + days: number; + }; + theme: { + customCss: string; + }; + library: { + scan: { + enabled: boolean; + cronExpression: string; + }; + watch: { + enabled: boolean; + }; + }; + notifications: { + smtp: { + enabled: boolean; + from: string; + replyTo: string; + transport: { + ignoreCert: boolean; + host: string; + port: number; + username: string; + password: string; + }; + }; + }; + server: { + externalDomain: string; + loginPageMessage: string; + }; + user: { + deleteDelay: number; + }; +} + +export const defaults = Object.freeze({ + ffmpeg: { + crf: 23, + threads: 0, + preset: 'ultrafast', + targetVideoCodec: VideoCodec.H264, + acceptedVideoCodecs: [VideoCodec.H264], + targetAudioCodec: AudioCodec.AAC, + acceptedAudioCodecs: [AudioCodec.AAC, AudioCodec.MP3, AudioCodec.LIBOPUS], + targetResolution: '720', + maxBitrate: '0', + bframes: -1, + refs: 0, + gopSize: 0, + npl: 0, + temporalAQ: false, + cqMode: CQMode.AUTO, + twoPass: false, + preferredHwDevice: 'auto', + transcode: TranscodePolicy.REQUIRED, + tonemap: ToneMapping.HABLE, + accel: TranscodeHWAccel.DISABLED, + }, + job: { + [QueueName.BACKGROUND_TASK]: { concurrency: 5 }, + [QueueName.SMART_SEARCH]: { concurrency: 2 }, + [QueueName.METADATA_EXTRACTION]: { concurrency: 5 }, + [QueueName.FACE_DETECTION]: { concurrency: 2 }, + [QueueName.SEARCH]: { concurrency: 5 }, + [QueueName.SIDECAR]: { concurrency: 5 }, + [QueueName.LIBRARY]: { concurrency: 5 }, + [QueueName.MIGRATION]: { concurrency: 5 }, + [QueueName.THUMBNAIL_GENERATION]: { concurrency: 5 }, + [QueueName.VIDEO_CONVERSION]: { concurrency: 1 }, + [QueueName.NOTIFICATION]: { concurrency: 5 }, + }, + logging: { + enabled: true, + level: LogLevel.LOG, + }, + machineLearning: { + enabled: process.env.IMMICH_MACHINE_LEARNING_ENABLED !== 'false', + url: process.env.IMMICH_MACHINE_LEARNING_URL || 'http://immich-machine-learning:3003', + clip: { + enabled: true, + modelName: 'ViT-B-32__openai', + }, + facialRecognition: { + enabled: true, + modelName: 'buffalo_l', + minScore: 0.7, + maxDistance: 0.5, + minFaces: 3, + }, + }, + map: { + enabled: true, + lightStyle: '', + darkStyle: '', + }, + reverseGeocoding: { + enabled: true, + }, + oauth: { + autoLaunch: false, + autoRegister: true, + buttonText: 'Login with OAuth', + clientId: '', + clientSecret: '', + defaultStorageQuota: 0, + enabled: false, + issuerUrl: '', + mobileOverrideEnabled: false, + mobileRedirectUri: '', + scope: 'openid email profile', + signingAlgorithm: 'RS256', + storageLabelClaim: 'preferred_username', + storageQuotaClaim: 'immich_quota', + }, + passwordLogin: { + enabled: true, + }, + storageTemplate: { + enabled: false, + hashVerificationEnabled: true, + template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}', + }, + image: { + thumbnailFormat: ImageFormat.WEBP, + thumbnailSize: 250, + previewFormat: ImageFormat.JPEG, + previewSize: 1440, + quality: 80, + colorspace: Colorspace.P3, + extractEmbedded: false, + }, + newVersionCheck: { + enabled: true, + }, + trash: { + enabled: true, + days: 30, + }, + theme: { + customCss: '', + }, + library: { + scan: { + enabled: true, + cronExpression: CronExpression.EVERY_DAY_AT_MIDNIGHT, + }, + watch: { + enabled: false, + }, + }, + server: { + externalDomain: '', + loginPageMessage: '', + }, + notifications: { + smtp: { + enabled: false, + from: '', + replyTo: '', + transport: { + ignoreCert: false, + host: '', + port: 587, + username: '', + password: '', + }, + }, + }, + user: { + deleteDelay: 7, + }, +}); const WHEN_DB_URL_SET = Joi.when('DB_URL', { is: Joi.exist(), diff --git a/server/src/cores/storage.core.ts b/server/src/cores/storage.core.ts index 4e5f4742a4..eace24d5be 100644 --- a/server/src/cores/storage.core.ts +++ b/server/src/cores/storage.core.ts @@ -1,11 +1,11 @@ import { randomUUID } from 'node:crypto'; import { dirname, join, resolve } from 'node:path'; +import { ImageFormat } from 'src/config'; import { APP_MEDIA_LOCATION } from 'src/constants'; import { SystemConfigCore } from 'src/cores/system-config.core'; import { AssetEntity } from 'src/entities/asset.entity'; import { AssetPathType, PathType, PersonPathType } from 'src/entities/move.entity'; import { PersonEntity } from 'src/entities/person.entity'; -import { ImageFormat } from 'src/entities/system-config.entity'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; diff --git a/server/src/cores/system-config.core.ts b/server/src/cores/system-config.core.ts index 050f2891c4..adf89363ea 100644 --- a/server/src/cores/system-config.core.ts +++ b/server/src/cores/system-config.core.ts @@ -1,172 +1,19 @@ import { BadRequestException, ForbiddenException, Injectable } from '@nestjs/common'; -import { CronExpression } from '@nestjs/schedule'; import AsyncLock from 'async-lock'; import { plainToInstance } from 'class-transformer'; import { validate } from 'class-validator'; import { load as loadYaml } from 'js-yaml'; import * as _ from 'lodash'; import { Subject } from 'rxjs'; +import { SystemConfig, defaults } from 'src/config'; import { SystemConfigDto } from 'src/dtos/system-config.dto'; -import { - AudioCodec, - CQMode, - Colorspace, - ImageFormat, - LogLevel, - SystemConfig, - SystemConfigEntity, - SystemConfigKey, - SystemConfigValue, - ToneMapping, - TranscodeHWAccel, - TranscodePolicy, - VideoCodec, -} from 'src/entities/system-config.entity'; +import { SystemConfigEntity, SystemConfigKey, SystemConfigValue } from 'src/entities/system-config.entity'; import { DatabaseLock } from 'src/interfaces/database.interface'; -import { QueueName } from 'src/interfaces/job.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; export type SystemConfigValidator = (config: SystemConfig, newConfig: SystemConfig) => void | Promise; -export const defaults = Object.freeze({ - ffmpeg: { - crf: 23, - threads: 0, - preset: 'ultrafast', - targetVideoCodec: VideoCodec.H264, - acceptedVideoCodecs: [VideoCodec.H264], - targetAudioCodec: AudioCodec.AAC, - acceptedAudioCodecs: [AudioCodec.AAC, AudioCodec.MP3, AudioCodec.LIBOPUS], - targetResolution: '720', - maxBitrate: '0', - bframes: -1, - refs: 0, - gopSize: 0, - npl: 0, - temporalAQ: false, - cqMode: CQMode.AUTO, - twoPass: false, - preferredHwDevice: 'auto', - transcode: TranscodePolicy.REQUIRED, - tonemap: ToneMapping.HABLE, - accel: TranscodeHWAccel.DISABLED, - }, - job: { - [QueueName.BACKGROUND_TASK]: { concurrency: 5 }, - [QueueName.SMART_SEARCH]: { concurrency: 2 }, - [QueueName.METADATA_EXTRACTION]: { concurrency: 5 }, - [QueueName.FACE_DETECTION]: { concurrency: 2 }, - [QueueName.SEARCH]: { concurrency: 5 }, - [QueueName.SIDECAR]: { concurrency: 5 }, - [QueueName.LIBRARY]: { concurrency: 5 }, - [QueueName.MIGRATION]: { concurrency: 5 }, - [QueueName.THUMBNAIL_GENERATION]: { concurrency: 5 }, - [QueueName.VIDEO_CONVERSION]: { concurrency: 1 }, - [QueueName.NOTIFICATION]: { concurrency: 5 }, - }, - logging: { - enabled: true, - level: LogLevel.LOG, - }, - machineLearning: { - enabled: process.env.IMMICH_MACHINE_LEARNING_ENABLED !== 'false', - url: process.env.IMMICH_MACHINE_LEARNING_URL || 'http://immich-machine-learning:3003', - clip: { - enabled: true, - modelName: 'ViT-B-32__openai', - }, - facialRecognition: { - enabled: true, - modelName: 'buffalo_l', - minScore: 0.7, - maxDistance: 0.5, - minFaces: 3, - }, - }, - map: { - enabled: true, - lightStyle: '', - darkStyle: '', - }, - reverseGeocoding: { - enabled: true, - }, - oauth: { - autoLaunch: false, - autoRegister: true, - buttonText: 'Login with OAuth', - clientId: '', - clientSecret: '', - defaultStorageQuota: 0, - enabled: false, - issuerUrl: '', - mobileOverrideEnabled: false, - mobileRedirectUri: '', - scope: 'openid email profile', - signingAlgorithm: 'RS256', - storageLabelClaim: 'preferred_username', - storageQuotaClaim: 'immich_quota', - }, - passwordLogin: { - enabled: true, - }, - storageTemplate: { - enabled: false, - hashVerificationEnabled: true, - template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}', - }, - image: { - thumbnailFormat: ImageFormat.WEBP, - thumbnailSize: 250, - previewFormat: ImageFormat.JPEG, - previewSize: 1440, - quality: 80, - colorspace: Colorspace.P3, - extractEmbedded: false, - }, - newVersionCheck: { - enabled: true, - }, - trash: { - enabled: true, - days: 30, - }, - theme: { - customCss: '', - }, - library: { - scan: { - enabled: true, - cronExpression: CronExpression.EVERY_DAY_AT_MIDNIGHT, - }, - watch: { - enabled: false, - }, - }, - server: { - externalDomain: '', - loginPageMessage: '', - }, - notifications: { - smtp: { - enabled: false, - from: '', - replyTo: '', - transport: { - ignoreCert: false, - host: '', - port: 587, - username: '', - password: '', - }, - }, - }, - user: { - deleteDelay: 7, - }, -}); - export enum FeatureFlag { SMART_SEARCH = 'smartSearch', FACIAL_RECOGNITION = 'facialRecognition', @@ -192,7 +39,7 @@ export class SystemConfigCore { private config: SystemConfig | null = null; private lastUpdated: number | null = null; - public config$ = new Subject(); + config$ = new Subject(); private constructor( private repository: ISystemConfigRepository, @@ -267,11 +114,7 @@ export class SystemConfigCore { }; } - public getDefaults(): SystemConfig { - return defaults; - } - - public async getConfig(force = false): Promise { + async getConfig(force = false): Promise { if (force || !this.config) { const lastUpdated = this.lastUpdated; await this.asyncLock.acquire(DatabaseLock[DatabaseLock.GetSystemConfig], async () => { @@ -285,7 +128,7 @@ export class SystemConfigCore { return this.config!; } - public async updateConfig(newConfig: SystemConfig): Promise { + async updateConfig(newConfig: SystemConfig): Promise { if (await this.hasFeature(FeatureFlag.CONFIG_FILE)) { throw new BadRequestException('Cannot update configuration while IMMICH_CONFIG_FILE is in use'); } @@ -328,9 +171,8 @@ export class SystemConfigCore { return config; } - public async refreshConfig() { + async refreshConfig() { const newConfig = await this.getConfig(true); - this.config$.next(newConfig); } diff --git a/server/src/dtos/system-config.dto.ts b/server/src/dtos/system-config.dto.ts index 1d02e3bf92..da68c27478 100644 --- a/server/src/dtos/system-config.dto.ts +++ b/server/src/dtos/system-config.dto.ts @@ -18,7 +18,6 @@ import { ValidatorConstraint, ValidatorConstraintInterface, } from 'class-validator'; -import { CLIPConfig, RecognitionConfig } from 'src/dtos/model-config.dto'; import { AudioCodec, CQMode, @@ -30,7 +29,8 @@ import { TranscodeHWAccel, TranscodePolicy, VideoCodec, -} from 'src/entities/system-config.entity'; +} from 'src/config'; +import { CLIPConfig, RecognitionConfig } from 'src/dtos/model-config.dto'; import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.interface'; import { ValidateBoolean, validateCronExpression } from 'src/validation'; diff --git a/server/src/entities/system-config.entity.ts b/server/src/entities/system-config.entity.ts index 3675a7846a..64342cc195 100644 --- a/server/src/entities/system-config.entity.ts +++ b/server/src/entities/system-config.entity.ts @@ -1,4 +1,4 @@ -import { ConcurrentQueueName } from 'src/interfaces/job.interface'; +import { SystemConfig } from 'src/config'; import { Column, Entity, PrimaryColumn } from 'typeorm'; export type SystemConfigValue = string | string[] | number | boolean; @@ -143,197 +143,3 @@ export class SystemConfigEntity { @Column({ type: 'varchar', nullable: true, transformer: { to: JSON.stringify, from: JSON.parse } }) value!: T; } - -export enum TranscodePolicy { - ALL = 'all', - OPTIMAL = 'optimal', - BITRATE = 'bitrate', - REQUIRED = 'required', - DISABLED = 'disabled', -} - -export enum TranscodeTarget { - NONE, - AUDIO, - VIDEO, - ALL, -} - -export enum VideoCodec { - H264 = 'h264', - HEVC = 'hevc', - VP9 = 'vp9', - AV1 = 'av1', -} - -export enum AudioCodec { - MP3 = 'mp3', - AAC = 'aac', - LIBOPUS = 'libopus', -} - -export enum TranscodeHWAccel { - NVENC = 'nvenc', - QSV = 'qsv', - VAAPI = 'vaapi', - RKMPP = 'rkmpp', - DISABLED = 'disabled', -} - -export enum ToneMapping { - HABLE = 'hable', - MOBIUS = 'mobius', - REINHARD = 'reinhard', - DISABLED = 'disabled', -} - -export enum CQMode { - AUTO = 'auto', - CQP = 'cqp', - ICQ = 'icq', -} - -export enum Colorspace { - SRGB = 'srgb', - P3 = 'p3', -} - -export enum ImageFormat { - JPEG = 'jpeg', - WEBP = 'webp', -} - -export enum LogLevel { - VERBOSE = 'verbose', - DEBUG = 'debug', - LOG = 'log', - WARN = 'warn', - ERROR = 'error', - FATAL = 'fatal', -} - -export interface SystemConfig { - ffmpeg: { - crf: number; - threads: number; - preset: string; - targetVideoCodec: VideoCodec; - acceptedVideoCodecs: VideoCodec[]; - targetAudioCodec: AudioCodec; - acceptedAudioCodecs: AudioCodec[]; - targetResolution: string; - maxBitrate: string; - bframes: number; - refs: number; - gopSize: number; - npl: number; - temporalAQ: boolean; - cqMode: CQMode; - twoPass: boolean; - preferredHwDevice: string; - transcode: TranscodePolicy; - accel: TranscodeHWAccel; - tonemap: ToneMapping; - }; - job: Record; - logging: { - enabled: boolean; - level: LogLevel; - }; - machineLearning: { - enabled: boolean; - url: string; - clip: { - enabled: boolean; - modelName: string; - }; - facialRecognition: { - enabled: boolean; - modelName: string; - minScore: number; - minFaces: number; - maxDistance: number; - }; - }; - map: { - enabled: boolean; - lightStyle: string; - darkStyle: string; - }; - reverseGeocoding: { - enabled: boolean; - }; - oauth: { - autoLaunch: boolean; - autoRegister: boolean; - buttonText: string; - clientId: string; - clientSecret: string; - defaultStorageQuota: number; - enabled: boolean; - issuerUrl: string; - mobileOverrideEnabled: boolean; - mobileRedirectUri: string; - scope: string; - signingAlgorithm: string; - storageLabelClaim: string; - storageQuotaClaim: string; - }; - passwordLogin: { - enabled: boolean; - }; - storageTemplate: { - enabled: boolean; - hashVerificationEnabled: boolean; - template: string; - }; - image: { - thumbnailFormat: ImageFormat; - thumbnailSize: number; - previewFormat: ImageFormat; - previewSize: number; - quality: number; - colorspace: Colorspace; - extractEmbedded: boolean; - }; - newVersionCheck: { - enabled: boolean; - }; - trash: { - enabled: boolean; - days: number; - }; - theme: { - customCss: string; - }; - library: { - scan: { - enabled: boolean; - cronExpression: string; - }; - watch: { - enabled: boolean; - }; - }; - notifications: { - smtp: { - enabled: boolean; - from: string; - replyTo: string; - transport: { - ignoreCert: boolean; - host: string; - port: number; - username: string; - password: string; - }; - }; - }; - server: { - externalDomain: string; - loginPageMessage: string; - }; - user: { - deleteDelay: number; - }; -} diff --git a/server/src/interfaces/event.interface.ts b/server/src/interfaces/event.interface.ts index 49b5177f3d..e3539a823d 100644 --- a/server/src/interfaces/event.interface.ts +++ b/server/src/interfaces/event.interface.ts @@ -1,6 +1,6 @@ +import { SystemConfig } from 'src/config'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server-info.dto'; -import { SystemConfig } from 'src/entities/system-config.entity'; export const IEventRepository = 'IEventRepository'; diff --git a/server/src/interfaces/logger.interface.ts b/server/src/interfaces/logger.interface.ts index d6959063a8..f0afdce2a5 100644 --- a/server/src/interfaces/logger.interface.ts +++ b/server/src/interfaces/logger.interface.ts @@ -1,4 +1,4 @@ -import { LogLevel } from 'src/entities/system-config.entity'; +import { LogLevel } from 'src/config'; export const ILoggerRepository = 'ILoggerRepository'; diff --git a/server/src/interfaces/media.interface.ts b/server/src/interfaces/media.interface.ts index 092536b026..80beb8c436 100644 --- a/server/src/interfaces/media.interface.ts +++ b/server/src/interfaces/media.interface.ts @@ -1,5 +1,5 @@ import { Writable } from 'node:stream'; -import { ImageFormat, TranscodeTarget, VideoCodec } from 'src/entities/system-config.entity'; +import { ImageFormat, TranscodeTarget, VideoCodec } from 'src/config'; export const IMediaRepository = 'IMediaRepository'; diff --git a/server/src/main.ts b/server/src/main.ts index f6f108b5e9..0d30fe7832 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -7,8 +7,8 @@ import { existsSync } from 'node:fs'; import { Worker } from 'node:worker_threads'; import sirv from 'sirv'; import { ApiModule, ImmichAdminModule } from 'src/app.module'; +import { LogLevel } from 'src/config'; import { WEB_ROOT, envName, excludePaths, isDev, serverVersion } from 'src/constants'; -import { LogLevel } from 'src/entities/system-config.entity'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { WebSocketAdapter } from 'src/middleware/websocket.adapter'; import { ApiService } from 'src/services/api.service'; diff --git a/server/src/repositories/logger.repository.ts b/server/src/repositories/logger.repository.ts index 42b7adb22e..0f82c6ee0f 100644 --- a/server/src/repositories/logger.repository.ts +++ b/server/src/repositories/logger.repository.ts @@ -1,7 +1,7 @@ import { ConsoleLogger, Injectable, Scope } from '@nestjs/common'; import { isLogLevelEnabled } from '@nestjs/common/services/utils/is-log-level-enabled.util'; import { ClsService } from 'nestjs-cls'; -import { LogLevel } from 'src/entities/system-config.entity'; +import { LogLevel } from 'src/config'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { LogColor } from 'src/utils/logger-colors'; diff --git a/server/src/repositories/media.repository.ts b/server/src/repositories/media.repository.ts index 71c58fd91e..04a9751223 100644 --- a/server/src/repositories/media.repository.ts +++ b/server/src/repositories/media.repository.ts @@ -5,7 +5,7 @@ import fs from 'node:fs/promises'; import { Writable } from 'node:stream'; import { promisify } from 'node:util'; import sharp from 'sharp'; -import { Colorspace } from 'src/entities/system-config.entity'; +import { Colorspace } from 'src/config'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMediaRepository, diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index b2c524decd..7e7bbb6675 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -10,6 +10,7 @@ import cookieParser from 'cookie'; import { DateTime } from 'luxon'; import { IncomingHttpHeaders } from 'node:http'; import { ClientMetadata, Issuer, UserinfoResponse, custom, generators } from 'openid-client'; +import { SystemConfig } from 'src/config'; import { AuthType, LOGIN_URL, MOBILE_REDIRECT } from 'src/constants'; import { AccessCore } from 'src/cores/access.core'; import { SystemConfigCore } from 'src/cores/system-config.core'; @@ -28,7 +29,6 @@ import { mapLoginResponse, } from 'src/dtos/auth.dto'; import { UserResponseDto, mapUser } from 'src/dtos/user.dto'; -import { SystemConfig } from 'src/entities/system-config.entity'; import { UserEntity } from 'src/entities/user.entity'; import { IAccessRepository } from 'src/interfaces/access.interface'; import { IKeyRepository } from 'src/interfaces/api-key.interface'; diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts index 92fc60141a..4801a6814c 100644 --- a/server/src/services/job.service.spec.ts +++ b/server/src/services/job.service.spec.ts @@ -1,6 +1,7 @@ import { BadRequestException } from '@nestjs/common'; +import { SystemConfig } from 'src/config'; import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core'; -import { SystemConfig, SystemConfigKey, SystemConfigKeyPaths } from 'src/entities/system-config.entity'; +import { SystemConfigKey, SystemConfigKeyPaths } from 'src/entities/system-config.entity'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index 59f2c5a1d3..f987fd1b57 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -1,10 +1,11 @@ import { BadRequestException } from '@nestjs/common'; import { Stats } from 'node:fs'; +import { SystemConfig } from 'src/config'; import { SystemConfigCore } from 'src/cores/system-config.core'; import { mapLibrary } from 'src/dtos/library.dto'; import { AssetType } from 'src/entities/asset.entity'; import { LibraryType } from 'src/entities/library.entity'; -import { SystemConfig, SystemConfigKey } from 'src/entities/system-config.entity'; +import { SystemConfigKey } from 'src/entities/system-config.entity'; import { UserEntity } from 'src/entities/user.entity'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index 2cf103748d..4a7a6836af 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -1,16 +1,16 @@ import { Stats } from 'node:fs'; -import { AssetType } from 'src/entities/asset.entity'; -import { ExifEntity } from 'src/entities/exif.entity'; import { AudioCodec, Colorspace, ImageFormat, - SystemConfigKey, ToneMapping, TranscodeHWAccel, TranscodePolicy, VideoCodec, -} from 'src/entities/system-config.entity'; +} from 'src/config'; +import { AssetType } from 'src/entities/asset.entity'; +import { ExifEntity } from 'src/entities/exif.entity'; +import { SystemConfigKey } from 'src/entities/system-config.entity'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; diff --git a/server/src/services/media.service.ts b/server/src/services/media.service.ts index 8a4ca4ce3e..e493b22f95 100644 --- a/server/src/services/media.service.ts +++ b/server/src/services/media.service.ts @@ -1,10 +1,5 @@ import { Inject, Injectable, UnsupportedMediaTypeException } from '@nestjs/common'; import { dirname } from 'node:path'; -import { GeneratedImageType, StorageCore, StorageFolder } from 'src/cores/storage.core'; -import { SystemConfigCore } from 'src/cores/system-config.core'; -import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto'; -import { AssetEntity, AssetType } from 'src/entities/asset.entity'; -import { AssetPathType } from 'src/entities/move.entity'; import { AudioCodec, Colorspace, @@ -13,7 +8,12 @@ import { TranscodePolicy, TranscodeTarget, VideoCodec, -} from 'src/entities/system-config.entity'; +} from 'src/config'; +import { GeneratedImageType, StorageCore, StorageFolder } from 'src/cores/storage.core'; +import { SystemConfigCore } from 'src/cores/system-config.core'; +import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto'; +import { AssetEntity, AssetType } from 'src/entities/asset.entity'; +import { AssetPathType } from 'src/entities/move.entity'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { diff --git a/server/src/services/person.service.spec.ts b/server/src/services/person.service.spec.ts index 3a1d76388e..934a204246 100644 --- a/server/src/services/person.service.spec.ts +++ b/server/src/services/person.service.spec.ts @@ -1,8 +1,9 @@ import { BadRequestException, NotFoundException } from '@nestjs/common'; +import { Colorspace } from 'src/config'; import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { PersonResponseDto, mapFaces, mapPerson } from 'src/dtos/person.dto'; import { AssetFaceEntity } from 'src/entities/asset-face.entity'; -import { Colorspace, SystemConfigKey } from 'src/entities/system-config.entity'; +import { SystemConfigKey } from 'src/entities/system-config.entity'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; diff --git a/server/src/services/person.service.ts b/server/src/services/person.service.ts index c8a16f71c7..0c0ece9fde 100644 --- a/server/src/services/person.service.ts +++ b/server/src/services/person.service.ts @@ -1,4 +1,5 @@ import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common'; +import { ImageFormat } from 'src/config'; import { FACE_THUMBNAIL_SIZE } from 'src/constants'; import { AccessCore, Permission } from 'src/cores/access.core'; import { StorageCore } from 'src/cores/storage.core'; @@ -23,7 +24,6 @@ import { } from 'src/dtos/person.dto'; import { PersonPathType } from 'src/entities/move.entity'; import { PersonEntity } from 'src/entities/person.entity'; -import { ImageFormat } from 'src/entities/system-config.entity'; import { IAccessRepository } from 'src/interfaces/access.interface'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; diff --git a/server/src/services/server-info.service.ts b/server/src/services/server-info.service.ts index 52bf8bd1d3..37fdbe0fb8 100644 --- a/server/src/services/server-info.service.ts +++ b/server/src/services/server-info.service.ts @@ -82,7 +82,7 @@ export class ServerInfoService { return serverVersion; } - getFeatures(): Promise { + async getFeatures(): Promise { return this.configCore.getFeatures(); } diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index 0ee0204357..26263cafbd 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -1,8 +1,9 @@ import { Stats } from 'node:fs'; -import { SystemConfigCore, defaults } from 'src/cores/system-config.core'; +import { SystemConfig, defaults } from 'src/config'; +import { SystemConfigCore } from 'src/cores/system-config.core'; import { AssetEntity } from 'src/entities/asset.entity'; import { AssetPathType } from 'src/entities/move.entity'; -import { SystemConfig, SystemConfigKey } from 'src/entities/system-config.entity'; +import { SystemConfigKey } from 'src/entities/system-config.entity'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; diff --git a/server/src/services/storage-template.service.ts b/server/src/services/storage-template.service.ts index 1a3c61a4b8..f7d25054af 100644 --- a/server/src/services/storage-template.service.ts +++ b/server/src/services/storage-template.service.ts @@ -3,6 +3,7 @@ import handlebar from 'handlebars'; import { DateTime } from 'luxon'; import path from 'node:path'; import sanitize from 'sanitize-filename'; +import { SystemConfig } from 'src/config'; import { supportedDayTokens, supportedHourTokens, @@ -17,7 +18,6 @@ import { SystemConfigCore } from 'src/cores/system-config.core'; import { OnServerEvent } from 'src/decorators'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { AssetPathType } from 'src/entities/move.entity'; -import { SystemConfig } from 'src/entities/system-config.entity'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index 23ec2a2432..d345d55df6 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -1,5 +1,4 @@ import { BadRequestException } from '@nestjs/common'; -import { defaults } from 'src/cores/system-config.core'; import { AudioCodec, CQMode, @@ -7,13 +6,13 @@ import { ImageFormat, LogLevel, SystemConfig, - SystemConfigEntity, - SystemConfigKey, ToneMapping, TranscodeHWAccel, TranscodePolicy, VideoCodec, -} from 'src/entities/system-config.entity'; + defaults, +} from 'src/config'; +import { SystemConfigEntity, SystemConfigKey } from 'src/entities/system-config.entity'; import { IEventRepository, ServerEvent } from 'src/interfaces/event.interface'; import { QueueName } from 'src/interfaces/job.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; diff --git a/server/src/services/system-config.service.ts b/server/src/services/system-config.service.ts index 2203f7a687..eb83e8733d 100644 --- a/server/src/services/system-config.service.ts +++ b/server/src/services/system-config.service.ts @@ -1,6 +1,7 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import { instanceToPlain } from 'class-transformer'; import _ from 'lodash'; +import { LogLevel, SystemConfig, defaults } from 'src/config'; import { supportedDayTokens, supportedHourTokens, @@ -14,7 +15,6 @@ import { import { SystemConfigCore } from 'src/cores/system-config.core'; import { OnServerEvent } from 'src/decorators'; import { SystemConfigDto, SystemConfigTemplateStorageOptionDto, mapConfig } from 'src/dtos/system-config.dto'; -import { LogLevel, SystemConfig } from 'src/entities/system-config.entity'; import { ClientEvent, IEventRepository, @@ -56,8 +56,7 @@ export class SystemConfigService { } getDefaults(): SystemConfigDto { - const config = this.core.getDefaults(); - return mapConfig(config); + return mapConfig(defaults); } @OnServerEvent(ServerAsyncEvent.CONFIG_VALIDATE) diff --git a/server/src/utils/media.ts b/server/src/utils/media.ts index ff38ded631..f43b78464e 100644 --- a/server/src/utils/media.ts +++ b/server/src/utils/media.ts @@ -1,5 +1,5 @@ +import { CQMode, ToneMapping, TranscodeHWAccel, TranscodeTarget, VideoCodec } from 'src/config'; import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto'; -import { CQMode, ToneMapping, TranscodeHWAccel, TranscodeTarget, VideoCodec } from 'src/entities/system-config.entity'; import { AudioStreamInfo, BitrateDistribution,