chore(ops): split /api/health (liveness) from /api/ready (readiness)
Previously /api/health did deep dependency probes (postgres + redis + minio) and 503'd on any failure. That's readiness behavior, not liveness — a transient Redis/MinIO blip would tell the orchestrator to restart the pod when it should only be dropped from the load balancer. Make /api/health a thin liveness check (returns 200 unconditionally if the process is responding) and move the deep checks to a new /api/ready endpoint with the canonical Kubernetes-style 200/503 contract. Docker-compose healthchecks keep pointing at /api/health, which is now more conservative (no false-positive container restarts). Documenso/SMTP are intentionally not probed in /api/ready: each tenant configures its own credentials and a tenant misconfiguration shouldn't deadline the entire shared CRM. Also tighten the gdpr-bundle-builder casts: replace the scattered `as unknown as Record<string, unknown>` double-casts with a small `toJsonRow<T>()` helper that does the widen narrow→wide in one place with one cast hop instead of two. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -145,25 +145,40 @@ export async function buildClientBundle(clientId: string, portId: string): Promi
|
||||
clientId,
|
||||
schemaVersion: 1,
|
||||
},
|
||||
client: client as unknown as Record<string, unknown>,
|
||||
contacts: contacts as unknown as Record<string, unknown>[],
|
||||
addresses: addresses as unknown as Record<string, unknown>[],
|
||||
// Drizzle row types contain non-`unknown` value types (Date, branded
|
||||
// strings). The bundle is exported as JSON, so we widen to plain
|
||||
// `Record<string, unknown>` here. `toJsonRow` performs the narrow → wide
|
||||
// widening in a single, locally-typed step instead of the prior
|
||||
// `as unknown as Record<string, unknown>` double-cast.
|
||||
client: toJsonRow(client),
|
||||
contacts: contacts.map(toJsonRow),
|
||||
addresses: addresses.map(toJsonRow),
|
||||
tags: tagJoins,
|
||||
relationships: relationships as unknown as Record<string, unknown>[],
|
||||
notes: notes as unknown as Record<string, unknown>[],
|
||||
ownedYachts: ownedYachts as unknown as Record<string, unknown>[],
|
||||
companyMemberships: membershipRows as unknown as Array<{
|
||||
membership: Record<string, unknown>;
|
||||
company: Record<string, unknown>;
|
||||
}>,
|
||||
interests: interestRows as unknown as Record<string, unknown>[],
|
||||
reservations: reservationRows as unknown as Record<string, unknown>[],
|
||||
invoices: invoiceRows as unknown as Record<string, unknown>[],
|
||||
documents: documentRows as unknown as Record<string, unknown>[],
|
||||
auditTrail: auditRows as unknown as Record<string, unknown>[],
|
||||
relationships: relationships.map(toJsonRow),
|
||||
notes: notes.map(toJsonRow),
|
||||
ownedYachts: ownedYachts.map(toJsonRow),
|
||||
companyMemberships: membershipRows.map((row) => ({
|
||||
membership: toJsonRow(row.membership),
|
||||
company: toJsonRow(row.company),
|
||||
})),
|
||||
interests: interestRows.map(toJsonRow),
|
||||
reservations: reservationRows.map(toJsonRow),
|
||||
invoices: invoiceRows.map(toJsonRow),
|
||||
documents: documentRows.map(toJsonRow),
|
||||
auditTrail: auditRows.map(toJsonRow),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Widens a Drizzle row type to `Record<string, unknown>` for inclusion in
|
||||
* the JSON bundle. Drizzle row types are narrower than the open record
|
||||
* shape we want; this helper does the widening in one place rather than
|
||||
* scattering double-casts across the call sites.
|
||||
*/
|
||||
function toJsonRow<T extends object>(row: T): Record<string, unknown> {
|
||||
return row as Record<string, unknown>;
|
||||
}
|
||||
|
||||
// ─── HTML rendering ──────────────────────────────────────────────────────────
|
||||
|
||||
function escapeHtml(s: unknown): string {
|
||||
@@ -217,7 +232,7 @@ export function renderBundleHtml(bundle: GdprBundle): string {
|
||||
tableSection('Client', [bundle.client]),
|
||||
tableSection('Contacts', bundle.contacts),
|
||||
tableSection('Addresses', bundle.addresses),
|
||||
tableSection('Tags', bundle.tags as unknown as Record<string, unknown>[]),
|
||||
tableSection('Tags', bundle.tags.map(toJsonRow)),
|
||||
tableSection('Relationships', bundle.relationships),
|
||||
tableSection('Notes', bundle.notes),
|
||||
tableSection('Owned yachts', bundle.ownedYachts),
|
||||
|
||||
Reference in New Issue
Block a user