Files
dogfather-universe/server-internal/db.js
T
DogFatherGitandClaude Opus 5 01b61905aa Altfaelle aufraeumen: abgebrochene Projekte verschwinden aus der Liste
P-2608-0001 wurde abgebrochen, bevor es das Archiv-Kennzeichen gab. Es
hat deshalb zwar den Status "abgebrochen", aber archiviert = 0 -- und
stand bis heute zwischen den offenen Auftraegen. Also genau das, was der
Wunsch "wenn ich abbreche dan sollen die auch da weg" abstellen sollte.

Ueber die Oberflaeche war das nicht zu reparieren: Die Verwaltung zeigt
bei Status "abgebrochen" den Info-Block statt des Abbrechen-Knopfes, ein
zweiter Klick war also gar nicht moeglich.

Migration statt Handarbeit in der Datenbank: Ein einzelner UPDATE per
Hand repariert einen Fall und hinterlaesst keine Spur. Die Migration
erwischt jeden Altfall, laeuft ueberall gleich (Server, Testdatenbank,
ein spaeterer Neuaufbau) und steht nachvollziehbar in der Geschichte.

Bewusst NICHT gefuellt: abbruch_am, abbruch_grund, abbruch_wer,
abbruch_erstattung_cent. Diese Felder gab es beim damaligen Abbruch noch
nicht. Sie nachtraeglich mit plausiblen Werten zu fuellen waere eine
Behauptung ueber einen Vorgang, bei dem niemand mehr weiss, wie er
wirklich ablief -- und ausgerechnet im Streitfall waere das die
gefaehrlichste Stelle fuer eine Erfindung. Ein leeres Feld sagt ehrlich
"unbekannt", und die Verwaltung zeigt diese Angaben ohnehin nur an, wenn
sie gefuellt sind.

Der Test stellt den Altzustand echt nach: alle Migrationen bis 0017,
dann der Altfall, dann erst 0018. Geprueft wird auch, was NICHT passieren
darf -- ein laufendes Projekt bleibt unberuehrt, ein sauber abgebrochenes
behaelt Datum, Grund und Erstattung, und ein zweiter Durchlauf aendert
nichts mehr (beim Wiederherstellen einer Sicherung passiert genau das).

Geprueft: 167 Pruefungen gruen (Altabbruch 16, Automatik 55, Angebote 56,
Kette 40).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 23:07:37 +02:00

90 lines
4.0 KiB
JavaScript

/* =====================================================================
db.js — SQLite-Datenbank (better-sqlite3), Ersatz für Cloudflare D1.
Führt beim ersten Start alle Migrationen aus ../cloudflare-worker/migrations/
in der ursprünglichen Reihenfolge aus (0001 Basis-Schema, 0002 app_settings,
0003 Supporter-Abo, 0004 TikTok/Discord-Spalten, 0005 Supporter-Abstimmungen)
— anders als bei VanVans Shop ist hier NIE alles in einer konsolidierten
schema.sql zusammengefasst worden, deshalb müssen wir hier tatsächlich alle
der Reihe nach anwenden.
===================================================================== */
import Database from "better-sqlite3";
import { readFileSync, existsSync, mkdirSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const DB_PATH = process.env.DB_PATH || "/var/lib/dogfather-internal/dogfather-internal.db";
const MIGRATIONS_DIR = join(__dirname, "..", "cloudflare-worker", "migrations");
mkdirSync(dirname(DB_PATH), { recursive: true });
export const db = new Database(DB_PATH);
db.pragma("journal_mode = WAL");
db.pragma("foreign_keys = ON");
const MIGRATION_FILES = [
"0001_init.sql",
"0002_app_settings.sql",
"0003_supporter_abo.sql",
"0004_supporter_oauth_providers.sql",
"0005_supporter_polls.sql",
"0006_testimonials.sql",
"0007_team_members.sql",
"0008_team_members_rich.sql",
"0009_supporter_access_code.sql",
"0010_testimonials_avatar.sql",
// Webdesign-Bereich (22.08.2026): Anfragen, Kunden, Projekte, Dateien,
// Nachrichten, Änderungswünsche, pflegbare Inhalte, Änderungsverlauf.
// Alle Tabellen tragen das Präfix "wd_" und sind fachlich vollständig
// vom Universe getrennt — Begründung im Kopf der Migration.
"0011_webdesign.sql",
// Zahlungen im Webdesign-Bereich (22.08.2026): PayPal-Einmalzahlungen
// fuer Anzahlung/Restbetrag/Zusatzangebote, PayPal-Abo fuer die
// monatliche Betreuung, Webhook-Merkliste gegen Doppelverbuchung.
"0012_webdesign_zahlungen.sql",
// Aufgabenliste je Projekt ("was ist schon gemacht von dem, was im Plan
// war") und ein allgemeines Postfach fuer Nachrichten OHNE Projektbezug
// (22.08.2026). Begruendung fuer die getrennte Postfach-Tabelle steht
// im Kopf der Migration.
"0013_webdesign_aufgaben.sql",
// Elektronische Widerrufsfunktion (§ 356a BGB, seit 19.06.2026) und der
// Nachweis der Zustimmung zum vorzeitigen Leistungsbeginn (§ 356 Abs. 4
// BGB). Ausfuehrliche Begruendung im Kopf der Migration -- kurz: Im
// Portal werden Zusatzangebote per Klick verbindlich angenommen, das ist
// ein Vertragsschluss ueber eine Online-Benutzeroberflaeche.
"0014_webdesign_widerruf.sql",
// Annehmen und Ablehnen einer Anfrage: echter Liefertermin (termin_am)
// fuer den Countdown, Zeitpunkt der Annahme, Ablehnungsgrund.
"0015_webdesign_annahme.sql",
// Die Uhr laeuft erst bei Anzahlung; Abbruch mit Erstattung;
// Benachrichtigungen fuer die Verwaltung.
"0016_webdesign_automatik.sql",
// Angebote mit Zusage im Portal.
"0017_webdesign_angebote.sql",
// Altfaelle: Projekte, die abgebrochen wurden, BEVOR es das
// Archiv-Kennzeichen gab, stehen sonst weiter in der laufenden Liste.
"0018_webdesign_altabbrueche_archivieren.sql",
];
function alreadyApplied(name) {
const row = db.prepare(`SELECT 1 FROM _migrations WHERE name = ?`).get(name);
return !!row;
}
export function initDb() {
db.exec(`CREATE TABLE IF NOT EXISTS _migrations (name TEXT PRIMARY KEY, applied_at TEXT NOT NULL)`);
for (const file of MIGRATION_FILES) {
if (alreadyApplied(file)) continue;
const path = join(MIGRATIONS_DIR, file);
if (!existsSync(path)) {
console.warn(`⚠️ Migration ${file} nicht gefunden unter ${path}, übersprungen.`);
continue;
}
const sql = readFileSync(path, "utf-8");
db.exec(sql);
db.prepare(`INSERT INTO _migrations (name, applied_at) VALUES (?, ?)`).run(file, new Date().toISOString());
console.log(`Migration angewendet: ${file}`);
}
}