1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-17 01:06:46 +01:00

refactor(server): simplify config init process (#5702)

This commit is contained in:
Jason Rasmussen 2024-01-01 13:16:44 -05:00 committed by GitHub
parent 5f6d09d3da
commit 03eb5ffc5c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 35 additions and 35 deletions

View file

@ -19,7 +19,7 @@ import { SharedLinkService } from './shared-link';
import { SmartInfoService } from './smart-info'; import { SmartInfoService } from './smart-info';
import { StorageService } from './storage'; import { StorageService } from './storage';
import { StorageTemplateService } from './storage-template'; import { StorageTemplateService } from './storage-template';
import { INITIAL_SYSTEM_CONFIG, SystemConfigService } from './system-config'; import { SystemConfigService } from './system-config';
import { TagService } from './tag'; import { TagService } from './tag';
import { UserService } from './user'; import { UserService } from './user';
@ -47,14 +47,6 @@ const providers: Provider[] = [
TagService, TagService,
UserService, UserService,
ImmichLogger, ImmichLogger,
{
provide: INITIAL_SYSTEM_CONFIG,
inject: [SystemConfigService, DatabaseService],
useFactory: async (configService: SystemConfigService, databaseService: DatabaseService) => {
await databaseService.init();
return configService.getConfig();
},
},
]; ];
@Global() @Global()

View file

