Een dating-app op basis van brieven, niet van gezichten!
Deze repository wordt als momentopname gepubliceerd: een clone bevat de volledige inhoud, maar geen commitgeschiedenis.
/*
* Voert db/schema.sql en db/seed.sql uit tegen DATABASE_URL.
* Maakt de database aan als die nog niet bestaat.
*
* npm run db:setup # schema + seed
* npm run db:setup -- --no-seed
*
* Twee van de seed-accounts — de bouwer en het account voor de beoordelaar van de app-store — krijgen
* hun échte adres en wachtwoord uit de omgeving (SEED_BRAM_* en SEED_REVIEW_*). Staan die er niet, dan
* blijven ze wat ze in de seed zijn: @voorbeeld.nl met het gewone seed-wachtwoord. Zo staat er geen
* enkel bruikbaar inloggegeven in deze repository, die openbaar is, terwijl een echte installatie toch
* met één opdracht klaarstaat voor een review.
*/
import { readFile, readdir } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import path from "node:path";
import pg from "pg";
const here = path.dirname(fileURLToPath(import.meta.url));
const url = new URL(process.env.DATABASE_URL ?? "postgres://postgres@localhost:5432/liefdesbrieven");
const dbName = url.pathname.slice(1);
const withSeed = !process.argv.includes("--no-seed");
/* 1. Database aanmaken indien nodig (via de standaard 'postgres'-database) */
{
const admin = new pg.Client({ connectionString: new URL("/postgres", url).toString() });
await admin.connect();
const { rowCount } = await admin.query("select 1 from pg_database where datname = $1", [dbName]);
if (rowCount === 0) {
await admin.query(`create database "${dbName.replace(/"/g, '""')}"`);
console.log(`database ${dbName} aangemaakt`);
}
await admin.end();
}
/* 2. Schema en seed uitvoeren */
{
const client = new pg.Client({ connectionString: url.toString() });
await client.connect();
const files = ["schema.sql", ...(withSeed ? ["seed.sql"] : [])];
for (const f of files) {
await client.query(await readFile(path.join(here, f), "utf8"));
console.log(`${f} uitgevoerd`);
}
/*
* Een bestáánde database verandert via db/migrations/ (npm run db:migrate); dit script is er
* alleen nog voor een verse. schema.sql heeft elke migratie al verwerkt, dus een verse opbouw
* registreert ze meteen als uitgevoerd — anders zou db:migrate hierna toevoegen wat er al staat.
*/
let migraties = [];
try {
migraties = (await readdir(path.join(here, "migrations"))).filter((f) => f.endsWith(".sql")).sort();
} catch (err) {
if (err.code !== "ENOENT") throw err;
}
for (const f of migraties) await client.query("insert into schema_migrations (filename) values ($1)", [f]);
if (migraties.length)
console.log(`${migraties.length} migratie(s) als uitgevoerd geregistreerd — schema.sql is al de nieuwste stand`);
/*
* De echte inloggegevens erover, als de omgeving ze aandraagt. Per veld: wie alleen een wachtwoord
* zet houdt het adres uit de seed, en andersom. Zwijgt de omgeving, dan gebeurt er niets — dat is
* het gewone geval op een ontwikkelmachine.
*/
if (withSeed) {
const { hashPassword } = await import("../server/auth.js");
const echte = [
{ naam: "Bram", email: process.env.SEED_BRAM_EMAIL, wachtwoord: process.env.SEED_BRAM_PASSWORD },
{ naam: "Nina", email: process.env.SEED_REVIEW_EMAIL, wachtwoord: process.env.SEED_REVIEW_PASSWORD },
];
for (const p of echte) {
if (!p.email && !p.wachtwoord) continue;
const waarden = [p.naam];
const zetten = [];
if (p.email) {
waarden.push(p.email.trim().toLowerCase());
zetten.push(`email = $${waarden.length}`);
}
if (p.wachtwoord) {
waarden.push(await hashPassword(p.wachtwoord));
zetten.push(`password_hash = $${waarden.length}`);
}
const { rowCount } = await client.query(`update users set ${zetten.join(", ")} where name = $1`, waarden);
/* Het wachtwoord zelf komt hier niet in beeld, en het adres alleen als het gezet is. */
console.log(rowCount
? `${p.naam}: eigen inloggegevens gezet (${zetten.length === 2 ? "adres en wachtwoord" : p.email ? "adres" : "wachtwoord"})`
: `${p.naam}: niet gevonden in de seed — inloggegevens niet gezet`);
}
}
await client.end();
}