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

+9 -2
View File
@@ -13,7 +13,9 @@ for (const file of files) {
const migrationJob = read("migration-job.yaml");
assert(migrationJob.includes("name: ZHINIAN_DATA_BACKEND\n value: postgres"), "migration Job must select postgres");
assert(migrationJob.includes("name: DATABASE_APP_ROLE"), "migration Job must provision the Web role");
assert(migrationJob.includes("secretName: zhinian-rds-ca"), "migration Job must mount the RDS CA");
assert(migrationJob.includes("secretName: zhinian-rds-ca"), "migration Job must mount the optional RDS CA used by DATABASE_URL");
assert(!migrationJob.includes("DATABASE_SSL_MODE"), "migration Job must keep database TLS inside DATABASE_URL");
assert(!migrationJob.includes("DATABASE_CA_CERT_PATH"), "migration Job must keep database TLS inside DATABASE_URL");
const web = read("web.yaml");
assert(/^\s*replicas: 1\s*$/m.test(web), "Web must default to one replica until object storage is shared");
@@ -27,10 +29,15 @@ assert(goApi.includes("path: /api/ready"), "Go API must use database-aware readi
assert(goApi.includes("runAsNonRoot: true"), "Go API must run as a non-root user");
assert(goApi.includes("name: zhinian-go-runtime"), "Go API must consume the Go runtime ConfigMap");
assert(goApi.includes("name: zhinian-go-bootstrap"), "Go API must receive bootstrap administrator credentials");
assert(goApi.includes("secretName: zhinian-rds-ca"), "Go API must mount the RDS CA");
assert(goApi.includes("secretName: zhinian-rds-ca"), "Go API must mount the optional RDS CA used by DATABASE_URL");
assert(!goApi.includes("DATABASE_SSL_MODE"), "Go API must keep database TLS inside DATABASE_URL");
assert(!goApi.includes("DATABASE_CA_CERT_PATH"), "Go API must keep database TLS inside DATABASE_URL");
const configMap = read("configmap.yaml");
assert(configMap.includes("ZHINIAN_GO_EMBEDDED_WORKER: \"true\""), "Go runtime ConfigMap must embed the WorkerLoop");
assert(configMap.includes("GO_BACKEND_HOST: 0.0.0.0"), "Go runtime ConfigMap must listen on the Pod interface");
assert(!configMap.includes("DATABASE_SSL_MODE"), "ConfigMaps must not carry database TLS settings");
assert(!configMap.includes("DATABASE_CA_CERT_PATH"), "ConfigMaps must not carry database CA paths");
const ingress = read("ingress.yaml");
assert(ingress.includes("path: /api/internal/worker"), "Ingress must intercept the internal worker prefix");
+20 -8
View File
@@ -12,7 +12,7 @@ export function getScriptDataBackend(env = process.env) {
export function createPostgresPool({ env = process.env, applicationName = "zhinian-script" } = {}) {
const connectionString = 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 = {
connectionString,
@@ -23,15 +23,21 @@ export function createPostgresPool({ env = process.env, applicationName = "zhini
application_name: applicationName
};
const sslMode = env.DATABASE_SSL_MODE?.trim().toLowerCase() || "disable";
const sslMode = parsed.searchParams.get("sslmode")?.trim().toLowerCase() || "verify-full";
if (sslMode === "verify-full") {
const caPath = 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();
return new Pool(config);
}
@@ -57,11 +63,17 @@ function assertConnectionStringContract(connectionString) {
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(env, name, fallback) {