simplify production database configuration

This commit is contained in:
brother7 committed 2026-08-16 12:40:43 +08:00
1 parent 8cb8b5e463
commit b26f9679ab
17 files changed
+199 -234

No files matched your search

+20 -9
View File
@@ -97,7 +97,7 @@ function getPool(): Pool {
const connectionString = process.env.DATABASE_URL?.trim();
if (!connectionString) throw new Error("DATABASE_URL is required when ZHINIAN_DATA_BACKEND=postgres");
assertConnectionStringContract(connectionString);
const parsed = assertConnectionStringContract(connectionString);
const config: PoolConfig = {
connectionString,
max: positiveInteger("DATABASE_POOL_MAX", 10),
@@ -106,20 +106,25 @@ function getPool(): Pool {
statement_timeout: positiveInteger("DATABASE_STATEMENT_TIMEOUT_MS", 30_000),
application_name: process.env.DATABASE_APPLICATION_NAME?.trim() || "zhinian-web"
};
const sslMode = process.env.DATABASE_SSL_MODE?.trim().toLowerCase() || "disable";
const sslMode = parsed.searchParams.get("sslmode")?.trim().toLowerCase() || "verify-full";
if (sslMode === "verify-full") {
const caPath = process.env.DATABASE_CA_CERT_PATH?.trim();
if (!caPath) throw new Error("DATABASE_CA_CERT_PATH is required when DATABASE_SSL_MODE=verify-full");
config.ssl = { ca: readFileSync(caPath, "utf8"), rejectUnauthorized: true };
const caPath = parsed.searchParams.get("sslrootcert")?.trim();
config.ssl = {
...(caPath ? { ca: readFileSync(caPath, "utf8") } : {}),
rejectUnauthorized: true
};
} else if (sslMode !== "disable") {
throw new Error("DATABASE_SSL_MODE must be 'disable' or 'verify-full'");
throw new Error("DATABASE_URL sslmode must be 'disable' or 'verify-full'");
}
parsed.searchParams.delete("sslmode");
parsed.searchParams.delete("sslrootcert");
config.connectionString = parsed.toString();
pool = new Pool(config);
pool.on("error", (error) => console.error("Unexpected PostgreSQL pool error", error));
return pool;
}
function assertConnectionStringContract(connectionString: string): void {
function assertConnectionStringContract(connectionString: string): URL {
let parsed: URL;
try {
parsed = new URL(connectionString);
@@ -130,11 +135,17 @@ function assertConnectionStringContract(connectionString: string): void {
throw new Error("DATABASE_URL must use the postgres:// or postgresql:// scheme");
}
const sslParameters = [...parsed.searchParams.keys()].filter((key) => key.toLowerCase().startsWith("ssl"));
if (sslParameters.length > 0) {
const unsupportedSSLParameters = sslParameters.filter((key) => !["sslmode", "sslrootcert"].includes(key.toLowerCase()));
if (unsupportedSSLParameters.length > 0) {
throw new Error(
`DATABASE_URL must not contain SSL query parameters (${sslParameters.join(", ")}); use DATABASE_SSL_MODE and DATABASE_CA_CERT_PATH`
`DATABASE_URL contains unsupported SSL query parameters (${unsupportedSSLParameters.join(", ")}); use sslmode and optional sslrootcert`
);
}
const sslMode = parsed.searchParams.get("sslmode")?.trim().toLowerCase();
if (sslMode && sslMode !== "disable" && sslMode !== "verify-full") {
throw new Error("DATABASE_URL sslmode must be 'disable' or 'verify-full'");
}
return parsed;
}
function positiveInteger(name: string, fallback: number): number {