104 lines
2.9 KiB
TypeScript
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;
|
|
}
|