ThreadCount Community edition

Uniform stock management for healthcare linen rooms. Licensed under the GNU AGPL v3.
This commit is contained in:
ThreadCount
2026-09-13 08:45:19 +10:00
commit 1bc2de655a
505 changed files with 56223 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
/* The console's database clients. Two, and the difference between them is the whole design.
*
* opsDb() connects as `ops_ro`, a Postgres role granted SELECT on control-plane columns only —
* counts, dates, configuration — and nothing on customer content: no wearer's name, no photo, no
* request text, no coordinator's email. The grants are in the ops_ro_grants migration and are
* proven on production by scripts/ops-ro-probe.cjs. The point is that the guard is the database,
* not code review: there is no row-level security in this product and no linter, so a query
* written against the wrong table in some future screen must fail with a permission error rather
* than return data.
*
* revealDb() connects as `ops_reveal`, which may read exactly four columns of one table: a
* facility's id and its three coordinator contacts. It exists so that revealing a contact is not an
* exception carved into code but a second, narrower door with its own key — widening the reveal's
* SELECT fails at the database too. Only lib/ops/reveal.ts may use it.
*
* Both are read-only by construction. The console's own tables — Operator, OperatorEvent,
* RevealGrant — are written through lib/db.ts, because they are the console's records.
*
* Both are lazy: nothing runs at import time, so a box without the variables still starts the
* product; only the console fails, and it fails closed. Small pools — this is a small box, and
* lib/db.ts already holds the main one. */
const g = globalThis as unknown as { __opsDb?: PrismaClient; __revealDb?: PrismaClient };
function make(envVar: "OPS_DATABASE_URL" | "OPS_REVEAL_DATABASE_URL", max: number): PrismaClient {
const url = process.env[envVar];
if (!url) throw new Error(`${envVar} not set`);
return new PrismaClient({ adapter: new PrismaPg({ connectionString: url, max }), log: ["error"] });
}
export function opsDb(): PrismaClient {
if (!g.__opsDb) g.__opsDb = make("OPS_DATABASE_URL", 2);
return g.__opsDb;
}
export function revealDb(): PrismaClient {
if (!g.__revealDb) g.__revealDb = make("OPS_REVEAL_DATABASE_URL", 1);
return g.__revealDb;
}