feat(planning): ship holiday-aware planning and assistant upgrades
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import { URL } from "node:url";
|
||||
|
||||
interface DestructiveGuardOptions {
|
||||
commandName: string;
|
||||
allowedDatabaseNames?: string[];
|
||||
requireConfirmation?: boolean;
|
||||
}
|
||||
|
||||
const PROTECTED_DATABASE_NAMES = new Set(["capakraken", "planarchy"]);
|
||||
|
||||
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),
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user