Files
nadir-webui/src/lib/server/config.ts
T
2026-06-26 00:31:29 +02:00

104 lines
2.9 KiB
TypeScript

import { env } from '$lib/const/schema';
import { db } from '$lib/server/db';
import { decryptValue, encrypt } from '$lib/server/db/custom-types';
import { settings } from '$lib/server/db/schema';
// Keys editable via /admin/config. Keep in sync with the form.
export const CONFIG_KEYS = [
'DISABLE_SIGNUP',
'ENABLE_2FA',
'ENABLE_EMAIL_AND_PASSWORD',
'FACEBOOK_CLIENT_ID',
'FACEBOOK_CLIENT_SECRET',
'GITHUB_CLIENT_ID',
'GITHUB_CLIENT_SECRET',
'GOOGLE_CLIENT_ID',
'GOOGLE_CLIENT_SECRET',
'ORIGIN',
'SMTP_FROM',
'SMTP_HOST',
'SMTP_PASS',
'SMTP_PORT',
'SMTP_SSL',
'SMTP_USER'
] as const;
export type ConfigKey = (typeof CONFIG_KEYS)[number];
const ENCRYPTED_KEYS = new Set<ConfigKey>([
'FACEBOOK_CLIENT_SECRET',
'GITHUB_CLIENT_SECRET',
'GOOGLE_CLIENT_SECRET',
'SMTP_PASS'
]);
const BOOLEAN_KEYS = new Set<ConfigKey>([
'DISABLE_SIGNUP',
'ENABLE_2FA',
'ENABLE_EMAIL_AND_PASSWORD',
'SMTP_SSL'
]);
const NUMBER_KEYS = new Set<ConfigKey>(['SMTP_PORT']);
type Effective = typeof env;
let cache: Effective | null = null;
export function getConfig(): Effective {
if (!cache) cache = build();
return cache;
}
export function getRawConfig(): Record<ConfigKey, string> {
const rows = db.select().from(settings).all();
const out: Partial<Record<ConfigKey, string>> = {};
for (const row of rows) {
if (!(CONFIG_KEYS as readonly string[]).includes(row.key)) continue;
const key = row.key as ConfigKey;
out[key] = ENCRYPTED_KEYS.has(key) ? (decryptValue(row.value) ?? '') : row.value;
}
return out as Record<ConfigKey, string>;
}
export function refreshConfig(): Effective {
cache = build();
return cache;
}
export function setConfigValues(updates: Partial<Record<ConfigKey, unknown>>): void {
const now = new Date();
for (const [k, v] of Object.entries(updates)) {
const key = k as ConfigKey;
if (!(CONFIG_KEYS as readonly string[]).includes(key)) continue;
const raw =
v === undefined || v === null
? ''
: BOOLEAN_KEYS.has(key)
? v
? 'true'
: 'false'
: String(v);
const stored = ENCRYPTED_KEYS.has(key) && raw !== '' ? encrypt(raw, env.CRYPTO_SECRET) : raw;
db.insert(settings)
.values({ key, updatedAt: now, value: stored })
.onConflictDoUpdate({ set: { updatedAt: now, value: stored }, target: settings.key })
.run();
}
refreshConfig();
}
function build(): Effective {
const rows = db.select().from(settings).all();
const overrides: Record<string, unknown> = {};
for (const row of rows) {
if (!(CONFIG_KEYS as readonly string[]).includes(row.key)) continue;
overrides[row.key] = parseValue(row.key as ConfigKey, row.value);
}
return { ...env, ...overrides } as Effective;
}
function parseValue(key: ConfigKey, raw: string): unknown {
const value = ENCRYPTED_KEYS.has(key) ? (decryptValue(raw) ?? '') : raw;
if (BOOLEAN_KEYS.has(key)) return value === 'true';
if (NUMBER_KEYS.has(key)) return value === '' ? undefined : Number(value);
return value;
}