diff --git a/server/src/domain/domain.module.ts b/server/src/domain/domain.module.ts index ff666d89a2..f1d0ddc0e5 100644 --- a/server/src/domain/domain.module.ts +++ b/server/src/domain/domain.module.ts @@ -19,7 +19,7 @@ import { SharedLinkService } from './shared-link'; import { SmartInfoService } from './smart-info'; import { StorageService } from './storage'; import { StorageTemplateService } from './storage-template'; -import { INITIAL_SYSTEM_CONFIG, SystemConfigService } from './system-config'; +import { SystemConfigService } from './system-config'; import { TagService } from './tag'; import { UserService } from './user'; @@ -47,14 +47,6 @@ const providers: Provider[] = [ TagService, UserService, ImmichLogger, - { - provide: INITIAL_SYSTEM_CONFIG, - inject: [SystemConfigService, DatabaseService], - useFactory: async (configService: SystemConfigService, databaseService: DatabaseService) => { - await databaseService.init(); - return configService.getConfig(); - }, - }, ]; @Global() diff --git a/server/src/domain/job/job.service.spec.ts b/server/src/domain/job/job.service.spec.ts index decf269987..1e5da85729 100644 --- a/server/src/domain/job/job.service.spec.ts +++ b/server/src/domain/job/job.service.spec.ts @@ -207,15 +207,15 @@ describe(JobService.name, () => { }); }); - describe('registerHandlers', () => { + describe('init', () => { it('should register a handler for each queue', async () => { - await sut.registerHandlers(makeMockHandlers(true)); + await sut.init(makeMockHandlers(true)); expect(configMock.load).toHaveBeenCalled(); expect(jobMock.addHandler).toHaveBeenCalledTimes(Object.keys(QueueName).length); }); it('should subscribe to config changes', async () => { - await sut.registerHandlers(makeMockHandlers(false)); + await sut.init(makeMockHandlers(false)); SystemConfigCore.create(newSystemConfigRepositoryMock(false)).config$.next({ job: { @@ -323,7 +323,7 @@ describe(JobService.name, () => { assetMock.getByIds.mockResolvedValue([]); } - await sut.registerHandlers(makeMockHandlers(true)); + await sut.init(makeMockHandlers(true)); await jobMock.addHandler.mock.calls[0][2](item); await asyncTick(3); @@ -334,7 +334,7 @@ describe(JobService.name, () => { }); it(`should not queue any jobs when ${item.name} finishes with 'false'`, async () => { - await sut.registerHandlers(makeMockHandlers(false)); + await sut.init(makeMockHandlers(false)); await jobMock.addHandler.mock.calls[0][2](item); await asyncTick(3); diff --git a/server/src/domain/job/job.service.ts b/server/src/domain/job/job.service.ts index 312b862f69..f622dbdf20 100644 --- a/server/src/domain/job/job.service.ts +++ b/server/src/domain/job/job.service.ts @@ -120,7 +120,7 @@ export class JobService { } } - async registerHandlers(jobHandlers: Record) { + async init(jobHandlers: Record) { const config = await this.configCore.getConfig(); for (const queueName of Object.values(QueueName)) { let concurrency = 1; diff --git a/server/src/domain/storage-template/storage-template.service.spec.ts b/server/src/domain/storage-template/storage-template.service.spec.ts index 68d0152345..a49f0347aa 100644 --- a/server/src/domain/storage-template/storage-template.service.spec.ts +++ b/server/src/domain/storage-template/storage-template.service.spec.ts @@ -27,6 +27,7 @@ import { } from '@test'; import { when } from 'jest-when'; import { Stats } from 'node:fs'; +import { SystemConfigCore } from '../system-config'; describe(StorageTemplateService.name, () => { let sut: StorageTemplateService; @@ -38,7 +39,7 @@ describe(StorageTemplateService.name, () => { let storageMock: jest.Mocked; let userMock: jest.Mocked; let cryptoMock: jest.Mocked; - let databaseRepository: jest.Mocked; + let databaseMock: jest.Mocked; it('should work', () => { expect(sut).toBeDefined(); @@ -53,22 +54,23 @@ describe(StorageTemplateService.name, () => { storageMock = newStorageRepositoryMock(); userMock = newUserRepositoryMock(); cryptoMock = newCryptoRepositoryMock(); - databaseRepository = newDatabaseRepositoryMock(); + databaseMock = newDatabaseRepositoryMock(); + + configMock.load.mockResolvedValue([{ key: SystemConfigKey.STORAGE_TEMPLATE_ENABLED, value: true }]); sut = new StorageTemplateService( albumMock, assetMock, configMock, - defaults, moveMock, personMock, storageMock, userMock, cryptoMock, - databaseRepository, + databaseMock, ); - configMock.load.mockResolvedValue([{ key: SystemConfigKey.STORAGE_TEMPLATE_ENABLED, value: true }]); + SystemConfigCore.create(configMock).config$.next(defaults); }); describe('handleMigrationSingle', () => { diff --git a/server/src/domain/storage-template/storage-template.service.ts b/server/src/domain/storage-template/storage-template.service.ts index c8d2d3e875..cbed4a06c9 100644 --- a/server/src/domain/storage-template/storage-template.service.ts +++ b/server/src/domain/storage-template/storage-template.service.ts @@ -21,7 +21,6 @@ import { } from '../repositories'; import { StorageCore, StorageFolder } from '../storage'; import { - INITIAL_SYSTEM_CONFIG, supportedDayTokens, supportedHourTokens, supportedMinuteTokens, @@ -49,32 +48,33 @@ export class StorageTemplateService { private logger = new ImmichLogger(StorageTemplateService.name); private configCore: SystemConfigCore; private storageCore: StorageCore; - private template: { + private _template: { compiled: HandlebarsTemplateDelegate; raw: string; needsAlbum: boolean; - }; + } | null = null; + + private get template() { + if (!this._template) { + throw new Error('Template not initialized'); + } + return this._template; + } constructor( @Inject(IAlbumRepository) private albumRepository: IAlbumRepository, @Inject(IAssetRepository) private assetRepository: IAssetRepository, @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, - @Inject(INITIAL_SYSTEM_CONFIG) config: SystemConfig, @Inject(IMoveRepository) moveRepository: IMoveRepository, @Inject(IPersonRepository) personRepository: IPersonRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, @Inject(IUserRepository) private userRepository: IUserRepository, - @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, + @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository, @Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository, ) { - this.template = this.compile(config.storageTemplate.template); this.configCore = SystemConfigCore.create(configRepository); this.configCore.addValidator((config) => this.validate(config)); - this.configCore.config$.subscribe((config) => { - const template = config.storageTemplate.template; - this.logger.debug(`Received config, compiling storage template: ${template}`); - this.template = this.compile(template); - }); + this.configCore.config$.subscribe((config) => this.onConfig(config)); this.storageCore = StorageCore.create( assetRepository, moveRepository, @@ -270,6 +270,14 @@ export class StorageTemplateService { } } + private onConfig(config: SystemConfig) { + const template = config.storageTemplate.template; + if (!this._template || template !== this.template.raw) { + this.logger.debug(`Compiling new storage template: ${template}`); + this._template = this.compile(template); + } + } + private compile(template: string) { return { raw: template, diff --git a/server/src/domain/system-config/system-config.constants.ts b/server/src/domain/system-config/system-config.constants.ts index 65808f6df2..47bddb97b0 100644 --- a/server/src/domain/system-config/system-config.constants.ts +++ b/server/src/domain/system-config/system-config.constants.ts @@ -25,5 +25,3 @@ export const supportedPresetTokens = [ '{{y}}/{{y}}-{{WW}}/{{assetId}}', '{{album}}/{{filename}}', ]; - -export const INITIAL_SYSTEM_CONFIG = 'INITIAL_SYSTEM_CONFIG'; diff --git a/server/src/domain/system-config/system-config.service.ts b/server/src/domain/system-config/system-config.service.ts index 5de4c93987..261f8f9a16 100644 --- a/server/src/domain/system-config/system-config.service.ts +++ b/server/src/domain/system-config/system-config.service.ts @@ -42,7 +42,7 @@ export class SystemConfigService { async init() { const config = await this.core.getConfig(); - await this.setLogLevel(config); + this.config$.next(config); } get config$() { diff --git a/server/src/microservices/app.service.ts b/server/src/microservices/app.service.ts index ef8af48aca..fe734bbff7 100644 --- a/server/src/microservices/app.service.ts +++ b/server/src/microservices/app.service.ts @@ -38,7 +38,7 @@ export class AppService { async init() { await this.databaseService.init(); await this.configService.init(); - await this.jobService.registerHandlers({ + await this.jobService.init({ [JobName.ASSET_DELETION]: (data) => this.assetService.handleAssetDeletion(data), [JobName.ASSET_DELETION_CHECK]: () => this.assetService.handleAssetDeletionCheck(), [JobName.DELETE_FILES]: (data: IDeleteFilesJob) => this.storageService.handleDeleteFiles(data),