All checks were successful
CD / Build and push images (push) Successful in 2m36s
CI / Lint, typecheck, test (push) Successful in 1m50s
CI / Auth e2e pack (push) Successful in 1m58s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 9s
CD / Smoke tests against Test (push) Successful in 1m13s
CD / Promote to Int (push) Successful in 11s
Bootstrap apps/collab as a Hocuspocus WebSocket server (ADR 0003): - pino JSON logging (service=collab) and shared Zod env validation (collabEnvSchema); structured connection open/close logs. - /healthz endpoint (process liveness + PostgreSQL ping) served via the onRequest hook, matching the container-internal path and the proxied /collab/healthz path; any WebSocket handshake is accepted for now (authentication arrives with #34, persistence with #35). - Dockerfile (ESM workspace build) and a compose service on the frontend and internal networks with a healthcheck; dev overlay service and a new COLLAB_PORT variable. - CD builds, pushes, and promotes the collab image; CI builds it on PRs; the smoke suite asserts /collab/healthz through the reverse proxy. - deployment.md/stages.md: proxy routing, per-stage COLLAB_PORT, checklist. Closes #33 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
35 lines
1.1 KiB
TypeScript
35 lines
1.1 KiB
TypeScript
import { Pool } from 'pg';
|
|
|
|
import type { DatabaseProbe } from './health.js';
|
|
|
|
/**
|
|
* A small connection pool to the same PostgreSQL the api uses (ADR 0002). The
|
|
* collab skeleton (issue #33) only needs it for the health probe; the document
|
|
* persistence hooks (issue #35) will reuse this pool for load/store.
|
|
*/
|
|
export function createPool(connectionString: string): Pool {
|
|
return new Pool({
|
|
connectionString,
|
|
max: 4,
|
|
// Fail fast on health probes instead of hanging when the db is unreachable.
|
|
connectionTimeoutMillis: 5000,
|
|
query_timeout: 5000,
|
|
});
|
|
}
|
|
|
|
/** Probe database reachability for the health endpoint. Never throws. */
|
|
export async function pingDatabase(pool: Pool): Promise<DatabaseProbe> {
|
|
try {
|
|
await pool.query('SELECT 1');
|
|
return { ok: true };
|
|
} catch (error) {
|
|
return { ok: false, detail: shortMessage(error) };
|
|
}
|
|
}
|
|
|
|
function shortMessage(error: unknown): string {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
// Keep the health output single-line and free of any connection string.
|
|
return message.split('\n').filter(Boolean).slice(-1)[0]?.slice(0, 200) ?? 'unknown error';
|
|
}
|