35 lines
2.6 KiB
TypeScript
Executable File
35 lines
2.6 KiB
TypeScript
Executable File
import { lstat, readFile } from 'node:fs/promises';
|
|
import type { PoolConfig } from 'pg';
|
|
|
|
export function custodyStorage(env: NodeJS.ProcessEnv = process.env): 'sqlite' | 'postgres' {
|
|
const value = env.WIKIDECK_CUSTODY_STORAGE ?? 'sqlite';
|
|
if (value !== 'sqlite' && value !== 'postgres') throw new Error('Invalid custody storage backend');
|
|
return value;
|
|
}
|
|
async function secretFile(path: string | undefined): Promise<string> {
|
|
if (!path) throw new Error('Custody PostgreSQL secret file is required');
|
|
const info = await lstat(path);
|
|
if (!info.isFile() || info.isSymbolicLink() || info.size > 16384 || info.size === 0) throw new Error('Invalid PostgreSQL secret file');
|
|
return (await readFile(path, 'utf8')).trim();
|
|
}
|
|
/** No URL logging, no TLS options in a connection string overriding verified transport. */
|
|
export async function postgresConfig(env: NodeJS.ProcessEnv = process.env): Promise<PoolConfig> {
|
|
try {
|
|
const url = new URL(await secretFile(env.WIKIDECK_CUSTODY_PG_URL_FILE));
|
|
if (!['postgres:', 'postgresql:'].includes(url.protocol) || !url.hostname || url.search || url.hash ||
|
|
!/^\/[a-zA-Z0-9_-]+$/.test(url.pathname)) throw new Error('Invalid PostgreSQL URL');
|
|
const insecure = env.WIKIDECK_CUSTODY_PG_LOCAL_TEST === '1';
|
|
if (env.WIKIDECK_CUSTODY_PG_LOCAL_TEST !== undefined && !insecure) throw new Error('Invalid local-test switch');
|
|
if (insecure && !['127.0.0.1', 'localhost', '[::1]'].includes(url.hostname)) throw new Error('Non-TLS test connections must be loopback');
|
|
if (!insecure && (!url.username || !url.password)) throw new Error('PostgreSQL credentials required');
|
|
if (url.port && (!/^\d+$/.test(url.port) || Number(url.port) < 1 || Number(url.port) > 65535)) throw new Error('Invalid PostgreSQL port');
|
|
const ca = env.WIKIDECK_CUSTODY_PG_CA_FILE ? await secretFile(env.WIKIDECK_CUSTODY_PG_CA_FILE) : undefined;
|
|
return { host: url.hostname.replace(/^\[|\]$/g, ''), port: url.port ? Number(url.port) : 5432,
|
|
database: url.pathname.slice(1), user: decodeURIComponent(url.username), password: decodeURIComponent(url.password),
|
|
ssl: insecure ? false : { rejectUnauthorized: true, ...(ca ? { ca } : {}) },
|
|
max: 4, connectionTimeoutMillis: 5000, idleTimeoutMillis: 10000,
|
|
statement_timeout: 10000, lock_timeout: 3000, idle_in_transaction_session_timeout: 10000,
|
|
application_name: 'wikideck-custody', options: '-c search_path=wk_custody,pg_catalog' };
|
|
} catch { throw new Error('Invalid or inaccessible custody PostgreSQL configuration; check secret files and verified TLS settings'); }
|
|
}
|