@ -207,15 +207,15 @@ describe(JobService.name, () => {
}); });
}); });
describe('registerHandlers', () => { describe('init', () => {
it('should register a handler for each queue', async () => { it('should register a handler for each queue', async () => {
await sut.registerHandlers(makeMockHandlers(true)); await sut.init(makeMockHandlers(true));
expect(configMock.load).toHaveBeenCalled(); expect(configMock.load).toHaveBeenCalled();
expect(jobMock.addHandler).toHaveBeenCalledTimes(Object.keys(QueueName).length); expect(jobMock.addHandler).toHaveBeenCalledTimes(Object.keys(QueueName).length);
}); });
it('should subscribe to config changes', async () => { it('should subscribe to config changes', async () => {
await sut.registerHandlers(makeMockHandlers(false)); await sut.init(makeMockHandlers(false));
SystemConfigCore.create(newSystemConfigRepositoryMock(false)).config$.next({ SystemConfigCore.create(newSystemConfigRepositoryMock(false)).config$.next({
job: { job: {
@ -323,7 +323,7 @@ describe(JobService.name, () => {
assetMock.getByIds.mockResolvedValue([]); assetMock.getByIds.mockResolvedValue([]);
} }
await sut.registerHandlers(makeMockHandlers(true)); await sut.init(makeMockHandlers(true));
await jobMock.addHandler.mock.calls[0][2](item); await jobMock.addHandler.mock.calls[0][2](item);
await asyncTick(3); await asyncTick(3);
@ -334,7 +334,7 @@ describe(JobService.name, () => {
}); });
it(`should not queue any jobs when ${item.name} finishes with 'false'`, async () => { 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 jobMock.addHandler.mock.calls[0][2](item);
await asyncTick(3); await asyncTick(3);

View file

@ -120,7 +120,7 @@ export class JobService {
} }
} }
async registerHandlers(jobHandlers: Record<JobName, JobHandler>) { async init(jobHandlers: Record<JobName, JobHandler>) {
const config = await this.configCore.getConfig(); const config = await this.configCore.getConfig();
for (const queueName of Object.values(QueueName)) { for (const queueName of Object.values(QueueName)) {
let concurrency = 1; let concurrency = 1;

View file

@ -27,6 +27,7 @@ import {
} from '@test'; } from '@test';
import { when } from 'jest-when'; import { when } from 'jest-when';
import { Stats } from 'node:fs'; import { Stats } from 'node:fs';
import { SystemConfigCore } from '../system-config';
describe(StorageTemplateService.name, () => { describe(StorageTemplateService.name, () => {
let sut: StorageTemplateService; let sut: StorageTemplateService;
@ -38,7 +39,7 @@ describe(StorageTemplateService.name, () => {
let storageMock: jest.Mocked<IStorageRepository>; let storageMock: jest.Mocked<IStorageRepository>;
let userMock: jest.Mocked<IUserRepository>; let userMock: jest.Mocked<IUserRepository>;
let cryptoMock: jest.Mocked<ICryptoRepository>; let cryptoMock: jest.Mocked<ICryptoRepository>;
let databaseRepository: jest.Mocked<IDatabaseRepository>; let databaseMock: jest.Mocked<IDatabaseRepository>;
it('should work', () => { it('should work', () => {
expect(sut).toBeDefined(); expect(sut).toBeDefined();
@ -53,22 +54,23 @@ describe(StorageTemplateService.name, () => {
storageMock = newStorageRepositoryMock(); storageMock = newStorageRepositoryMock();
userMock = newUserRepositoryMock(); userMock = newUserRepositoryMock();
cryptoMock = newCryptoRepositoryMock(); cryptoMock = newCryptoRepositoryMock();
databaseRepository = newDatabaseRepositoryMock(); databaseMock = newDatabaseRepositoryMock();
configMock.load.mockResolvedValue([{ key: SystemConfigKey.STORAGE_TEMPLATE_ENABLED, value: true }]);
sut = new StorageTemplateService( sut = new StorageTemplateService(
albumMock, albumMock,
assetMock, assetMock,
configMock, configMock,
defaults,
moveMock, moveMock,
personMock, personMock,
storageMock, storageMock,
userMock, userMock,
cryptoMock, cryptoMock,
databaseRepository, databaseMock,
); );
configMock.load.mockResolvedValue([{ key: SystemConfigKey.STORAGE_TEMPLATE_ENABLED, value: true }]); SystemConfigCore.create(configMock).config$.next(defaults);
}); });
describe('handleMigrationSingle', () => { describe('handleMigrationSingle', () => {

View file

@ -21,7 +21,6 @@ import {
} from '../repositories'; } from '../repositories';
import { StorageCore, StorageFolder } from '../storage'; import { StorageCore, StorageFolder } from '../storage';
import { import {
INITIAL_SYSTEM_CONFIG,
supportedDayTokens, supportedDayTokens,
supportedHourTokens, supportedHourTokens,
supportedMinuteTokens, supportedMinuteTokens,
@ -49,32 +48,33 @@ export class StorageTemplateService {
private logger = new ImmichLogger(StorageTemplateService.name); private logger = new ImmichLogger(StorageTemplateService.name);
private configCore: SystemConfigCore; private configCore: SystemConfigCore;
private storageCore: StorageCore; private storageCore: StorageCore;
private template: { private _template: {
compiled: HandlebarsTemplateDelegate<any>; compiled: HandlebarsTemplateDelegate<any>;
raw: string; raw: string;
needsAlbum: boolean; needsAlbum: boolean;
}; } | null = null;
private get template() {
if (!this._template) {
throw new Error('Template not initialized');
}
return this._template;
}
constructor( constructor(
@Inject(IAlbumRepository) private albumRepository: IAlbumRepository, @Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
@Inject(IAssetRepository) private assetRepository: IAssetRepository, @Inject(IAssetRepository) private assetRepository: IAssetRepository,
@Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
@Inject(INITIAL_SYSTEM_CONFIG) config: SystemConfig,
@Inject(IMoveRepository) moveRepository: IMoveRepository, @Inject(IMoveRepository) moveRepository: IMoveRepository,
@Inject(IPersonRepository) personRepository: IPersonRepository, @Inject(IPersonRepository) personRepository: IPersonRepository,
@Inject(IStorageRepository) private storageRepository: IStorageRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository,
@Inject(IUserRepository) private userRepository: IUserRepository, @Inject(IUserRepository) private userRepository: IUserRepository,
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
@Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository, @Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository,
) { ) {
this.template = this.compile(config.storageTemplate.template);
this.configCore = SystemConfigCore.create(configRepository); this.configCore = SystemConfigCore.create(configRepository);
this.configCore.addValidator((config) => this.validate(config)); this.configCore.addValidator((config) => this.validate(config));
this.configCore.config$.subscribe((config) => { this.configCore.config$.subscribe((config) => this.onConfig(config));
const template = config.storageTemplate.template;
this.logger.debug(`Received config, compiling storage template: ${template}`);
this.template = this.compile(template);
});
this.storageCore = StorageCore.create( this.storageCore = StorageCore.create(
assetRepository, assetRepository,
moveRepository, 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) { private compile(template: string) {
return { return {
raw: template, raw: template,

View file

@ -25,5 +25,3 @@ export const supportedPresetTokens = [
'{{y}}/{{y}}-{{WW}}/{{assetId}}', '{{y}}/{{y}}-{{WW}}/{{assetId}}',
'{{album}}/{{filename}}', '{{album}}/{{filename}}',
]; ];
export const INITIAL_SYSTEM_CONFIG = 'INITIAL_SYSTEM_CONFIG';

View file

@ -42,7 +42,7 @@ export class SystemConfigService {
async init() { async init() {
const config = await this.core.getConfig(); const config = await this.core.getConfig();
await this.setLogLevel(config); this.config$.next(config);
} }
get config$() { get config$() {

View file

@ -38,7 +38,7 @@ export class AppService {
async init() { async init() {
await this.databaseService.init(); await this.databaseService.init();
await this.configService.init(); await this.configService.init();
await this.jobService.registerHandlers({ await this.jobService.init({
[JobName.ASSET_DELETION]: (data) => this.assetService.handleAssetDeletion(data), [JobName.ASSET_DELETION]: (data) => this.assetService.handleAssetDeletion(data),
[JobName.ASSET_DELETION_CHECK]: () => this.assetService.handleAssetDeletionCheck(), [JobName.ASSET_DELETION_CHECK]: () => this.assetService.handleAssetDeletionCheck(),
[JobName.DELETE_FILES]: (data: IDeleteFilesJob) => this.storageService.handleDeleteFiles(data), [JobName.DELETE_FILES]: (data: IDeleteFilesJob) => this.storageService.handleDeleteFiles(data),