fiddy/packages/db/test-ui-seed/index.js
2026-02-11 23:45:15 -08:00

111 lines
3.2 KiB
JavaScript

import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import dotenv from "dotenv";
import pg from "pg";
import bcrypt from "bcryptjs";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
dotenv.config({ path: path.resolve(__dirname, "../../../apps/web/.env") });
const { Pool } = pg;
function getDatabaseName(connectionString) {
try {
const url = new URL(connectionString);
const name = url.pathname.replace(/^\/+/, "");
return name ? decodeURIComponent(name) : "";
} catch {
return "";
}
}
function requireAllowedDatabase() {
if (!process.env.DATABASE_URL)
throw new Error("DATABASE_URL is required");
const allowedRaw = process.env.ALLOWED_DB_NAMES;
if (!allowedRaw)
throw new Error("ALLOWED_DB_NAMES is required");
const allowed = allowedRaw
.split(",")
.map(value => value.trim().toLowerCase())
.filter(Boolean);
if (!allowed.length)
throw new Error("ALLOWED_DB_NAMES must include at least one database name");
const dbName = getDatabaseName(process.env.DATABASE_URL);
if (!dbName)
throw new Error("DATABASE_URL must include a database name");
if (!allowed.includes(dbName.toLowerCase()))
throw new Error(`DATABASE_URL must target an allowed database. Found "${dbName}"`);
}
async function main() {
requireAllowedDatabase();
const sqlPath = path.resolve(__dirname, "../../../docs/seed-ui.sql");
let sql = fs.readFileSync(sqlPath, "utf8");
if (!sql.includes("__PASSWORD_HASH__"))
throw new Error("Seed SQL missing __PASSWORD_HASH__ placeholder");
const seedPassword = process.env.SEED_PASSWORD || "FiddyDev123!";
const passwordHash = await bcrypt.hash(seedPassword, 12);
sql = sql.replaceAll("__PASSWORD_HASH__", passwordHash);
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: process.env.DATABASE_SSL === "false" ? false : { rejectUnauthorized: false }
});
const client = await pool.connect();
try {
await client.query("begin");
await client.query(sql);
await client.query("commit");
const tables = [
"users",
"groups",
"group_members",
"group_settings",
"group_join_requests",
"group_invite_links",
"group_audit_log",
"tags",
"entries",
"entry_tags",
"receipts"
];
const counts = {};
for (const table of tables) {
const { rows } = await client.query(`select count(*)::int as count from ${table}`);
counts[table] = rows[0]?.count ?? 0;
}
const { rows: userRows } = await client.query("select email from users order by id");
const { rows: groupRows } = await client.query("select name from groups order by id");
console.log("UI seed complete.");
console.log("Counts:");
for (const [table, count] of Object.entries(counts))
console.log(`- ${table}: ${count}`);
console.log("Users:");
for (const row of userRows)
console.log(`- ${row.email}`);
console.log("Groups:");
for (const row of groupRows)
console.log(`- ${row.name}`);
} catch (error) {
await client.query("rollback");
console.error(error);
process.exitCode = 1;
} finally {
client.release();
await pool.end();
}
}
main();