// SPDX-FileCopyrightText: 2026 Paul Schoeneck // // SPDX-License-Identifier: AGPL-3.0-only AND LicenseRef-KIBU-Additional-Terms import "dotenv/config"; import { spawnSync } from "node:child_process"; import { createRequire } from "node:module"; import { fileURLToPath } from "node:url"; import { db } from "../src/lib/db"; import { isDemoDatabase } from "../src/lib/demo"; async function reset() { const [mode, confirmation, ...extra] = process.argv.slice(2); if (!["clear", "demo"].includes(mode) || confirmation !== "--confirm-reset" || extra.length) { throw new Error("Aufruf: npm run db:clear|db:reset -- --confirm-reset. Alle Instanzdaten und Zugaenge werden geloescht!"); } if (!process.env.DATABASE_URL) throw new Error("DATABASE_URL fehlt. Zuerst npm run configure ausfuehren."); const target = new URL(process.env.DATABASE_URL); if (!["postgres:", "postgresql:"].includes(target.protocol) || (!["localhost", "127.0.0.1", "[::1]"].includes(target.hostname) && !isDemoDatabase(target)) || !target.pathname.slice(1) || ["/postgres", "/template0", "/template1"].includes(target.pathname)) { throw new Error("Reset nur fuer eine ausdruecklich konfigurierte lokale Anwendungsdatenbank erlaubt."); } if (mode === "demo" && process.env.BOOTSTRAP_PASSWORD !== undefined && process.env.BOOTSTRAP_PASSWORD.length < 12) { throw new Error("BOOTSTRAP_PASSWORD muss mindestens 12 Zeichen haben. Es wurde nichts geloescht."); } console.log(`ACHTUNG: Alle Instanzdaten einschliesslich Konten, Sitzungen und Audit-Verlauf in ${target.hostname}:${target.port || "5432"}${target.pathname} werden geloescht.`); const client = await db.connect(); try { await client.query("BEGIN"); await client.query("SET LOCAL lock_timeout = '5s'"); await client.query("SELECT pg_advisory_xact_lock(724139002)"); await client.query("ALTER TABLE public.audit_log DISABLE TRIGGER audit_immutable"); await client.query(`TRUNCATE TABLE public.applications, public.admission_rounds, public.application_limits, public.attendance, public.day_overrides, public.weekly_schedules, public.pickup_contacts, public.alone_rules, public.children, public.audit_log, public.sessions, public.login_attempts, public.staff, public.pickup_slots RESTART IDENTITY`); await client.query("ALTER SEQUENCE public.attendance_revision RESTART WITH 1"); await client.query("ALTER TABLE public.audit_log ENABLE TRIGGER audit_immutable"); await client.query(`INSERT INTO public.pickup_slots(starts_at, ends_at) VALUES ('14:00', '14:15'), ('15:00', '15:15'), ('16:00', '16:15'), ('17:00', '17:15')`); await client.query("COMMIT"); console.log("Instanzdaten geloescht. Schema, Migrationen und vier Standardslots sind bereit."); } catch (error) { await client.query("ROLLBACK"); throw error; } finally { client.release(); } if (mode === "clear") { console.log("Keine Konten vorhanden. Neuer Zugang: npm run db:setup. Demodaten: npm run db:seed."); return; } const tsx = createRequire(import.meta.url).resolve("tsx/cli"); for (const script of ["setup.ts", "seed-demo.ts"]) { const result = spawnSync(process.execPath, [tsx, fileURLToPath(new URL(script, import.meta.url))], { cwd: fileURLToPath(new URL("../", import.meta.url)), env: process.env, stdio: "inherit", }); if (result.error) throw result.error; if (result.status !== 0) { throw new Error(`${script} fehlgeschlagen. Daten wurden bereits geleert; db:setup und db:seed nach Fehlerbehebung erneut ausfuehren.`); } } console.log("Demo-Instanz bereit. Mit dem neu ausgegebenen Zugang anmelden; alte Sitzungen sind ungueltig."); } reset().catch((error: unknown) => { console.error("Instanz-Reset fehlgeschlagen:", error); process.exitCode = 1; }).finally(() => db.end());