feat(server,web): system config for admin (#959)

* feat: add admin config module for user configured config, uses it for ffmpeg

* feat: add api endpoint to retrieve admin config settings and values

* feat: add settings panel to admin page on web (wip)

* feat: add api endpoint to update the admin config

* chore: re-generate openapi spec after rebase

* refactor: move from admin config to system config naming

* chore: move away from UseGuards to new @Authenticated decorator

* style: dark mode styling for lists and fix conflicting colors

* wip: 2 column design, no edit button

* refactor: system config

* chore: generate open api

* chore: rm broken test

* chore: cleanup types

* refactor: config module names

Co-authored-by: Zack Pollard <zackpollard@ymail.com>
Co-authored-by: Zack Pollard <zack.pollard@moonpig.com>
This commit is contained in:
Jason Rasmussen
2022-11-14 23:39:32 -05:00
committed by GitHub
parent d3c35ec9c5
commit b5d75e2016
52 changed files with 2062 additions and 38 deletions

View File

@@ -0,0 +1,97 @@
import { SystemConfigEntity, SystemConfigKey, SystemConfigValue } from '@app/database/entities/system-config.entity';
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { In, Repository } from 'typeorm';
type SystemConfigMap = Record<SystemConfigKey, SystemConfigValue>;
const configDefaults: Record<SystemConfigKey, { name: string; value: SystemConfigValue }> = {
[SystemConfigKey.FFMPEG_CRF]: {
name: 'FFmpeg Constant Rate Factor (-crf)',
value: '23',
},
[SystemConfigKey.FFMPEG_PRESET]: {
name: 'FFmpeg preset (-preset)',
value: 'ultrafast',
},
[SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC]: {
name: 'FFmpeg target video codec (-vcodec)',
value: 'libx264',
},
[SystemConfigKey.FFMPEG_TARGET_AUDIO_CODEC]: {
name: 'FFmpeg target audio codec (-acodec)',
value: 'mp3',
},
[SystemConfigKey.FFMPEG_TARGET_SCALING]: {
name: 'FFmpeg target scaling (-vf scale=)',
value: '1280:-2',
},
};
@Injectable()
export class ImmichConfigService {
constructor(
@InjectRepository(SystemConfigEntity)
private systemConfigRepository: Repository<SystemConfigEntity>,
) {}
public async getSystemConfig() {
const items = this._getDefaults();
// override default values
const overrides = await this.systemConfigRepository.find();
for (const override of overrides) {
const item = items.find((_item) => _item.key === override.key);
if (item) {
item.value = override.value;
}
}
return items;
}
public async getSystemConfigMap(): Promise<SystemConfigMap> {
const items = await this.getSystemConfig();
const map: Partial<SystemConfigMap> = {};
for (const { key, value } of items) {
map[key] = value;
}
return map as SystemConfigMap;
}
public async updateSystemConfig(items: SystemConfigEntity[]): Promise<void> {
const deletes: SystemConfigEntity[] = [];
const updates: SystemConfigEntity[] = [];
for (const item of items) {
if (item.value === null || item.value === this._getDefaultValue(item.key)) {
deletes.push(item);
continue;
}
updates.push(item);
}
if (updates.length > 0) {
await this.systemConfigRepository.save(updates);
}
if (deletes.length > 0) {
await this.systemConfigRepository.delete({ key: In(deletes.map((item) => item.key)) });
}
}
private _getDefaults() {
return Object.values(SystemConfigKey).map((key) => ({
key,
defaultValue: configDefaults[key].value,
...configDefaults[key],
}));
}
private _getDefaultValue(key: SystemConfigKey) {
return this._getDefaults().find((item) => item.key === key)?.value || null;
}
}