fix: use PK01 public PostgreSQL endpoint

This commit is contained in:
Codex
2026-06-12 05:37:16 +00:00
parent 23803d96f7
commit 9e8197e2cc
8 changed files with 130 additions and 8 deletions
+81 -4
View File
@@ -1,4 +1,5 @@
import { createHash, randomBytes } from "node:crypto";
import { spawnSync } from "node:child_process";
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, isAbsolute, join } from "node:path";
import type { UniDeskConfig } from "./config";
@@ -248,10 +249,23 @@ interface RemoteFacts {
appConnectionOk: boolean | null;
appConnectionSsl: boolean | null;
appConnectionHost: string | null;
appConnectionError: string | null;
};
raw?: Record<string, unknown>;
}
interface ControllerConnectionProbe {
attempted: boolean;
ok: boolean | null;
ssl: boolean | null;
host: string;
port: number;
clientAddr: string | null;
serverAddr: string | null;
psqlVersion: string | null;
error: string | null;
}
interface RemoteJobStart {
ok: boolean;
remoteJobId: string | null;
@@ -367,6 +381,8 @@ async function status(config: UniDeskConfig, options: PlatformDbOptions): Promis
const secrets = inspectSecrets(pg, false);
const remote = await remoteFacts(config, pg, secrets);
const facts = remote.parsed;
const controllerConnection = controllerConnectionProbe(pg, secrets);
const endpointHealthy = controllerConnection.ok === true && controllerConnection.ssl === true;
const deploymentHealthy = facts !== null
&& facts.postgres.packageInstalled
&& facts.postgres.serviceActive
@@ -376,7 +392,14 @@ async function status(config: UniDeskConfig, options: PlatformDbOptions): Promis
&& facts.network.port5432Listening
&& facts.postgres.appConnectionOk === true
&& facts.postgres.appConnectionSsl === true;
const cutoverReady = deploymentHealthy && facts.network.dns.resolves;
const cutoverReady = deploymentHealthy && secrets.ok && endpointHealthy;
const cutoverBlockers = cutoverReady
? []
: [
...(deploymentHealthy ? [] : ["deployment-unhealthy"]),
...(secrets.ok ? [] : ["secrets-unhealthy"]),
...(endpointHealthy ? [] : [controllerConnection.attempted ? "connection-host-probe-failed" : "connection-host-probe-unavailable"]),
];
return {
ok: remote.capture.exitCode === 0 && facts !== null && deploymentHealthy && secrets.ok,
action: "platform-db-postgres-status",
@@ -386,7 +409,9 @@ async function status(config: UniDeskConfig, options: PlatformDbOptions): Promis
healthy: deploymentHealthy,
deploymentHealthy,
cutoverReady,
cutoverBlockers: cutoverReady ? [] : ["dns-db-pikapython-unresolved"],
cutoverBlockers,
connectionHost: pg.postgres.network.connectionHost,
publicDns: pg.postgres.network.publicDns,
pgVersion: pg.postgres.package.version,
packageInstalled: facts.postgres.packageInstalled,
serviceActive: facts.postgres.serviceActive,
@@ -399,10 +424,13 @@ async function status(config: UniDeskConfig, options: PlatformDbOptions): Promis
appConnectionOk: facts.postgres.appConnectionOk,
appConnectionSsl: facts.postgres.appConnectionSsl,
appConnectionHost: facts.postgres.appConnectionHost,
appConnectionError: facts.postgres.appConnectionError,
connectionHostProbe: controllerConnection,
port5432Listening: facts.network.port5432Listening,
serverVersion: facts.postgres.serverVersion,
secretsOk: secrets.ok,
dnsRequiredBeforeSub2ApiCutover: true,
dnsRequiredBeforeSub2ApiCutover: false,
dnsDisposition: facts.network.dns.resolves ? "optional-alias-resolves" : "optional-alias-unresolved",
},
remoteFacts: facts,
secrets: secretSummary(secrets),
@@ -1042,6 +1070,10 @@ function quoteEnv(value: string): string {
return `'${value.replaceAll("'", "'\"'\"'")}'`;
}
function compactText(value: string): string {
return value.replace(/\s+/gu, " ").trim().slice(0, 500);
}
function fingerprintValues(values: Record<string, string>, keys: string[]): string {
const material = keys
.slice()
@@ -1127,7 +1159,7 @@ function desiredChecks(pg: PostgresHostConfig, facts: RemoteFacts, secrets: Secr
{ name: "pgdg-repo-release", ok: facts.repo.reachable, releaseUrl: facts.repo.releaseUrl, firstLine: facts.repo.firstLine },
{ name: "secrets", ok: secrets.ok, entries: secrets.entries.map((entry) => ({ sourceRef: entry.sourceRef, action: entry.action, missingKeys: entry.missingKeys })) },
{ name: "pg16-required", ok: pg.postgres.package.version === "16", detail: "Sub2API deployment requires PostgreSQL 16 per issue #281 update." },
{ name: "dns-db-pikapython", ok: facts.network.dns.resolves, host: facts.network.dns.hostname, addresses: facts.network.dns.addresses, requiredBeforeSub2ApiCutover: true },
{ name: "dns-db-pikapython", ok: true, host: facts.network.dns.hostname, addresses: facts.network.dns.addresses, requiredBeforeSub2ApiCutover: false, disposition: facts.network.dns.resolves ? "optional-alias-resolves" : "optional-alias-unresolved" },
{ name: "postgres-tls-required", ok: pg.postgres.network.tls.enabled && pg.postgres.network.sslmode === "require", transport: pg.postgres.network.transport, sslmode: pg.postgres.network.sslmode },
{ name: "remote-pg-hba-hostssl", ok: pg.postgres.auth.pgHba.filter((rule) => rule.type !== "local" && rule.address !== "127.0.0.1/32").every((rule) => rule.type === "hostssl"), detail: "remote PostgreSQL access must use hostssl so plaintext remote connections are refused" },
{ name: "postgres-ssl-runtime", ok: !facts.postgres.packageInstalled || facts.postgres.sslOn, observed: facts.postgres.sslOn, disposition: facts.postgres.packageInstalled ? "must-be-on" : "will-enable-on-apply" },
@@ -1151,6 +1183,50 @@ function appProbe(pg: PostgresHostConfig, secrets: SecretInspection | null): { u
return { user: role.name, password, database: database.name };
}
function controllerConnectionProbe(pg: PostgresHostConfig, secrets: SecretInspection): ControllerConnectionProbe {
const host = pg.postgres.network.connectionHost;
const port = pg.postgres.network.port;
const base = { host, port, clientAddr: null, serverAddr: null };
if (!secrets.ok) {
return { ...base, attempted: false, ok: null, ssl: null, psqlVersion: null, error: "secrets-unhealthy" };
}
const probe = appProbe(pg, secrets);
if (probe === null) {
return { ...base, attempted: false, ok: null, ssl: null, psqlVersion: null, error: "probe-secret-material-unavailable" };
}
const version = spawnSync("psql", ["--version"], { encoding: "utf8", timeout: 5_000 });
if (version.error !== undefined || version.status !== 0) {
return {
...base,
attempted: false,
ok: null,
ssl: null,
psqlVersion: null,
error: compactText(version.error?.message ?? version.stderr ?? "psql-unavailable"),
};
}
const conn = `host=${host} port=${port} user=${probe.user} dbname=${probe.database} sslmode=require connect_timeout=8`;
const result = spawnSync("psql", ["-Atq", "-F", "\t", conn, "-c", "SELECT inet_client_addr()::text, inet_server_addr()::text, ssl::text FROM pg_stat_ssl WHERE pid = pg_backend_pid();"], {
encoding: "utf8",
timeout: 12_000,
env: { ...process.env, PGPASSWORD: probe.password },
});
const line = result.stdout.trim().split(/\r?\n/u).find((item) => item.trim().length > 0);
const fields = line?.split("\t") ?? [];
const sslText = (fields[2] ?? "").toLowerCase();
const ssl = ["t", "true", "1", "on"].includes(sslText) ? true : ["f", "false", "0", "off"].includes(sslText) ? false : null;
return {
...base,
attempted: true,
ok: result.status === 0 && ssl === true,
ssl,
clientAddr: fields[0] ?? null,
serverAddr: fields[1] ?? null,
psqlVersion: version.stdout.trim() || null,
error: result.status === 0 ? null : compactText(result.stderr || result.error?.message || "psql-connection-failed"),
};
}
function factsScript(pg: PostgresHostConfig, probe: { user: string; password: string; database: string } | null): string {
const release = releaseUrl(pg);
const dataDir = pg.postgres.paths.dataDir;
@@ -1280,6 +1356,7 @@ payload = {
"appConnectionOk": None if text("appConnRc") == "" else text("appConnRc") == "0",
"appConnectionSsl": None if text("appSsl") == "" else text("appSsl").lower() in {"t", "true", "1", "on"},
"appConnectionHost": text("appConnHost") or None,
"appConnectionError": text("appConnErr") or None,
},
}
print(json.dumps(payload, ensure_ascii=False, indent=2))