Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/utils/db/seed/localCredentialsEnvironment.server.ts
T

78 lines
3.1 KiB
TypeScript

import type { DbClient } from "@hivegrid/db/client.server";
export type LocalCredentialsEnvironment = {
ADMIN_APP_HOST?: string;
CI?: string;
DATABASE_URL?: string;
LOCAL_DEV_CREDENTIALS?: string;
NODE_ENV?: string;
RLS_RUNTIME_MODE?: string;
SENTRY_ENVIRONMENT?: string;
};
const LOCAL_DATABASE_HOSTS = new Set(["127.0.0.1", "[::1]", "localhost"]);
const LOCAL_ADMIN_HOST_PATTERN = /^admin\.localhost(?::\d+)?$/i;
const LOCAL_DATABASE_MARKER = "app.local_dev_credentials";
const LOCAL_DATABASE_MARKER_VALUE = "enabled";
/**
* Local credential normalization requires independent process, URL, and
* database-side proof. A loopback SSH tunnel must never be sufficient.
*/
export function assertLocalCredentialsEnvironment(env: LocalCredentialsEnvironment = process.env): URL {
if (env.LOCAL_DEV_CREDENTIALS !== "1") {
throw new Error("Local credential normalization requires the canonical local launcher.");
}
if (env.NODE_ENV !== "development") {
throw new Error("Local credential normalization requires NODE_ENV=development.");
}
if (env.CI?.trim()) {
throw new Error("Local credential normalization is disabled in CI.");
}
if (env.SENTRY_ENVIRONMENT?.trim()) {
throw new Error("Local credential normalization is disabled for deployed Sentry environments.");
}
if (env.RLS_RUNTIME_MODE?.trim().toLowerCase() === "required") {
throw new Error("Local credential normalization is disabled when RLS runtime mode is required.");
}
if (!LOCAL_ADMIN_HOST_PATTERN.test(env.ADMIN_APP_HOST?.trim() ?? "")) {
throw new Error("Local credential normalization requires the admin.localhost host.");
}
let databaseUrl: URL;
try {
databaseUrl = new URL(env.DATABASE_URL ?? "");
} catch {
throw new Error("Local credential normalization requires a valid DATABASE_URL.");
}
// node-postgres accepts query parameters that can override the URI host.
if (databaseUrl.search !== "") {
throw new Error("Local credential normalization does not allow PostgreSQL connection query parameters.");
}
if (!new Set(["postgres:", "postgresql:"]).has(databaseUrl.protocol) || !LOCAL_DATABASE_HOSTS.has(databaseUrl.hostname)) {
throw new Error("Local credential normalization requires a loopback PostgreSQL database.");
}
return databaseUrl;
}
export async function assertLocalCredentialsDatabaseMarker(client: Pick<DbClient, "$queryRaw">): Promise<void> {
const markerRows = await client.$queryRaw<Array<{ marked: boolean }>>`
SELECT EXISTS (
SELECT 1
FROM pg_catalog.pg_db_role_setting AS setting
JOIN pg_catalog.pg_database AS database
ON database.oid = setting.setdatabase
WHERE database.datname = current_database()
AND setting.setrole = 0
AND ${`${LOCAL_DATABASE_MARKER}=${LOCAL_DATABASE_MARKER_VALUE}`} = ANY(setting.setconfig)
) AS marked
`;
if (markerRows[0]?.marked !== true) {
throw new Error(
"Local credential normalization requires a database marked as disposable local development data. Run pnpm dev:services:mark-local only for the repository's local Postgres container."
);
}
}