Files
CapaKraken/packages/db/src/destructive-db-guard.ts
T

71 lines
2.2 KiB
TypeScript

import { URL } from "node:url";
interface DestructiveGuardOptions {
commandName: string;
allowedDatabaseNames?: string[];
requireConfirmation?: boolean;
}
const PROTECTED_DATABASE_NAMES = new Set(["capakraken"]);
export function parseDatabaseUrl(rawUrl: string) {
const parsed = new URL(rawUrl);
const databaseName = parsed.pathname.replace(/^\/+/, "");
return {
protocol: parsed.protocol,
hostname: parsed.hostname,
port: parsed.port,
databaseName,
username: decodeURIComponent(parsed.username),
};
}
export function formatTarget(target: ReturnType<typeof parseDatabaseUrl>) {
const port = target.port ? `:${target.port}` : "";
return `${target.protocol}//${target.username}@${target.hostname}${port}/${target.databaseName}`;
}
export function assertDestructiveDbAllowed({
commandName,
allowedDatabaseNames = [],
requireConfirmation = true,
}: DestructiveGuardOptions) {
const rawUrl = process.env.DATABASE_URL;
if (!rawUrl) {
throw new Error(`${commandName} aborted: DATABASE_URL is not configured.`);
}
const target = parseDatabaseUrl(rawUrl);
const allowFlag = process.env.ALLOW_DESTRUCTIVE_DB_TOOLS === "true";
const confirmationDb = process.env.CONFIRM_DESTRUCTIVE_DB_NAME;
const allowlisted = allowedDatabaseNames.includes(target.databaseName);
if (PROTECTED_DATABASE_NAMES.has(target.databaseName)) {
throw new Error(
`${commandName} aborted: database '${target.databaseName}' is explicitly protected from destructive tooling. Target=${formatTarget(target)}`,
);
}
if (!allowlisted) {
throw new Error(
`${commandName} aborted: database '${target.databaseName}' is not in the destructive-tool allowlist (${allowedDatabaseNames.join(", ") || "none"}). Target=${formatTarget(target)}`,
);
}
if (!allowFlag) {
throw new Error(
`${commandName} aborted: set ALLOW_DESTRUCTIVE_DB_TOOLS=true to allow destructive database operations. Target=${formatTarget(target)}`,
);
}
if (requireConfirmation && confirmationDb !== target.databaseName) {
throw new Error(
`${commandName} aborted: set CONFIRM_DESTRUCTIVE_DB_NAME=${target.databaseName} to confirm the destructive target. Current value=${confirmationDb ?? "<unset>"}`,
);
}
return target;
}