mirror of
https://github.com/KevinMidboe/immich.git
synced 2025-10-29 17:40:28 +00:00
feat(server,web): migrate oauth settings from env to system config (#1061)
This commit is contained in:
@@ -16,12 +16,6 @@ const jwtSecretValidator: Joi.CustomValidator<string> = (value) => {
|
||||
return value;
|
||||
};
|
||||
|
||||
const WHEN_OAUTH_ENABLED = Joi.when('OAUTH_ENABLED', {
|
||||
is: true,
|
||||
then: Joi.string().required(),
|
||||
otherwise: Joi.string().optional(),
|
||||
});
|
||||
|
||||
export const immichAppConfig: ConfigModuleOptions = {
|
||||
envFilePath: '.env',
|
||||
isGlobal: true,
|
||||
@@ -34,12 +28,5 @@ export const immichAppConfig: ConfigModuleOptions = {
|
||||
DISABLE_REVERSE_GEOCODING: Joi.boolean().optional().valid(true, false).default(false),
|
||||
REVERSE_GEOCODING_PRECISION: Joi.number().optional().valid(0, 1, 2, 3).default(3),
|
||||
LOG_LEVEL: Joi.string().optional().valid('simple', 'verbose').default('simple'),
|
||||
OAUTH_ENABLED: Joi.bool().valid(true, false).default(false),
|
||||
OAUTH_BUTTON_TEXT: Joi.string().optional().default('Login with OAuth'),
|
||||
OAUTH_AUTO_REGISTER: Joi.bool().valid(true, false).default(true),
|
||||
OAUTH_ISSUER_URL: WHEN_OAUTH_ENABLED,
|
||||
OAUTH_SCOPE: Joi.string().optional().default('openid email profile'),
|
||||
OAUTH_CLIENT_ID: WHEN_OAUTH_ENABLED,
|
||||
OAUTH_CLIENT_SECRET: WHEN_OAUTH_ENABLED,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -1,27 +1,47 @@
|
||||
import { Column, Entity, PrimaryColumn } from 'typeorm';
|
||||
|
||||
@Entity('system_config')
|
||||
export class SystemConfigEntity {
|
||||
export class SystemConfigEntity<T = string> {
|
||||
@PrimaryColumn()
|
||||
key!: SystemConfigKey;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
value!: SystemConfigValue;
|
||||
@Column({ type: 'varchar', nullable: true, transformer: { to: JSON.stringify, from: JSON.parse } })
|
||||
value!: T;
|
||||
}
|
||||
|
||||
export type SystemConfig = SystemConfigEntity[];
|
||||
export type SystemConfigValue = any;
|
||||
|
||||
// dot notation matches path in `SystemConfig`
|
||||
export enum SystemConfigKey {
|
||||
FFMPEG_CRF = 'ffmpeg_crf',
|
||||
FFMPEG_PRESET = 'ffmpeg_preset',
|
||||
FFMPEG_TARGET_VIDEO_CODEC = 'ffmpeg_target_video_codec',
|
||||
FFMPEG_TARGET_AUDIO_CODEC = 'ffmpeg_target_audio_codec',
|
||||
FFMPEG_TARGET_SCALING = 'ffmpeg_target_scaling',
|
||||
FFMPEG_CRF = 'ffmpeg.crf',
|
||||
FFMPEG_PRESET = 'ffmpeg.preset',
|
||||
FFMPEG_TARGET_VIDEO_CODEC = 'ffmpeg.targetVideoCodec',
|
||||
FFMPEG_TARGET_AUDIO_CODEC = 'ffmpeg.targetAudioCodec',
|
||||
FFMPEG_TARGET_SCALING = 'ffmpeg.targetScaling',
|
||||
OAUTH_ENABLED = 'oauth.enabled',
|
||||
OAUTH_ISSUER_URL = 'oauth.issuerUrl',
|
||||
OAUTH_CLIENT_ID = 'oauth.clientId',
|
||||
OAUTH_CLIENT_SECRET = 'oauth.clientSecret',
|
||||
OAUTH_SCOPE = 'oauth.scope',
|
||||
OAUTH_BUTTON_TEXT = 'oauth.buttonText',
|
||||
OAUTH_AUTO_REGISTER = 'oauth.autoRegister',
|
||||
}
|
||||
|
||||
export type SystemConfigValue = string | null;
|
||||
|
||||
export interface SystemConfigItem {
|
||||
key: SystemConfigKey;
|
||||
value: SystemConfigValue;
|
||||
export interface SystemConfig {
|
||||
ffmpeg: {
|
||||
crf: string;
|
||||
preset: string;
|
||||
targetVideoCodec: string;
|
||||
targetAudioCodec: string;
|
||||
targetScaling: string;
|
||||
};
|
||||
oauth: {
|
||||
enabled: boolean;
|
||||
issuerUrl: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
scope: string;
|
||||
buttonText: string;
|
||||
autoRegister: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class TruncateOldConfigItems1670607437008 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`TRUNCATE TABLE "system_config"`);
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,27 @@
|
||||
import { SystemConfigEntity, SystemConfigKey, SystemConfigValue } from '@app/database/entities/system-config.entity';
|
||||
import { SystemConfig, SystemConfigEntity, SystemConfigKey } from '@app/database/entities/system-config.entity';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import * as _ from 'lodash';
|
||||
import { DeepPartial, 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',
|
||||
const defaults: SystemConfig = Object.freeze({
|
||||
ffmpeg: {
|
||||
crf: '23',
|
||||
preset: 'ultrafast',
|
||||
targetVideoCodec: 'libx264',
|
||||
targetAudioCodec: 'mp3',
|
||||
targetScaling: '1280:-2',
|
||||
},
|
||||
[SystemConfigKey.FFMPEG_PRESET]: {
|
||||
name: 'FFmpeg preset (-preset)',
|
||||
value: 'ultrafast',
|
||||
oauth: {
|
||||
enabled: false,
|
||||
issuerUrl: '',
|
||||
clientId: '',
|
||||
clientSecret: '',
|
||||
scope: 'openid email profile',
|
||||
buttonText: 'Login with OAuth',
|
||||
autoRegister: true,
|
||||
},
|
||||
[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 {
|
||||
@@ -35,38 +30,32 @@ export class ImmichConfigService {
|
||||
private systemConfigRepository: Repository<SystemConfigEntity>,
|
||||
) {}
|
||||
|
||||
public async getSystemConfig() {
|
||||
const items = this._getDefaults();
|
||||
public getDefaults(): SystemConfig {
|
||||
return defaults;
|
||||
}
|
||||
|
||||
// override default values
|
||||
public async getConfig() {
|
||||
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;
|
||||
}
|
||||
const config: DeepPartial<SystemConfig> = {};
|
||||
for (const { key, value } of overrides) {
|
||||
// set via dot notation
|
||||
_.set(config, key, value);
|
||||
}
|
||||
|
||||
return items;
|
||||
return _.defaultsDeep(config, defaults) as SystemConfig;
|
||||
}
|
||||
|
||||
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[] = [];
|
||||
public async updateConfig(config: DeepPartial<SystemConfig> | null): Promise<void> {
|
||||
const updates: SystemConfigEntity[] = [];
|
||||
const deletes: SystemConfigEntity[] = [];
|
||||
|
||||
for (const item of items) {
|
||||
if (item.value === null || item.value === this._getDefaultValue(item.key)) {
|
||||
for (const key of Object.values(SystemConfigKey)) {
|
||||
// get via dot notation
|
||||
const item = { key, value: _.get(config, key) };
|
||||
const defaultValue = _.get(defaults, key);
|
||||
const isMissing = !_.has(config, key);
|
||||
|
||||
if (isMissing || item.value === null || item.value === '' || item.value === defaultValue) {
|
||||
deletes.push(item);
|
||||
continue;
|
||||
}
|
||||
@@ -82,16 +71,4 @@ export class ImmichConfigService {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user