diff --git a/server/src/cores/storage.core.ts b/server/src/cores/storage.core.ts index d33d81410c..8e42cd1076 100644 --- a/server/src/cores/storage.core.ts +++ b/server/src/cores/storage.core.ts @@ -5,6 +5,7 @@ import { AssetEntity } from 'src/entities/asset.entity'; import { PersonEntity } from 'src/entities/person.entity'; import { AssetFileType, AssetPathType, ImageFormat, PathType, PersonPathType, StorageFolder } from 'src/enum'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; @@ -36,6 +37,7 @@ let instance: StorageCore | null; export class StorageCore { private constructor( private assetRepository: IAssetRepository, + private configRepository: IConfigRepository, private cryptoRepository: ICryptoRepository, private moveRepository: IMoveRepository, private personRepository: IPersonRepository, @@ -46,6 +48,7 @@ export class StorageCore { static create( assetRepository: IAssetRepository, + configRepository: IConfigRepository, cryptoRepository: ICryptoRepository, moveRepository: IMoveRepository, personRepository: IPersonRepository, @@ -56,6 +59,7 @@ export class StorageCore { if (!instance) { instance = new StorageCore( assetRepository, + configRepository, cryptoRepository, moveRepository, personRepository, @@ -245,7 +249,11 @@ export class StorageCore { this.logger.warn(`Unable to complete move. File size mismatch: ${newPathSize} !== ${oldPathSize}`); return false; } - const repos = { metadataRepo: this.systemMetadataRepository, logger: this.logger }; + const repos = { + configRepo: this.configRepository, + metadataRepo: this.systemMetadataRepository, + logger: this.logger, + }; const config = await getConfig(repos, { withCache: true }); if (assetInfo && config.storageTemplate.hashVerificationEnabled) { const { checksum } = assetInfo; diff --git a/server/src/interfaces/config.interface.ts b/server/src/interfaces/config.interface.ts index df999db18b..b81c4fa48e 100644 --- a/server/src/interfaces/config.interface.ts +++ b/server/src/interfaces/config.interface.ts @@ -3,6 +3,7 @@ import { VectorExtension } from 'src/interfaces/database.interface'; export const IConfigRepository = 'IConfigRepository'; export interface EnvData { + configFile?: string; database: { skipMigrations: boolean; vectorExtension: VectorExtension; diff --git a/server/src/repositories/config.repository.ts b/server/src/repositories/config.repository.ts index 04d790b812..fe952c4f77 100644 --- a/server/src/repositories/config.repository.ts +++ b/server/src/repositories/config.repository.ts @@ -6,6 +6,7 @@ import { EnvData, IConfigRepository } from 'src/interfaces/config.interface'; export class ConfigRepository implements IConfigRepository { getEnv(): EnvData { return { + configFile: process.env.IMMICH_CONFIG_FILE, database: { skipMigrations: process.env.DB_SKIP_MIGRATIONS === 'true', vectorExtension: getVectorExtension(), diff --git a/server/src/services/asset.service.spec.ts b/server/src/services/asset.service.spec.ts index f36d26fa7c..968b774b77 100755 --- a/server/src/services/asset.service.spec.ts +++ b/server/src/services/asset.service.spec.ts @@ -4,6 +4,7 @@ import { AssetJobName, AssetStatsResponseDto } from 'src/dtos/asset.dto'; import { AssetEntity } from 'src/entities/asset.entity'; import { AssetStatus, AssetType } from 'src/enum'; import { AssetStats, IAssetRepository } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, JobName } from 'src/interfaces/job.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; @@ -19,6 +20,7 @@ import { partnerStub } from 'test/fixtures/partner.stub'; import { userStub } from 'test/fixtures/user.stub'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; @@ -45,6 +47,7 @@ describe(AssetService.name, () => { let sut: AssetService; let accessMock: IAccessRepositoryMock; let assetMock: Mocked; + let configMock: Mocked; let jobMock: Mocked; let userMock: Mocked; let eventMock: Mocked; @@ -66,6 +69,7 @@ describe(AssetService.name, () => { beforeEach(() => { accessMock = newAccessRepositoryMock(); assetMock = newAssetRepositoryMock(); + configMock = newConfigRepositoryMock(); eventMock = newEventRepositoryMock(); jobMock = newJobRepositoryMock(); userMock = newUserRepositoryMock(); @@ -77,6 +81,7 @@ describe(AssetService.name, () => { sut = new AssetService( accessMock, assetMock, + configMock, jobMock, systemMock, userMock, diff --git a/server/src/services/asset.service.ts b/server/src/services/asset.service.ts index 171005ab74..1141688028 100644 --- a/server/src/services/asset.service.ts +++ b/server/src/services/asset.service.ts @@ -22,6 +22,7 @@ import { AssetEntity } from 'src/entities/asset.entity'; import { AssetStatus, Permission } from 'src/enum'; import { IAccessRepository } from 'src/interfaces/access.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { IAssetDeleteJob, @@ -46,6 +47,7 @@ export class AssetService extends BaseService { constructor( @Inject(IAccessRepository) private access: IAccessRepository, @Inject(IAssetRepository) private assetRepository: IAssetRepository, + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, @Inject(IUserRepository) private userRepository: IUserRepository, @@ -54,7 +56,7 @@ export class AssetService extends BaseService { @Inject(IStackRepository) private stackRepository: IStackRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(AssetService.name); } diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index d22a3b3634..7cb79b80a1 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -5,6 +5,7 @@ import { UserMetadataEntity } from 'src/entities/user-metadata.entity'; import { UserEntity } from 'src/entities/user.entity'; import { AuthType } from 'src/enum'; import { IKeyRepository } from 'src/interfaces/api-key.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; @@ -20,6 +21,7 @@ import { sharedLinkStub } from 'test/fixtures/shared-link.stub'; import { systemConfigStub } from 'test/fixtures/system-config.stub'; import { userStub } from 'test/fixtures/user.stub'; import { newKeyRepositoryMock } from 'test/repositories/api-key.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; @@ -57,6 +59,7 @@ const oauthUserWithDefaultQuota = { describe('AuthService', () => { let sut: AuthService; + let configMock: Mocked; let cryptoMock: Mocked; let eventMock: Mocked; let userMock: Mocked; @@ -89,6 +92,7 @@ describe('AuthService', () => { }), } as any); + configMock = newConfigRepositoryMock(); cryptoMock = newCryptoRepositoryMock(); eventMock = newEventRepositoryMock(); userMock = newUserRepositoryMock(); @@ -98,7 +102,17 @@ describe('AuthService', () => { shareMock = newSharedLinkRepositoryMock(); keyMock = newKeyRepositoryMock(); - sut = new AuthService(cryptoMock, eventMock, systemMock, loggerMock, userMock, sessionMock, shareMock, keyMock); + sut = new AuthService( + configMock, + cryptoMock, + eventMock, + systemMock, + loggerMock, + userMock, + sessionMock, + shareMock, + keyMock, + ); }); it('should be defined', () => { diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index 72d251ce78..3e4a55b7ff 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -31,6 +31,7 @@ import { UserAdminResponseDto, mapUserAdmin } from 'src/dtos/user.dto'; import { UserEntity } from 'src/entities/user.entity'; import { AuthType, Permission } from 'src/enum'; import { IKeyRepository } from 'src/interfaces/api-key.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; @@ -72,6 +73,7 @@ export type ValidateRequest = { @Injectable() export class AuthService extends BaseService { constructor( + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, @Inject(IEventRepository) private eventRepository: IEventRepository, @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, @@ -81,7 +83,7 @@ export class AuthService extends BaseService { @Inject(ISharedLinkRepository) private sharedLinkRepository: ISharedLinkRepository, @Inject(IKeyRepository) private keyRepository: IKeyRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(AuthService.name); custom.setHttpOptionsDefaults({ timeout: 30_000 }); diff --git a/server/src/services/base.service.ts b/server/src/services/base.service.ts index 776858aa1a..a2ddcb1e50 100644 --- a/server/src/services/base.service.ts +++ b/server/src/services/base.service.ts @@ -1,32 +1,30 @@ import { Inject } from '@nestjs/common'; import { SystemConfig } from 'src/config'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { getConfig, updateConfig } from 'src/utils/config'; export class BaseService { constructor( + @Inject(IConfigRepository) protected configRepository: IConfigRepository, @Inject(ISystemMetadataRepository) protected systemMetadataRepository: ISystemMetadataRepository, @Inject(ILoggerRepository) protected logger: ILoggerRepository, ) {} + private get repos() { + return { + configRepo: this.configRepository, + metadataRepo: this.systemMetadataRepository, + logger: this.logger, + }; + } + getConfig(options: { withCache: boolean }) { - return getConfig( - { - metadataRepo: this.systemMetadataRepository, - logger: this.logger, - }, - options, - ); + return getConfig(this.repos, options); } updateConfig(newConfig: SystemConfig) { - return updateConfig( - { - metadataRepo: this.systemMetadataRepository, - logger: this.logger, - }, - newConfig, - ); + return updateConfig(this.repos, newConfig); } } diff --git a/server/src/services/cli.service.spec.ts b/server/src/services/cli.service.spec.ts index e52c648664..f79c2d4934 100644 --- a/server/src/services/cli.service.spec.ts +++ b/server/src/services/cli.service.spec.ts @@ -1,9 +1,11 @@ +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { CliService } from 'src/services/cli.service'; import { userStub } from 'test/fixtures/user.stub'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metadata.repository.mock'; @@ -13,18 +15,20 @@ import { Mocked, describe, it } from 'vitest'; describe(CliService.name, () => { let sut: CliService; - let userMock: Mocked; + let configMock: Mocked; let cryptoMock: Mocked; + let userMock: Mocked; let systemMock: Mocked; let loggerMock: Mocked; beforeEach(() => { + configMock = newConfigRepositoryMock(); cryptoMock = newCryptoRepositoryMock(); systemMock = newSystemMetadataRepositoryMock(); userMock = newUserRepositoryMock(); loggerMock = newLoggerRepositoryMock(); - sut = new CliService(cryptoMock, systemMock, userMock, loggerMock); + sut = new CliService(configMock, cryptoMock, systemMock, userMock, loggerMock); }); describe('resetAdminPassword', () => { diff --git a/server/src/services/cli.service.ts b/server/src/services/cli.service.ts index c7ed510f5d..5abd1fab29 100644 --- a/server/src/services/cli.service.ts +++ b/server/src/services/cli.service.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { SALT_ROUNDS } from 'src/constants'; import { UserAdminResponseDto, mapUserAdmin } from 'src/dtos/user.dto'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; @@ -10,12 +11,13 @@ import { BaseService } from 'src/services/base.service'; @Injectable() export class CliService extends BaseService { constructor( + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, @Inject(IUserRepository) private userRepository: IUserRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(CliService.name); } diff --git a/server/src/services/duplicate.service.spec.ts b/server/src/services/duplicate.service.spec.ts index eada2fffcf..ff03f02389 100644 --- a/server/src/services/duplicate.service.spec.ts +++ b/server/src/services/duplicate.service.spec.ts @@ -1,4 +1,5 @@ import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; @@ -8,6 +9,7 @@ import { DuplicateService } from 'src/services/duplicate.service'; import { SearchService } from 'src/services/search.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; @@ -20,6 +22,7 @@ vitest.useFakeTimers(); describe(SearchService.name, () => { let sut: DuplicateService; let assetMock: Mocked; + let configMock: Mocked; let systemMock: Mocked; let searchMock: Mocked; let loggerMock: Mocked; @@ -28,13 +31,14 @@ describe(SearchService.name, () => { beforeEach(() => { assetMock = newAssetRepositoryMock(); + configMock = newConfigRepositoryMock(); systemMock = newSystemMetadataRepositoryMock(); searchMock = newSearchRepositoryMock(); loggerMock = newLoggerRepositoryMock(); cryptoMock = newCryptoRepositoryMock(); jobMock = newJobRepositoryMock(); - sut = new DuplicateService(systemMock, searchMock, assetMock, loggerMock, cryptoMock, jobMock); + sut = new DuplicateService(configMock, systemMock, searchMock, assetMock, loggerMock, cryptoMock, jobMock); }); it('should work', () => { diff --git a/server/src/services/duplicate.service.ts b/server/src/services/duplicate.service.ts index 00f738a613..f5baa611ff 100644 --- a/server/src/services/duplicate.service.ts +++ b/server/src/services/duplicate.service.ts @@ -4,6 +4,7 @@ import { AuthDto } from 'src/dtos/auth.dto'; import { DuplicateResponseDto, mapDuplicateResponse } from 'src/dtos/duplicate.dto'; import { AssetEntity } from 'src/entities/asset.entity'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IBaseJob, @@ -24,6 +25,7 @@ import { usePagination } from 'src/utils/pagination'; @Injectable() export class DuplicateService extends BaseService { constructor( + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, @Inject(ISearchRepository) private searchRepository: ISearchRepository, @Inject(IAssetRepository) private assetRepository: IAssetRepository, @@ -31,7 +33,7 @@ export class DuplicateService extends BaseService { @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(DuplicateService.name); } diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts index 8d7c15073d..8b23e133d9 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 { defaults } from 'src/config'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, @@ -18,6 +19,7 @@ import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interf import { JobService } from 'src/services/job.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; @@ -37,6 +39,7 @@ const makeMockHandlers = (status: JobStatus) => { describe(JobService.name, () => { let sut: JobService; let assetMock: Mocked; + let configMock: Mocked; let eventMock: Mocked; let jobMock: Mocked; let personMock: Mocked; @@ -46,13 +49,14 @@ describe(JobService.name, () => { beforeEach(() => { assetMock = newAssetRepositoryMock(); + configMock = newConfigRepositoryMock(); systemMock = newSystemMetadataRepositoryMock(); eventMock = newEventRepositoryMock(); jobMock = newJobRepositoryMock(); personMock = newPersonRepositoryMock(); metricMock = newMetricRepositoryMock(); loggerMock = newLoggerRepositoryMock(); - sut = new JobService(assetMock, eventMock, jobMock, systemMock, personMock, metricMock, loggerMock); + sut = new JobService(assetMock, configMock, eventMock, jobMock, systemMock, personMock, metricMock, loggerMock); }); it('should work', () => { diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index 8bcf5e5622..7ff7644796 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -5,6 +5,7 @@ import { mapAsset } from 'src/dtos/asset-response.dto'; import { AllJobStatusResponseDto, JobCommandDto, JobCreateDto, JobStatusDto } from 'src/dtos/job.dto'; import { AssetType, ManualJobName } from 'src/enum'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ArgOf, IEventRepository } from 'src/interfaces/event.interface'; import { ConcurrentQueueName, @@ -49,6 +50,7 @@ export class JobService extends BaseService { constructor( @Inject(IAssetRepository) private assetRepository: IAssetRepository, + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(IEventRepository) private eventRepository: IEventRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, @@ -56,7 +58,7 @@ export class JobService extends BaseService { @Inject(IMetricRepository) private metricRepository: IMetricRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(JobService.name); } diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index bcf0f1d0b5..3a6c8446e2 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -5,6 +5,7 @@ import { mapLibrary } from 'src/dtos/library.dto'; import { UserEntity } from 'src/entities/user.entity'; import { AssetType } from 'src/enum'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { @@ -26,6 +27,7 @@ import { libraryStub } from 'test/fixtures/library.stub'; import { systemConfigStub } from 'test/fixtures/system-config.stub'; import { userStub } from 'test/fixtures/user.stub'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; @@ -43,15 +45,17 @@ describe(LibraryService.name, () => { let sut: LibraryService; let assetMock: Mocked; - let systemMock: Mocked; + let configMock: Mocked; let cryptoMock: Mocked; + let databaseMock: Mocked; let jobMock: Mocked; let libraryMock: Mocked; let storageMock: Mocked; - let databaseMock: Mocked; + let systemMock: Mocked; let loggerMock: Mocked; beforeEach(() => { + configMock = newConfigRepositoryMock(); systemMock = newSystemMetadataRepositoryMock(); libraryMock = newLibraryRepositoryMock(); assetMock = newAssetRepositoryMock(); @@ -63,12 +67,13 @@ describe(LibraryService.name, () => { sut = new LibraryService( assetMock, - systemMock, + configMock, cryptoMock, + databaseMock, jobMock, libraryMock, storageMock, - databaseMock, + systemMock, loggerMock, ); diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index 07313e4ec8..abffad8166 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -18,6 +18,7 @@ import { AssetEntity } from 'src/entities/asset.entity'; import { LibraryEntity } from 'src/entities/library.entity'; import { AssetType } from 'src/enum'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { DatabaseLock, IDatabaseRepository } from 'src/interfaces/database.interface'; import { ArgOf } from 'src/interfaces/event.interface'; @@ -48,15 +49,16 @@ export class LibraryService extends BaseService { constructor( @Inject(IAssetRepository) private assetRepository: IAssetRepository, - @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, + @Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, @Inject(ILibraryRepository) private repository: ILibraryRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, - @Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository, + @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(LibraryService.name); } diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index 88e9f478bd..fd0de06926 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -12,6 +12,7 @@ import { VideoCodec, } from 'src/enum'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; @@ -26,6 +27,7 @@ import { faceStub } from 'test/fixtures/face.stub'; import { probeStub } from 'test/fixtures/media.stub'; import { personStub } from 'test/fixtures/person.stub'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; @@ -39,6 +41,7 @@ import { Mocked } from 'vitest'; describe(MediaService.name, () => { let sut: MediaService; let assetMock: Mocked; + let configMock: Mocked; let jobMock: Mocked; let mediaMock: Mocked; let moveMock: Mocked; @@ -50,6 +53,7 @@ describe(MediaService.name, () => { beforeEach(() => { assetMock = newAssetRepositoryMock(); + configMock = newConfigRepositoryMock(); systemMock = newSystemMetadataRepositoryMock(); jobMock = newJobRepositoryMock(); mediaMock = newMediaRepositoryMock(); @@ -61,6 +65,7 @@ describe(MediaService.name, () => { sut = new MediaService( assetMock, + configMock, personMock, jobMock, mediaMock, diff --git a/server/src/services/media.service.ts b/server/src/services/media.service.ts index 1f72c373f4..adb8c54f4a 100644 --- a/server/src/services/media.service.ts +++ b/server/src/services/media.service.ts @@ -18,6 +18,7 @@ import { VideoContainer, } from 'src/enum'; import { IAssetRepository, UpsertFileOptions, WithoutProperty } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IBaseJob, @@ -55,6 +56,7 @@ export class MediaService extends BaseService { constructor( @Inject(IAssetRepository) private assetRepository: IAssetRepository, + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(IPersonRepository) private personRepository: IPersonRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, @Inject(IMediaRepository) private mediaRepository: IMediaRepository, @@ -64,10 +66,11 @@ export class MediaService extends BaseService { @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(MediaService.name); this.storageCore = StorageCore.create( assetRepository, + configRepository, cryptoRepository, moveRepository, personRepository, diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index c74883c283..88b2498e91 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -6,6 +6,7 @@ import { ExifEntity } from 'src/entities/exif.entity'; import { AssetType, SourceType } from 'src/enum'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; @@ -29,6 +30,7 @@ import { personStub } from 'test/fixtures/person.stub'; import { tagStub } from 'test/fixtures/tag.stub'; import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; @@ -46,10 +48,14 @@ import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; import { Mocked } from 'vitest'; describe(MetadataService.name, () => { + let sut: MetadataService; + let albumMock: Mocked; let assetMock: Mocked; - let systemMock: Mocked; + let configMock: Mocked; let cryptoRepository: Mocked; + let databaseMock: Mocked; + let eventMock: Mocked; let jobMock: Mocked; let mapMock: Mocked; let metadataMock: Mocked; @@ -57,16 +63,15 @@ describe(MetadataService.name, () => { let mediaMock: Mocked; let personMock: Mocked; let storageMock: Mocked; - let eventMock: Mocked; - let databaseMock: Mocked; + let systemMock: Mocked; + let tagMock: Mocked; let userMock: Mocked; let loggerMock: Mocked; - let tagMock: Mocked; - let sut: MetadataService; beforeEach(() => { albumMock = newAlbumRepositoryMock(); assetMock = newAssetRepositoryMock(); + configMock = newConfigRepositoryMock(); cryptoRepository = newCryptoRepositoryMock(); jobMock = newJobRepositoryMock(); mapMock = newMapRepositoryMock(); @@ -85,9 +90,10 @@ describe(MetadataService.name, () => { sut = new MetadataService( albumMock, assetMock, - eventMock, + configMock, cryptoRepository, databaseMock, + eventMock, jobMock, mapMock, mediaMock, diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index e39e22b92f..3995c72f77 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -15,6 +15,7 @@ import { PersonEntity } from 'src/entities/person.entity'; import { AssetType, SourceType } from 'src/enum'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { DatabaseLock, IDatabaseRepository } from 'src/interfaces/database.interface'; import { ArgOf, IEventRepository } from 'src/interfaces/event.interface'; @@ -103,9 +104,10 @@ export class MetadataService extends BaseService { constructor( @Inject(IAlbumRepository) private albumRepository: IAlbumRepository, @Inject(IAssetRepository) private assetRepository: IAssetRepository, - @Inject(IEventRepository) private eventRepository: IEventRepository, + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, @Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository, + @Inject(IEventRepository) private eventRepository: IEventRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, @Inject(IMapRepository) private mapRepository: IMapRepository, @Inject(IMediaRepository) private mediaRepository: IMediaRepository, @@ -118,10 +120,11 @@ export class MetadataService extends BaseService { @Inject(IUserRepository) private userRepository: IUserRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(MetadataService.name); this.storageCore = StorageCore.create( assetRepository, + configRepository, cryptoRepository, moveRepository, personRepository, diff --git a/server/src/services/notification.service.spec.ts b/server/src/services/notification.service.spec.ts index 5fba38d1eb..6b3c9e6895 100644 --- a/server/src/services/notification.service.spec.ts +++ b/server/src/services/notification.service.spec.ts @@ -6,6 +6,7 @@ import { AssetFileEntity } from 'src/entities/asset-files.entity'; import { AssetFileType, UserMetadataKey } from 'src/enum'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; @@ -18,6 +19,7 @@ import { assetStub } from 'test/fixtures/asset.stub'; import { userStub } from 'test/fixtures/user.stub'; import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; @@ -66,6 +68,7 @@ const configs = { describe(NotificationService.name, () => { let albumMock: Mocked; let assetMock: Mocked; + let configMock: Mocked; let eventMock: Mocked; let jobMock: Mocked; let loggerMock: Mocked; @@ -77,6 +80,7 @@ describe(NotificationService.name, () => { beforeEach(() => { albumMock = newAlbumRepositoryMock(); assetMock = newAssetRepositoryMock(); + configMock = newConfigRepositoryMock(); eventMock = newEventRepositoryMock(); jobMock = newJobRepositoryMock(); loggerMock = newLoggerRepositoryMock(); @@ -85,6 +89,7 @@ describe(NotificationService.name, () => { userMock = newUserRepositoryMock(); sut = new NotificationService( + configMock, eventMock, systemMock, notificationMock, diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index cf6b89384d..dce13e5f6c 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -5,6 +5,7 @@ import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; import { AlbumEntity } from 'src/entities/album.entity'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ArgOf, IEventRepository } from 'src/interfaces/event.interface'; import { IEmailJob, @@ -28,6 +29,7 @@ import { getPreferences } from 'src/utils/preferences'; @Injectable() export class NotificationService extends BaseService { constructor( + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(IEventRepository) private eventRepository: IEventRepository, @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, @Inject(INotificationRepository) private notificationRepository: INotificationRepository, @@ -37,7 +39,7 @@ export class NotificationService extends BaseService { @Inject(IAssetRepository) private assetRepository: IAssetRepository, @Inject(IAlbumRepository) private albumRepository: IAlbumRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(NotificationService.name); } diff --git a/server/src/services/person.service.spec.ts b/server/src/services/person.service.spec.ts index 5214808de0..eb5362d62b 100644 --- a/server/src/services/person.service.spec.ts +++ b/server/src/services/person.service.spec.ts @@ -4,6 +4,7 @@ import { PersonResponseDto, mapFaces, mapPerson } from 'src/dtos/person.dto'; import { AssetFaceEntity } from 'src/entities/asset-face.entity'; import { CacheControl, Colorspace, ImageFormat, SourceType, SystemMetadataKey } from 'src/enum'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; @@ -23,6 +24,7 @@ import { personStub } from 'test/fixtures/person.stub'; import { systemConfigStub } from 'test/fixtures/system-config.stub'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; @@ -67,6 +69,7 @@ const detectFaceMock: DetectedFaces = { describe(PersonService.name, () => { let accessMock: IAccessRepositoryMock; let assetMock: Mocked; + let configMock: Mocked; let systemMock: Mocked; let jobMock: Mocked; let machineLearningMock: Mocked; @@ -82,6 +85,7 @@ describe(PersonService.name, () => { beforeEach(() => { accessMock = newAccessRepositoryMock(); assetMock = newAssetRepositoryMock(); + configMock = newConfigRepositoryMock(); systemMock = newSystemMetadataRepositoryMock(); jobMock = newJobRepositoryMock(); machineLearningMock = newMachineLearningRepositoryMock(); @@ -92,9 +96,11 @@ describe(PersonService.name, () => { searchMock = newSearchRepositoryMock(); cryptoMock = newCryptoRepositoryMock(); loggerMock = newLoggerRepositoryMock(); + sut = new PersonService( accessMock, assetMock, + configMock, machineLearningMock, moveMock, mediaMock, diff --git a/server/src/services/person.service.ts b/server/src/services/person.service.ts index 7cb6f42f15..3b71d3504e 100644 --- a/server/src/services/person.service.ts +++ b/server/src/services/person.service.ts @@ -33,6 +33,7 @@ import { } from 'src/enum'; import { IAccessRepository } from 'src/interfaces/access.interface'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IBaseJob, @@ -70,6 +71,7 @@ export class PersonService extends BaseService { constructor( @Inject(IAccessRepository) private access: IAccessRepository, @Inject(IAssetRepository) private assetRepository: IAssetRepository, + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(IMachineLearningRepository) private machineLearningRepository: IMachineLearningRepository, @Inject(IMoveRepository) moveRepository: IMoveRepository, @Inject(IMediaRepository) private mediaRepository: IMediaRepository, @@ -81,10 +83,11 @@ export class PersonService extends BaseService { @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(PersonService.name); this.storageCore = StorageCore.create( assetRepository, + configRepository, cryptoRepository, moveRepository, repository, diff --git a/server/src/services/search.service.spec.ts b/server/src/services/search.service.spec.ts index c30de68e05..eb30717a3a 100644 --- a/server/src/services/search.service.spec.ts +++ b/server/src/services/search.service.spec.ts @@ -1,6 +1,7 @@ import { mapAsset } from 'src/dtos/asset-response.dto'; import { SearchSuggestionType } from 'src/dtos/search.dto'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface'; @@ -12,6 +13,7 @@ import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { personStub } from 'test/fixtures/person.stub'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newMachineLearningRepositoryMock } from 'test/repositories/machine-learning.repository.mock'; import { newPartnerRepositoryMock } from 'test/repositories/partner.repository.mock'; @@ -25,6 +27,7 @@ vitest.useFakeTimers(); describe(SearchService.name, () => { let sut: SearchService; let assetMock: Mocked; + let configMock: Mocked; let systemMock: Mocked; let machineMock: Mocked; let personMock: Mocked; @@ -34,6 +37,7 @@ describe(SearchService.name, () => { beforeEach(() => { assetMock = newAssetRepositoryMock(); + configMock = newConfigRepositoryMock(); systemMock = newSystemMetadataRepositoryMock(); machineMock = newMachineLearningRepositoryMock(); personMock = newPersonRepositoryMock(); @@ -41,7 +45,16 @@ describe(SearchService.name, () => { partnerMock = newPartnerRepositoryMock(); loggerMock = newLoggerRepositoryMock(); - sut = new SearchService(systemMock, machineMock, personMock, searchMock, assetMock, partnerMock, loggerMock); + sut = new SearchService( + configMock, + systemMock, + machineMock, + personMock, + searchMock, + assetMock, + partnerMock, + loggerMock, + ); }); it('should work', () => { diff --git a/server/src/services/search.service.ts b/server/src/services/search.service.ts index 4227f35ec3..b878b4e898 100644 --- a/server/src/services/search.service.ts +++ b/server/src/services/search.service.ts @@ -17,6 +17,7 @@ import { import { AssetEntity } from 'src/entities/asset.entity'; import { AssetOrder } from 'src/enum'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface'; @@ -30,6 +31,7 @@ import { isSmartSearchEnabled } from 'src/utils/misc'; @Injectable() export class SearchService extends BaseService { constructor( + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, @Inject(IMachineLearningRepository) private machineLearning: IMachineLearningRepository, @Inject(IPersonRepository) private personRepository: IPersonRepository, @@ -38,7 +40,7 @@ export class SearchService extends BaseService { @Inject(IPartnerRepository) private partnerRepository: IPartnerRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(SearchService.name); } diff --git a/server/src/services/server.service.spec.ts b/server/src/services/server.service.spec.ts index e0cd41a27e..18e7bde1dc 100644 --- a/server/src/services/server.service.spec.ts +++ b/server/src/services/server.service.spec.ts @@ -1,4 +1,5 @@ import { SystemMetadataKey } from 'src/enum'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IServerInfoRepository } from 'src/interfaces/server-info.interface'; @@ -6,6 +7,7 @@ import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { ServerService } from 'src/services/server.service'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newServerInfoRepositoryMock } from 'test/repositories/server-info.repository.mock'; @@ -16,6 +18,7 @@ import { Mocked } from 'vitest'; describe(ServerService.name, () => { let sut: ServerService; + let configMock: Mocked; let storageMock: Mocked; let userMock: Mocked; let serverInfoMock: Mocked; @@ -24,6 +27,7 @@ describe(ServerService.name, () => { let cryptoMock: Mocked; beforeEach(() => { + configMock = newConfigRepositoryMock(); storageMock = newStorageRepositoryMock(); userMock = newUserRepositoryMock(); serverInfoMock = newServerInfoRepositoryMock(); @@ -31,7 +35,7 @@ describe(ServerService.name, () => { loggerMock = newLoggerRepositoryMock(); cryptoMock = newCryptoRepositoryMock(); - sut = new ServerService(userMock, storageMock, systemMock, serverInfoMock, loggerMock, cryptoMock); + sut = new ServerService(configMock, userMock, storageMock, systemMock, serverInfoMock, loggerMock, cryptoMock); }); it('should work', () => { diff --git a/server/src/services/server.service.ts b/server/src/services/server.service.ts index ed1533f667..ffab0c5a89 100644 --- a/server/src/services/server.service.ts +++ b/server/src/services/server.service.ts @@ -15,6 +15,7 @@ import { UsageByUserDto, } from 'src/dtos/server.dto'; import { StorageFolder, SystemMetadataKey } from 'src/enum'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IServerInfoRepository } from 'src/interfaces/server-info.interface'; @@ -23,13 +24,13 @@ import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interf import { IUserRepository, UserStatsQueryResponse } from 'src/interfaces/user.interface'; import { BaseService } from 'src/services/base.service'; import { asHumanReadable } from 'src/utils/bytes'; -import { isUsingConfigFile } from 'src/utils/config'; import { mimeTypes } from 'src/utils/mime-types'; import { isDuplicateDetectionEnabled, isFacialRecognitionEnabled, isSmartSearchEnabled } from 'src/utils/misc'; @Injectable() export class ServerService extends BaseService { constructor( + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(IUserRepository) private userRepository: IUserRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, @@ -37,7 +38,7 @@ export class ServerService extends BaseService { @Inject(ILoggerRepository) logger: ILoggerRepository, @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(ServerService.name); } @@ -91,6 +92,7 @@ export class ServerService extends BaseService { async getFeatures(): Promise { const { reverseGeocoding, metadata, map, machineLearning, trash, oauth, passwordLogin, notifications } = await this.getConfig({ withCache: false }); + const { configFile } = this.configRepository.getEnv(); return { smartSearch: isSmartSearchEnabled(machineLearning), @@ -105,7 +107,7 @@ export class ServerService extends BaseService { oauth: oauth.enabled, oauthAutoLaunch: oauth.autoLaunch, passwordLogin: passwordLogin.enabled, - configFile: isUsingConfigFile(), + configFile: !!configFile, email: notifications.smtp.enabled, }; } diff --git a/server/src/services/shared-link.service.spec.ts b/server/src/services/shared-link.service.spec.ts index 0fd47b612e..28afe94b9f 100644 --- a/server/src/services/shared-link.service.spec.ts +++ b/server/src/services/shared-link.service.spec.ts @@ -3,6 +3,7 @@ import _ from 'lodash'; import { DEFAULT_EXTERNAL_DOMAIN } from 'src/constants'; import { AssetIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { SharedLinkType } from 'src/enum'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface'; @@ -13,6 +14,7 @@ import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { sharedLinkResponseStub, sharedLinkStub } from 'test/fixtures/shared-link.stub'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newSharedLinkRepositoryMock } from 'test/repositories/shared-link.repository.mock'; @@ -22,6 +24,7 @@ import { Mocked } from 'vitest'; describe(SharedLinkService.name, () => { let sut: SharedLinkService; let accessMock: IAccessRepositoryMock; + let configMock: Mocked; let cryptoMock: Mocked; let shareMock: Mocked; let systemMock: Mocked; @@ -29,12 +32,13 @@ describe(SharedLinkService.name, () => { beforeEach(() => { accessMock = newAccessRepositoryMock(); + configMock = newConfigRepositoryMock(); cryptoMock = newCryptoRepositoryMock(); shareMock = newSharedLinkRepositoryMock(); systemMock = newSystemMetadataRepositoryMock(); logMock = newLoggerRepositoryMock(); - sut = new SharedLinkService(accessMock, cryptoMock, logMock, shareMock, systemMock); + sut = new SharedLinkService(accessMock, configMock, cryptoMock, logMock, shareMock, systemMock); }); it('should work', () => { diff --git a/server/src/services/shared-link.service.ts b/server/src/services/shared-link.service.ts index 883270f808..f2b0ea3c65 100644 --- a/server/src/services/shared-link.service.ts +++ b/server/src/services/shared-link.service.ts @@ -15,6 +15,7 @@ import { AssetEntity } from 'src/entities/asset.entity'; import { SharedLinkEntity } from 'src/entities/shared-link.entity'; import { Permission, SharedLinkType } from 'src/enum'; import { IAccessRepository } from 'src/interfaces/access.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface'; @@ -27,12 +28,13 @@ import { OpenGraphTags } from 'src/utils/misc'; export class SharedLinkService extends BaseService { constructor( @Inject(IAccessRepository) private access: IAccessRepository, + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, @Inject(ISharedLinkRepository) private repository: ISharedLinkRepository, @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(SharedLinkService.name); } diff --git a/server/src/services/smart-info.service.spec.ts b/server/src/services/smart-info.service.spec.ts index 97d22da9b8..be9fab54c6 100644 --- a/server/src/services/smart-info.service.spec.ts +++ b/server/src/services/smart-info.service.spec.ts @@ -1,5 +1,6 @@ import { SystemConfig } from 'src/config'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; @@ -11,6 +12,7 @@ import { getCLIPModelInfo } from 'src/utils/misc'; import { assetStub } from 'test/fixtures/asset.stub'; import { systemConfigStub } from 'test/fixtures/system-config.stub'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; @@ -22,6 +24,7 @@ import { Mocked } from 'vitest'; describe(SmartInfoService.name, () => { let sut: SmartInfoService; let assetMock: Mocked; + let configMock: Mocked; let systemMock: Mocked; let jobMock: Mocked; let searchMock: Mocked; @@ -31,13 +34,24 @@ describe(SmartInfoService.name, () => { beforeEach(() => { assetMock = newAssetRepositoryMock(); + configMock = newConfigRepositoryMock(); systemMock = newSystemMetadataRepositoryMock(); searchMock = newSearchRepositoryMock(); jobMock = newJobRepositoryMock(); machineMock = newMachineLearningRepositoryMock(); databaseMock = newDatabaseRepositoryMock(); loggerMock = newLoggerRepositoryMock(); - sut = new SmartInfoService(assetMock, databaseMock, jobMock, machineMock, searchMock, systemMock, loggerMock); + + sut = new SmartInfoService( + assetMock, + configMock, + databaseMock, + jobMock, + machineMock, + searchMock, + systemMock, + loggerMock, + ); assetMock.getByIds.mockResolvedValue([assetStub.image]); }); diff --git a/server/src/services/smart-info.service.ts b/server/src/services/smart-info.service.ts index 5db4236d58..6f24dafbfe 100644 --- a/server/src/services/smart-info.service.ts +++ b/server/src/services/smart-info.service.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { SystemConfig } from 'src/config'; import { OnEvent } from 'src/decorators'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { DatabaseLock, IDatabaseRepository } from 'src/interfaces/database.interface'; import { ArgOf } from 'src/interfaces/event.interface'; import { @@ -26,6 +27,7 @@ import { usePagination } from 'src/utils/pagination'; export class SmartInfoService extends BaseService { constructor( @Inject(IAssetRepository) private assetRepository: IAssetRepository, + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, @Inject(IMachineLearningRepository) private machineLearning: IMachineLearningRepository, @@ -33,7 +35,7 @@ export class SmartInfoService extends BaseService { @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(SmartInfoService.name); } diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index 36a50c41bd..aa127c2afc 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -4,6 +4,7 @@ import { AssetEntity } from 'src/entities/asset.entity'; import { AssetPathType } from 'src/enum'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { JobStatus } from 'src/interfaces/job.interface'; @@ -19,6 +20,7 @@ import { assetStub } from 'test/fixtures/asset.stub'; import { userStub } from 'test/fixtures/user.stub'; import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; @@ -33,6 +35,7 @@ describe(StorageTemplateService.name, () => { let sut: StorageTemplateService; let albumMock: Mocked; let assetMock: Mocked; + let configMock: Mocked; let cryptoMock: Mocked; let databaseMock: Mocked; let moveMock: Mocked; @@ -49,6 +52,7 @@ describe(StorageTemplateService.name, () => { beforeEach(() => { assetMock = newAssetRepositoryMock(); albumMock = newAlbumRepositoryMock(); + configMock = newConfigRepositoryMock(); cryptoMock = newCryptoRepositoryMock(); databaseMock = newDatabaseRepositoryMock(); moveMock = newMoveRepositoryMock(); @@ -63,6 +67,7 @@ describe(StorageTemplateService.name, () => { sut = new StorageTemplateService( albumMock, assetMock, + configMock, systemMock, moveMock, personMock, diff --git a/server/src/services/storage-template.service.ts b/server/src/services/storage-template.service.ts index 6ce79be33f..c0bf11b186 100644 --- a/server/src/services/storage-template.service.ts +++ b/server/src/services/storage-template.service.ts @@ -18,6 +18,7 @@ import { AssetEntity } from 'src/entities/asset.entity'; import { AssetPathType, AssetType, StorageFolder } from 'src/enum'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { DatabaseLock, IDatabaseRepository } from 'src/interfaces/database.interface'; import { ArgOf } from 'src/interfaces/event.interface'; @@ -63,6 +64,7 @@ export class StorageTemplateService extends BaseService { constructor( @Inject(IAlbumRepository) private albumRepository: IAlbumRepository, @Inject(IAssetRepository) private assetRepository: IAssetRepository, + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, @Inject(IMoveRepository) moveRepository: IMoveRepository, @Inject(IPersonRepository) personRepository: IPersonRepository, @@ -72,10 +74,11 @@ export class StorageTemplateService extends BaseService { @Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(StorageTemplateService.name); this.storageCore = StorageCore.create( assetRepository, + configRepository, cryptoRepository, moveRepository, personRepository, diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index 0e45e0b694..5782443c75 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -12,11 +12,13 @@ import { VideoCodec, VideoContainer, } from 'src/enum'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { QueueName } from 'src/interfaces/job.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { SystemConfigService } from 'src/services/system-config.service'; +import { mockEnvData, newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metadata.repository.mock'; @@ -187,16 +189,18 @@ const updatedConfig = Object.freeze({ describe(SystemConfigService.name, () => { let sut: SystemConfigService; + let configMock: Mocked; let systemMock: Mocked; let eventMock: Mocked; let loggerMock: Mocked; beforeEach(() => { - delete process.env.IMMICH_CONFIG_FILE; - systemMock = newSystemMetadataRepositoryMock(); + configMock = newConfigRepositoryMock(); eventMock = newEventRepositoryMock(); + systemMock = newSystemMetadataRepositoryMock(); loggerMock = newLoggerRepositoryMock(); - sut = new SystemConfigService(systemMock, eventMock, loggerMock); + + sut = new SystemConfigService(configMock, eventMock, systemMock, loggerMock); }); it('should work', () => { @@ -231,8 +235,7 @@ describe(SystemConfigService.name, () => { }); it('should load the config from a json file', async () => { - process.env.IMMICH_CONFIG_FILE = 'immich-config.json'; - + configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); systemMock.readFile.mockResolvedValue(JSON.stringify(partialConfig)); await expect(sut.getSystemConfig()).resolves.toEqual(updatedConfig); @@ -241,7 +244,7 @@ describe(SystemConfigService.name, () => { }); it('should log errors with the config file', async () => { - process.env.IMMICH_CONFIG_FILE = 'immich-config.json'; + configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); systemMock.readFile.mockResolvedValue(`{ "ffmpeg2": true, "ffmpeg2": true }`); @@ -256,7 +259,7 @@ describe(SystemConfigService.name, () => { }); it('should load the config from a yaml file', async () => { - process.env.IMMICH_CONFIG_FILE = 'immich-config.yaml'; + configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.yaml' })); const partialConfig = ` ffmpeg: crf: 30 @@ -275,7 +278,7 @@ describe(SystemConfigService.name, () => { }); it('should accept an empty configuration file', async () => { - process.env.IMMICH_CONFIG_FILE = 'immich-config.json'; + configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); systemMock.readFile.mockResolvedValue(JSON.stringify({})); await expect(sut.getSystemConfig()).resolves.toEqual(defaults); @@ -284,7 +287,7 @@ describe(SystemConfigService.name, () => { }); it('should allow underscores in the machine learning url', async () => { - process.env.IMMICH_CONFIG_FILE = 'immich-config.json'; + configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); const partialConfig = { machineLearning: { url: 'immich_machine_learning' } }; systemMock.readFile.mockResolvedValue(JSON.stringify(partialConfig)); @@ -300,7 +303,7 @@ describe(SystemConfigService.name, () => { for (const { should, externalDomain, result } of externalDomainTests) { it(`should normalize an external domain ${should}`, async () => { - process.env.IMMICH_CONFIG_FILE = 'immich-config.json'; + configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); const partialConfig = { server: { externalDomain } }; systemMock.readFile.mockResolvedValue(JSON.stringify(partialConfig)); @@ -310,7 +313,7 @@ describe(SystemConfigService.name, () => { } it('should warn for unknown options in yaml', async () => { - process.env.IMMICH_CONFIG_FILE = 'immich-config.yaml'; + configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.yaml' })); const partialConfig = ` unknownOption: true `; @@ -331,7 +334,7 @@ describe(SystemConfigService.name, () => { for (const test of tests) { it(`should ${test.should}`, async () => { - process.env.IMMICH_CONFIG_FILE = 'immich-config.json'; + configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); systemMock.readFile.mockResolvedValue(JSON.stringify(test.config)); if (test.warn) { @@ -390,7 +393,7 @@ describe(SystemConfigService.name, () => { }); it('should throw an error if a config file is in use', async () => { - process.env.IMMICH_CONFIG_FILE = 'immich-config.json'; + configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); systemMock.readFile.mockResolvedValue(JSON.stringify({})); await expect(sut.updateSystemConfig(defaults)).rejects.toBeInstanceOf(BadRequestException); expect(systemMock.set).not.toHaveBeenCalled(); diff --git a/server/src/services/system-config.service.ts b/server/src/services/system-config.service.ts index acf9f542ca..017dce9894 100644 --- a/server/src/services/system-config.service.ts +++ b/server/src/services/system-config.service.ts @@ -15,21 +15,23 @@ import { import { OnEvent } from 'src/decorators'; import { SystemConfigDto, SystemConfigTemplateStorageOptionDto, mapConfig } from 'src/dtos/system-config.dto'; import { LogLevel } from 'src/enum'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ArgOf, IEventRepository } from 'src/interfaces/event.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { BaseService } from 'src/services/base.service'; -import { clearConfigCache, isUsingConfigFile } from 'src/utils/config'; +import { clearConfigCache } from 'src/utils/config'; import { toPlainObject } from 'src/utils/object'; @Injectable() export class SystemConfigService extends BaseService { constructor( - @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(IEventRepository) private eventRepository: IEventRepository, + @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(SystemConfigService.name); } @@ -67,7 +69,8 @@ export class SystemConfigService extends BaseService { } async updateSystemConfig(dto: SystemConfigDto): Promise { - if (isUsingConfigFile()) { + const { configFile } = this.configRepository.getEnv(); + if (configFile) { throw new BadRequestException('Cannot update configuration while IMMICH_CONFIG_FILE is in use'); } diff --git a/server/src/services/user.service.spec.ts b/server/src/services/user.service.spec.ts index f5b564e86f..4a121dfda2 100644 --- a/server/src/services/user.service.spec.ts +++ b/server/src/services/user.service.spec.ts @@ -2,6 +2,7 @@ import { BadRequestException, InternalServerErrorException, NotFoundException } import { UserEntity } from 'src/entities/user.entity'; import { CacheControl, UserMetadataKey } from 'src/enum'; import { IAlbumRepository } from 'src/interfaces/album.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IJobRepository, JobName } from 'src/interfaces/job.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; @@ -14,6 +15,7 @@ import { authStub } from 'test/fixtures/auth.stub'; import { systemConfigStub } from 'test/fixtures/system-config.stub'; import { userStub } from 'test/fixtures/user.stub'; import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; @@ -34,6 +36,7 @@ describe(UserService.name, () => { let cryptoRepositoryMock: Mocked; let albumMock: Mocked; + let configMock: Mocked; let jobMock: Mocked; let storageMock: Mocked; let systemMock: Mocked; @@ -41,14 +44,24 @@ describe(UserService.name, () => { beforeEach(() => { albumMock = newAlbumRepositoryMock(); - systemMock = newSystemMetadataRepositoryMock(); + configMock = newConfigRepositoryMock(); cryptoRepositoryMock = newCryptoRepositoryMock(); jobMock = newJobRepositoryMock(); storageMock = newStorageRepositoryMock(); + systemMock = newSystemMetadataRepositoryMock(); userMock = newUserRepositoryMock(); loggerMock = newLoggerRepositoryMock(); - sut = new UserService(albumMock, cryptoRepositoryMock, jobMock, storageMock, systemMock, userMock, loggerMock); + sut = new UserService( + albumMock, + configMock, + cryptoRepositoryMock, + jobMock, + storageMock, + systemMock, + userMock, + loggerMock, + ); userMock.get.mockImplementation((userId) => Promise.resolve([userStub.admin, userStub.user1].find((user) => user.id === userId) ?? null), diff --git a/server/src/services/user.service.ts b/server/src/services/user.service.ts index f770d1d3b6..92c9c29994 100644 --- a/server/src/services/user.service.ts +++ b/server/src/services/user.service.ts @@ -12,6 +12,7 @@ import { UserMetadataEntity } from 'src/entities/user-metadata.entity'; import { UserEntity } from 'src/entities/user.entity'; import { CacheControl, StorageFolder, UserMetadataKey } from 'src/enum'; import { IAlbumRepository } from 'src/interfaces/album.interface'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IEntityJob, IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; @@ -26,6 +27,7 @@ import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/uti export class UserService extends BaseService { constructor( @Inject(IAlbumRepository) private albumRepository: IAlbumRepository, + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, @@ -33,7 +35,7 @@ export class UserService extends BaseService { @Inject(IUserRepository) private userRepository: IUserRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(UserService.name); } diff --git a/server/src/services/version.service.spec.ts b/server/src/services/version.service.spec.ts index a611ae5ecc..a71538ea09 100644 --- a/server/src/services/version.service.spec.ts +++ b/server/src/services/version.service.spec.ts @@ -1,6 +1,7 @@ import { DateTime } from 'luxon'; import { serverVersion } from 'src/constants'; import { SystemMetadataKey } from 'src/enum'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; @@ -9,6 +10,7 @@ import { IServerInfoRepository } from 'src/interfaces/server-info.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface'; import { VersionService } from 'src/services/version.service'; +import { newConfigRepositoryMock } from 'test/repositories/config.repository.mock'; import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; @@ -30,6 +32,7 @@ const mockRelease = (version: string) => ({ describe(VersionService.name, () => { let sut: VersionService; + let configMock: Mocked; let databaseMock: Mocked; let eventMock: Mocked; let jobMock: Mocked; @@ -39,6 +42,7 @@ describe(VersionService.name, () => { let loggerMock: Mocked; beforeEach(() => { + configMock = newConfigRepositoryMock(); databaseMock = newDatabaseRepositoryMock(); eventMock = newEventRepositoryMock(); jobMock = newJobRepositoryMock(); @@ -47,7 +51,16 @@ describe(VersionService.name, () => { versionMock = newVersionHistoryRepositoryMock(); loggerMock = newLoggerRepositoryMock(); - sut = new VersionService(databaseMock, eventMock, jobMock, serverMock, systemMock, versionMock, loggerMock); + sut = new VersionService( + configMock, + databaseMock, + eventMock, + jobMock, + serverMock, + systemMock, + versionMock, + loggerMock, + ); }); it('should work', () => { diff --git a/server/src/services/version.service.ts b/server/src/services/version.service.ts index 92bbb3c06d..91b473f128 100644 --- a/server/src/services/version.service.ts +++ b/server/src/services/version.service.ts @@ -6,6 +6,7 @@ import { OnEvent } from 'src/decorators'; import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto'; import { VersionCheckMetadata } from 'src/entities/system-metadata.entity'; import { SystemMetadataKey } from 'src/enum'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { DatabaseLock, IDatabaseRepository } from 'src/interfaces/database.interface'; import { ArgOf, IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; @@ -27,6 +28,7 @@ const asNotification = ({ checkedAt, releaseVersion }: VersionCheckMetadata): Re @Injectable() export class VersionService extends BaseService { constructor( + @Inject(IConfigRepository) configRepository: IConfigRepository, @Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository, @Inject(IEventRepository) private eventRepository: IEventRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, @@ -35,7 +37,7 @@ export class VersionService extends BaseService { @Inject(IVersionHistoryRepository) private versionRepository: IVersionHistoryRepository, @Inject(ILoggerRepository) logger: ILoggerRepository, ) { - super(systemMetadataRepository, logger); + super(configRepository, systemMetadataRepository, logger); this.logger.setContext(VersionService.name); } diff --git a/server/src/utils/config.ts b/server/src/utils/config.ts index 307db173ca..3a6e079e87 100644 --- a/server/src/utils/config.ts +++ b/server/src/utils/config.ts @@ -6,6 +6,7 @@ import * as _ from 'lodash'; import { SystemConfig, defaults } from 'src/config'; import { SystemConfigDto } from 'src/dtos/system-config.dto'; import { SystemMetadataKey } from 'src/enum'; +import { IConfigRepository } from 'src/interfaces/config.interface'; import { DatabaseLock } from 'src/interfaces/database.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; @@ -15,6 +16,7 @@ import { DeepPartial } from 'typeorm'; export type SystemConfigValidator = (config: SystemConfig, newConfig: SystemConfig) => void | Promise; type RepoDeps = { + configRepo: IConfigRepository; metadataRepo: ISystemMetadataRepository; logger: ILoggerRepository; }; @@ -28,10 +30,6 @@ export const clearConfigCache = () => { lastUpdated = null; }; -export const isUsingConfigFile = () => { - return !!process.env.IMMICH_CONFIG_FILE; -}; - export const getConfig = async (repos: RepoDeps, { withCache }: { withCache: boolean }): Promise => { if (!withCache || !config) { const timestamp = lastUpdated; @@ -80,11 +78,12 @@ const loadFromFile = async ({ metadataRepo, logger }: RepoDeps, filepath: string }; const buildConfig = async (repos: RepoDeps) => { - const { metadataRepo, logger } = repos; + const { configRepo, metadataRepo, logger } = repos; + const { configFile } = configRepo.getEnv(); // load partial - const partial = isUsingConfigFile() - ? await loadFromFile(repos, process.env.IMMICH_CONFIG_FILE as string) + const partial = configFile + ? await loadFromFile(repos, configFile) : await metadataRepo.get(SystemMetadataKey.SYSTEM_CONFIG); // merge with defaults @@ -106,7 +105,7 @@ const buildConfig = async (repos: RepoDeps) => { // validate full config const errors = await validate(plainToInstance(SystemConfigDto, config)); if (errors.length > 0) { - if (isUsingConfigFile()) { + if (configFile) { throw new Error(`Invalid value(s) in file: ${errors}`); } else { logger.error('Validation error', errors);