import { readFile } from "node:fs/promises"; export async function resolveDeviceDatabaseUrl( environment = process.env, readSecret = readFile, ) { const explicit = optionalValue(environment.DEVICE_DATABASE_URL); if (explicit) return explicit; const host = restrictedValue( environment.DEVICE_DATABASE_HOST, /^[A-Za-z0-9.-]{1,253}$/, "device_database_host_invalid", ); const port = parsePort(environment.DEVICE_DATABASE_PORT, 5432); const database = restrictedValue( environment.DEVICE_DATABASE_NAME, /^[A-Za-z_][A-Za-z0-9_-]{0,62}$/, "device_database_name_invalid", ); const user = restrictedValue( environment.DEVICE_DATABASE_USER, /^[A-Za-z_][A-Za-z0-9_-]{0,62}$/, "device_database_user_invalid", ); const passwordFile = requiredValue( environment.DEVICE_DATABASE_PASSWORD_FILE, "device_database_password_file_required", ); const password = (await readSecret(passwordFile, "utf8")).trim(); if (password.length < 32 || password.length > 512) { throw new Error("device_database_password_invalid"); } return [ "postgresql://", encodeURIComponent(user), ":", encodeURIComponent(password), "@", host, ":", String(port), "/", encodeURIComponent(database), "?sslmode=disable", ].join(""); } function optionalValue(value) { if (typeof value !== "string") return ""; return value.trim(); } function requiredValue(value, errorCode) { const normalized = optionalValue(value); if (!normalized) throw new Error(errorCode); return normalized; } function restrictedValue(value, pattern, errorCode) { const normalized = requiredValue(value, errorCode); if (!pattern.test(normalized)) throw new Error(errorCode); return normalized; } function parsePort(value, fallback) { const parsed = Number(value || fallback); if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 65535) { throw new Error("device_database_port_invalid"); } return parsed; }