feat(server): use nestjs events to validate config (#7986)

* use events for config validation

* chore: better types

* add unit tests

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
Daniel Dietzler
2024-03-17 20:16:02 +01:00
committed by GitHub
parent 14da671bf9
commit 148428a564
14 changed files with 170 additions and 81 deletions
@@ -1,6 +1,7 @@
import { AssetEntity, AssetPathType, AssetType, SystemConfig } from '@app/infra/entities';
import { ImmichLogger } from '@app/infra/logger';
import { Inject, Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import handlebar from 'handlebars';
import * as luxon from 'luxon';
import path from 'node:path';
@@ -18,6 +19,8 @@ import {
IStorageRepository,
ISystemConfigRepository,
IUserRepository,
InternalEvent,
InternalEventMap,
JobStatus,
} from '../repositories';
import { StorageCore, StorageFolder } from '../storage';
@@ -74,7 +77,6 @@ export class StorageTemplateService {
@Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository,
) {
this.configCore = SystemConfigCore.create(configRepository);
this.configCore.addValidator((config) => this.validate(config));
this.configCore.config$.subscribe((config) => this.onConfig(config));
this.storageCore = StorageCore.create(
assetRepository,
@@ -86,6 +88,27 @@ export class StorageTemplateService {
);
}
@OnEvent(InternalEvent.VALIDATE_CONFIG)
validate({ newConfig }: InternalEventMap[InternalEvent.VALIDATE_CONFIG]) {
try {
const { compiled } = this.compile(newConfig.storageTemplate.template);
this.render(compiled, {
asset: {
fileCreatedAt: new Date(),
originalPath: '/upload/test/IMG_123.jpg',
type: AssetType.IMAGE,
id: 'd587e44b-f8c0-4832-9ba3-43268bbf5d4e',
} as AssetEntity,
filename: 'IMG_123',
extension: 'jpg',
albumName: 'album',
});
} catch (error) {
this.logger.warn(`Storage template validation failed: ${JSON.stringify(error)}`);
throw new Error(`Invalid storage template: ${error}`);
}
}
async handleMigrationSingle({ id }: IEntityJob): Promise<JobStatus> {
const config = await this.configCore.getConfig();
const storageTemplateEnabled = config.storageTemplate.enabled;
@@ -259,26 +282,6 @@ export class StorageTemplateService {
}
}
private validate(config: SystemConfig) {
try {
const { compiled } = this.compile(config.storageTemplate.template);
this.render(compiled, {
asset: {
fileCreatedAt: new Date(),
originalPath: '/upload/test/IMG_123.jpg',
type: AssetType.IMAGE,
id: 'd587e44b-f8c0-4832-9ba3-43268bbf5d4e',
} as AssetEntity,
filename: 'IMG_123',
extension: 'jpg',
albumName: 'album',
});
} catch (error) {
this.logger.warn(`Storage template validation failed: ${JSON.stringify(error)}`);
throw new Error(`Invalid storage template: ${error}`);
}
}
private onConfig(config: SystemConfig) {
const template = config.storageTemplate.template;
if (!this._template || template !== this.template.raw) {