Files
Ferraz Fagundes 8c22597420
ci / lint, typecheck, testes e build (push) Canceled after 0s
ci / Postgres e Redis reais (push) Canceled after 0s
ci / imagem Docker Linux (push) Canceled after 0s
fix(db): protege conexoes e migrations de homologacao
2026-10-19 16:32:03 -03:00

78 lines
2.8 KiB
TypeScript

import path from "node:path";
import { adapterEnv, identityShimEnabled, isDeployedEnv } from "../src/config/env";
import { exitOnInvalidConfig } from "../src/config/fail-fast";
import { buildPostgresOptions, databaseUrlFor, scrubDbError, type DbPurpose } from "../src/infra/db/connection-policy";
import { createFileDb } from "../src/infra/db/pglite";
import { createPostgresDb } from "../src/infra/db/postgres";
import { assertDatabaseGuard } from "../src/infra/db/target-guard";
import type { Db } from "../src/infra/db/types";
/**
* Banco para os scripts operacionais. Em staging e produção `DATABASE_URL` é
* obrigatória: cair em PGlite com um segredo ausente faria uma migração
* silenciosa num arquivo local parecer sucesso.
*/
export async function openOperationalDb(purpose: DbPurpose): Promise<Db> {
const databaseUrl = databaseUrlFor(purpose);
if (databaseUrl) {
const db = createPostgresDb(databaseUrl, buildPostgresOptions(databaseUrl, purpose));
try {
await assertDatabaseGuard(db, purpose, databaseUrl);
return db;
} catch (error) {
await db.close().catch(() => undefined);
throw error;
}
}
if (isDeployedEnv()) {
throw new Error("DATABASE_URL é obrigatória fora do PGlite de development.");
}
const dataDir = path.join(process.cwd(), ".data", "pglite");
const { mkdir } = await import("node:fs/promises");
await mkdir(dataDir, { recursive: true });
return createFileDb(dataDir);
}
/** Recusa configuração inválida antes de tocar em qualquer recurso externo. */
export function bootstrapScript(name: string): void {
exitOnInvalidConfig(process.env);
console.log(`[${name}] ambiente=${adapterEnv()} shim_identidade=${identityShimEnabled() ? "sim" : "nao"}`);
}
export function describeTarget(): string {
return process.env.DATABASE_URL ? "postgres" : "pglite local";
}
/**
* Executa o corpo do script.
*
* Existe para que nenhum script use top-level await: `tsx` compila `.ts` como
* CommonJS quando o `package.json` não declara `"type": "module"`, e aí top-level
* await é erro de transformação. Isso impedia rodar os scripts com
* `node --import tsx`, que é a forma que entrega SIGTERM ao nosso processo — o
* CLI do `tsx` cria um processo filho e o sinal não chega ao handler.
*/
export function runScript(name: string, body: () => Promise<void>): void {
body().then(
() => {
process.exitCode ??= 0;
},
(error: unknown) => {
console.error(`[${name}] ${scrubDbError(error)}`);
process.exit(1);
},
);
}
/** Abre o banco, executa e fecha, mesmo em caso de falha. */
export function runWithDb(name: string, purpose: DbPurpose, body: (db: Db) => Promise<void>): void {
runScript(name, async () => {
const db = await openOperationalDb(purpose);
try {
await body(db);
} finally {
await db.close().catch(() => undefined);
}
});
}