mirror of
https://github.com/immich-app/immich.git
synced 2024-11-28 09:33:27 +02:00
refactor(server): streamline get config, enable the use of arrays (#4562)
This commit is contained in:
parent
8dcc01b2be
commit
013da0aa3d
@ -13,11 +13,10 @@ import {
|
|||||||
VideoCodec,
|
VideoCodec,
|
||||||
} from '@app/infra/entities';
|
} from '@app/infra/entities';
|
||||||
import { BadRequestException, ForbiddenException, Injectable, Logger } from '@nestjs/common';
|
import { BadRequestException, ForbiddenException, Injectable, Logger } from '@nestjs/common';
|
||||||
import { plainToClass } from 'class-transformer';
|
import { plainToInstance } from 'class-transformer';
|
||||||
import { validate } from 'class-validator';
|
import { validate } from 'class-validator';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { DeepPartial } from 'typeorm';
|
|
||||||
import { QueueName } from '../job/job.constants';
|
import { QueueName } from '../job/job.constants';
|
||||||
import { ISystemConfigRepository } from '../repositories';
|
import { ISystemConfigRepository } from '../repositories';
|
||||||
import { SystemConfigDto } from './dto';
|
import { SystemConfigDto } from './dto';
|
||||||
@ -140,7 +139,7 @@ let instance: SystemConfigCore | null;
|
|||||||
export class SystemConfigCore {
|
export class SystemConfigCore {
|
||||||
private logger = new Logger(SystemConfigCore.name);
|
private logger = new Logger(SystemConfigCore.name);
|
||||||
private validators: SystemConfigValidator[] = [];
|
private validators: SystemConfigValidator[] = [];
|
||||||
private configCache: SystemConfig | null = null;
|
private configCache: SystemConfigEntity<SystemConfigValue>[] | null = null;
|
||||||
|
|
||||||
public config$ = new Subject<SystemConfig>();
|
public config$ = new Subject<SystemConfig>();
|
||||||
|
|
||||||
@ -218,9 +217,28 @@ export class SystemConfigCore {
|
|||||||
this.validators.push(validator);
|
this.validators.push(validator);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getConfig(force = false): Promise<SystemConfig> {
|
public async getConfig(force = false): Promise<SystemConfig> {
|
||||||
const configFilePath = process.env.IMMICH_CONFIG_FILE;
|
const configFilePath = process.env.IMMICH_CONFIG_FILE;
|
||||||
return configFilePath ? this.loadFromFile(configFilePath, force) : this.loadFromDatabase();
|
const config = _.cloneDeep(defaults);
|
||||||
|
const overrides = configFilePath ? await this.loadFromFile(configFilePath, force) : await this.repository.load();
|
||||||
|
|
||||||
|
for (const { key, value } of overrides) {
|
||||||
|
// set via dot notation
|
||||||
|
_.set(config, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const errors = await validate(plainToInstance(SystemConfigDto, config), {
|
||||||
|
forbidNonWhitelisted: true,
|
||||||
|
forbidUnknownValues: true,
|
||||||
|
});
|
||||||
|
if (errors.length > 0) {
|
||||||
|
this.logger.error('Validation error', errors);
|
||||||
|
if (configFilePath) {
|
||||||
|
throw new Error(`Invalid value(s) in file: ${errors}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateConfig(config: SystemConfig): Promise<SystemConfig> {
|
public async updateConfig(config: SystemConfig): Promise<SystemConfig> {
|
||||||
@ -246,7 +264,13 @@ export class SystemConfigCore {
|
|||||||
const defaultValue = _.get(defaults, key);
|
const defaultValue = _.get(defaults, key);
|
||||||
const isMissing = !_.has(config, key);
|
const isMissing = !_.has(config, key);
|
||||||
|
|
||||||
if (isMissing || item.value === null || item.value === '' || item.value === defaultValue) {
|
if (
|
||||||
|
isMissing ||
|
||||||
|
item.value === null ||
|
||||||
|
item.value === '' ||
|
||||||
|
item.value === defaultValue ||
|
||||||
|
_.isEqual(item.value, defaultValue)
|
||||||
|
) {
|
||||||
deletes.push(item);
|
deletes.push(item);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -275,34 +299,25 @@ export class SystemConfigCore {
|
|||||||
this.config$.next(newConfig);
|
this.config$.next(newConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadFromDatabase() {
|
|
||||||
const config: DeepPartial<SystemConfig> = {};
|
|
||||||
const overrides = await this.repository.load();
|
|
||||||
for (const { key, value } of overrides) {
|
|
||||||
// set via dot notation
|
|
||||||
_.set(config, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return plainToClass(SystemConfigDto, _.defaultsDeep(config, defaults));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async loadFromFile(filepath: string, force = false) {
|
private async loadFromFile(filepath: string, force = false) {
|
||||||
if (force || !this.configCache) {
|
if (force || !this.configCache) {
|
||||||
try {
|
try {
|
||||||
const overrides = JSON.parse((await this.repository.readFile(filepath)).toString());
|
const file = JSON.parse((await this.repository.readFile(filepath)).toString());
|
||||||
const config = plainToClass(SystemConfigDto, _.defaultsDeep(overrides, defaults));
|
const overrides: SystemConfigEntity<SystemConfigValue>[] = [];
|
||||||
|
|
||||||
const errors = await validate(config, {
|
for (const key of Object.values(SystemConfigKey)) {
|
||||||
whitelist: true,
|
const value = _.get(file, key);
|
||||||
forbidNonWhitelisted: true,
|
this.unsetDeep(file, key);
|
||||||
forbidUnknownValues: true,
|
if (value !== undefined) {
|
||||||
});
|
overrides.push({ key, value });
|
||||||
if (errors.length > 0) {
|
}
|
||||||
this.logger.error('Validation error', errors);
|
|
||||||
throw new Error(`Invalid value(s) in file: ${errors}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.configCache = config;
|
if (!_.isEmpty(file)) {
|
||||||
|
throw new Error(`Unknown keys found: ${file}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.configCache = overrides;
|
||||||
} catch (error: Error | any) {
|
} catch (error: Error | any) {
|
||||||
this.logger.error(`Unable to load configuration file: ${filepath} due to ${error}`, error?.stack);
|
this.logger.error(`Unable to load configuration file: ${filepath} due to ${error}`, error?.stack);
|
||||||
throw new Error('Invalid configuration file');
|
throw new Error('Invalid configuration file');
|
||||||
@ -311,4 +326,15 @@ export class SystemConfigCore {
|
|||||||
|
|
||||||
return this.configCache;
|
return this.configCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private unsetDeep(object: object, key: string) {
|
||||||
|
_.unset(object, key);
|
||||||
|
const path = key.split('.');
|
||||||
|
while (path.pop()) {
|
||||||
|
if (!_.isEmpty(_.get(object, path))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_.unset(object, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user