74 lines
2.0 KiB
JavaScript
74 lines
2.0 KiB
JavaScript
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;
|
|
}
|