feat: harden authenticated deployment
This commit is contained in:
+44
-13
@@ -4,21 +4,52 @@ import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
export function createDb(connectionString = process.env.DATABASE_URL) {
|
||||
if (!connectionString) throw new Error("DATABASE_URL is required");
|
||||
const pool = new pg.Pool({ connectionString, max: 10, ssl: process.env.DATABASE_SSL === "true" ? { rejectUnauthorized: true } : undefined });
|
||||
return { query: (...args) => pool.query(...args), close: () => pool.end() };
|
||||
if (!connectionString) throw new Error("DATABASE_URL is required");
|
||||
const pool = new pg.Pool({
|
||||
connectionString,
|
||||
max: 10,
|
||||
ssl:
|
||||
process.env.DATABASE_SSL === "true"
|
||||
? { rejectUnauthorized: true }
|
||||
: undefined,
|
||||
});
|
||||
return { query: (...args) => pool.query(...args), close: () => pool.end() };
|
||||
}
|
||||
|
||||
/** Apply each versioned SQL file once; failed migrations are not recorded. */
|
||||
export async function migrate(db) {
|
||||
await db.query("CREATE TABLE IF NOT EXISTS schema_migrations (filename text PRIMARY KEY, applied_at timestamptz NOT NULL DEFAULT now())");
|
||||
const directory = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "db", "migrations");
|
||||
const files = (await fs.readdir(directory)).filter((file) => file.endsWith(".sql")).sort();
|
||||
for (const filename of files) {
|
||||
if ((await db.query("SELECT 1 FROM schema_migrations WHERE filename=$1", [filename])).rowCount) continue;
|
||||
const sql = await fs.readFile(path.join(directory, filename), "utf8");
|
||||
await db.query("BEGIN");
|
||||
try { await db.query(sql); await db.query("INSERT INTO schema_migrations(filename) VALUES($1)", [filename]); await db.query("COMMIT"); }
|
||||
catch (error) { await db.query("ROLLBACK"); throw error; }
|
||||
}
|
||||
await db.query(
|
||||
"CREATE TABLE IF NOT EXISTS schema_migrations (filename text PRIMARY KEY, applied_at timestamptz NOT NULL DEFAULT now())",
|
||||
);
|
||||
const directory = path.join(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"..",
|
||||
"db",
|
||||
"migrations",
|
||||
);
|
||||
const files = (await fs.readdir(directory))
|
||||
.filter((file) => file.endsWith(".sql"))
|
||||
.sort();
|
||||
for (const filename of files) {
|
||||
if (
|
||||
(
|
||||
await db.query("SELECT 1 FROM schema_migrations WHERE filename=$1", [
|
||||
filename,
|
||||
])
|
||||
).rowCount
|
||||
)
|
||||
continue;
|
||||
const sql = await fs.readFile(path.join(directory, filename), "utf8");
|
||||
await db.query("BEGIN");
|
||||
try {
|
||||
await db.query(sql);
|
||||
await db.query("INSERT INTO schema_migrations(filename) VALUES($1)", [
|
||||
filename,
|
||||
]);
|
||||
await db.query("COMMIT");
|
||||
} catch (error) {
|
||||
await db.query("ROLLBACK");
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user