Die Agentur-Umstellung schreibt den Bauplan nicht mehr ab
Sie baute `eintraege` mit einer von Hand abgeschriebenen Spaltenliste neu. 24 Spalten gingen hinein, 21 kamen heraus: einsatz, nur_leitung und gesendet_am wurden vom Spalten-Nachtrag angelegt und unmittelbar danach weggeworfen -- mit Inhalt, ohne Fehlermeldung, bei unveraenderter Zeilenzahl. DAS WAR DAS ZWEITE MAL. Am 07.09.2026 fehlten an derselben Stelle die drei Event-Spalten; sie wurden nachgetragen, und daneben kam ein Kommentar, der woertlich vor genau dieser Verlustart warnt. Seither kamen drei neue Spalten dazu, und die Liste wurde still falsch. An einer anderen Stelle im selben Modul steht sogar schon der Satz: "Eine vierte Abschrift waere die vierte Gelegenheit dazu." Jetzt uebernimmt checkListeErweitern -- dieselbe Funktion, die alle spaeteren Bereiche umstellt. Sie holt den Bauplan aus sqlite_master und die Spalten aus PRAGMA table_info: eine Liste, die nicht gepflegt wird, kann nicht veralten. Sichern, Zeilen innerhalb der Transaktion zaehlen, Indizes mitnehmen, Verweise pruefen -- alles, was der Block auch tat. 90 Zeilen weniger. NACHGEMESSEN, in dieser Reihenfolge: - pruef-agentur: 31 Fehler + Absturz -> 62 Pruefungen, alle gruen. - Die Umstellung meldet jetzt 24 Spalten statt 21. - Der echte Fall ist durchgespielt: Auf einer Datenbank, die 'agentur' schon kennt, laeuft sie GAR NICHT mehr an. Neue Pruefung dazu, die die Sicherungsdateien zaehlt (genau eine, trotz mehrerer Neustarts) -- gemessen an der Spur, die ein Umbau hinterlaesst, nicht an der Absicht. - Die Datenbank auf dem Server hat alle drei Spalten und kennt 'agentur' bereits. Sie war nie in Gefahr; gefaehrlich war das Zurueckspielen einer Sicherung von vor dem 06.09.2026. ZWEI ALTE PRUEFFEHLER LAGEN DAHINTER, beide bisher von einem Absturz verdeckt: - "der Wochenbericht kennt 6 Bereiche" -- eine feste Zahl, inzwischen sind es elf. Verglichen wird jetzt gegen die CHECK-Regel der Datenbank; damit stimmt sie auch beim zwoelften Bereich. - Die Meldung wurde am Satzbau erkannt statt an der Aussage. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -26,7 +26,7 @@
|
||||
schlimmere Ausgang.
|
||||
===================================================================== */
|
||||
|
||||
import { mkdtempSync, rmSync, appendFileSync, writeFileSync } from "node:fs";
|
||||
import { mkdtempSync, rmSync, appendFileSync, writeFileSync, readdirSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
@@ -259,7 +259,13 @@ let ausgabe = "";
|
||||
ausgabe = aus();
|
||||
}
|
||||
|
||||
ok(/Bereich 'agentur' freigeschaltet/.test(ausgabe),
|
||||
/* NICHT AUF DEN WORTLAUT FESTNAGELN (11.09.2026). Hier stand
|
||||
/Bereich 'agentur' freigeschaltet/ -- der Satz des alten, von Hand
|
||||
abgeschriebenen Blocks. Seit die Umstellung ueber
|
||||
checkListeErweitern laeuft, heisst es "'agentur' in
|
||||
eintraege.bereich freigeschaltet". Gemeint ist beides Mal dasselbe;
|
||||
geprueft wird deshalb die AUSSAGE, nicht der Satzbau. */
|
||||
ok(/agentur.*freigeschaltet/.test(ausgabe),
|
||||
"die Anwendung meldet die Umstellung"
|
||||
+ (/agentur/.test(ausgabe) ? "" : ` — Ausgabe: ${ausgabe.slice(-300)}`));
|
||||
|
||||
@@ -585,8 +591,37 @@ melde("\n=== Ueberall, wo Bereiche vorkommen ===");
|
||||
vorgekommen, ohne Fehler und ohne Luecke. */
|
||||
const { daten } = await ruf("GET", "/workspace/api/report", keksDogi);
|
||||
const b = daten?.bereiche || {};
|
||||
ok(Object.keys(b).length === 6,
|
||||
`der Wochenbericht kennt ${Object.keys(b).length} Bereiche: ${Object.keys(b).join(", ")}`);
|
||||
/* ABGELEITET STATT GEZAEHLT (11.09.2026). Hier stand `=== 6`. Das
|
||||
war richtig, als es sechs Bereiche gab; inzwischen sind es elf,
|
||||
und die Zeile war seither still falsch -- versteckt hinter einem
|
||||
Absturz weiter oben, der den Lauf vorher beendete. Dieselbe feste
|
||||
Zahl, dieselbe Falle wie in der Umstellung, die diese Datei
|
||||
aufgedeckt hat.
|
||||
|
||||
Verglichen wird jetzt gegen die Bereiche, die die DATENBANK
|
||||
ueberhaupt zulaesst -- die CHECK-Regel ist die einzige Liste, an
|
||||
der niemand vorbeikommt. Damit stimmt die Pruefung auch beim
|
||||
zwoelften Bereich noch, ohne dass jemand sie anfasst. */
|
||||
const erlaubt = (() => {
|
||||
const dd = new DatabaseSync(DB, { readOnly: true });
|
||||
const plan = dd.prepare(
|
||||
"SELECT sql FROM sqlite_master WHERE type='table' AND name='eintraege'").get()?.sql || "";
|
||||
dd.close();
|
||||
/* Ohne regulaeren Ausdruck: Der Bauplan enthaelt die Regel
|
||||
woertlich als "bereich IN (...)". Ein Muster mit Klammern und
|
||||
Leerzeichenklassen waere hier nur eine weitere Stelle, an der
|
||||
man sich vertun kann. */
|
||||
const auf = plan.indexOf("bereich IN (");
|
||||
if (auf < 0) return [];
|
||||
const zu = plan.indexOf(")", auf);
|
||||
if (zu < 0) return [];
|
||||
return plan.slice(auf + "bereich IN (".length, zu)
|
||||
.split(",").map((t) => t.trim().split("'").join(""));
|
||||
})();
|
||||
const fehlend = erlaubt.filter((n) => !(n in b));
|
||||
ok(erlaubt.length > 0 && fehlend.length === 0,
|
||||
`der Wochenbericht kennt alle ${erlaubt.length} Bereiche der Datenbank`
|
||||
+ (fehlend.length ? ` -- es fehlen: ${fehlend.join(", ")}` : ""));
|
||||
ok(b.agentur && b.agentur.neu >= ARTEN.length,
|
||||
`und zaehlt ${b.agentur?.neu} neue Agentur-Eintraege`);
|
||||
}
|
||||
@@ -741,6 +776,28 @@ melde("\n=== Die Kachel ===");
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
6. UND SIE LAEUFT GENAU EINMAL (11.09.2026)
|
||||
=======================================================================
|
||||
Der Fall, der auf dem echten Server gilt: Dort kennt die CHECK-Regel
|
||||
'agentur' laengst. Ein Neustart darf die Tabelle dann NICHT noch
|
||||
einmal umbauen -- jeder Umbau ist ein Kopieren von Hand auf echten
|
||||
Daten, und was man nicht tun muss, tut man auf einer Live-Datenbank
|
||||
nicht.
|
||||
|
||||
GEMESSEN AN DEN SICHERUNGEN, nicht an der Absicht: Vor jedem Umbau
|
||||
legt die Anwendung eine Datei ".vor-agentur-<stempel>" an. Diese
|
||||
Datei ist der Beweis, dass umgebaut wurde. Waeren es zwei, haette
|
||||
ein Neustart erneut angefasst -- und genau das soll hier auffallen.
|
||||
Der Server ist zwischendurch mehrfach neu gestartet worden, der Fall
|
||||
ist also wirklich durchgespielt und nicht nur behauptet. */
|
||||
{
|
||||
const spuren = readdirSync(ordner).filter((n) => n.includes(".vor-agentur-"));
|
||||
ok(spuren.length === 1,
|
||||
`die Agentur-Umstellung lief genau EINMAL, trotz mehrerer Neustarts `
|
||||
+ `(${spuren.length} Sicherung(en))`);
|
||||
}
|
||||
|
||||
await serverStoppen();
|
||||
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ }
|
||||
|
||||
|
||||
+32
-88
@@ -1826,96 +1826,40 @@ function umstellungen(d) {
|
||||
Derselbe Umbau wie bei "abgebrochen" darueber -- ein CHECK laesst
|
||||
sich in SQLite nicht aendern, die Tabelle muss neu gebaut werden:
|
||||
erst sichern, dann tauschen, Zeilen zaehlen, Verweise pruefen. */
|
||||
const eintraegePlan = d.prepare(
|
||||
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'eintraege'").get()?.sql || "";
|
||||
if (eintraegePlan && !eintraegePlan.includes("'agentur'")) {
|
||||
const sicherung = `${DB_PFAD}.vor-agentur-${jetztStempel}`;
|
||||
try {
|
||||
d.exec(`VACUUM INTO '${sicherung.replace(/'/g, "''")}'`);
|
||||
console.log("[workspace] Sicherung vor der Umstellung:", sicherung);
|
||||
} catch (fehler) {
|
||||
console.error("[workspace] Sicherung fehlgeschlagen, Umstellung abgebrochen:", fehler?.message);
|
||||
return;
|
||||
}
|
||||
/* ---- Der sechste Bereich: "agentur" (06.09.2026) ----
|
||||
ABGELEITET STATT ABGESCHRIEBEN (11.09.2026).
|
||||
|
||||
d.exec("PRAGMA foreign_keys = OFF");
|
||||
try {
|
||||
const vorher = d.prepare("SELECT COUNT(*) AS n FROM eintraege").get().n;
|
||||
d.exec("BEGIN");
|
||||
d.exec(`
|
||||
CREATE TABLE eintraege_neu (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
bereich TEXT NOT NULL
|
||||
CHECK (bereich IN ('live','content','technik','community','schutz','agentur')),
|
||||
art TEXT NOT NULL,
|
||||
titel TEXT NOT NULL,
|
||||
text TEXT,
|
||||
datum TEXT NOT NULL,
|
||||
bewertung INTEGER CHECK (bewertung IS NULL OR (bewertung BETWEEN 1 AND 5)),
|
||||
dringlichkeit TEXT NOT NULL DEFAULT 'mittel'
|
||||
CHECK (dringlichkeit IN ('hoch','mittel','niedrig')),
|
||||
status TEXT NOT NULL DEFAULT 'offen'
|
||||
CHECK (status IN ('offen','erledigt')),
|
||||
creator_id INTEGER REFERENCES personen(id) ON DELETE SET NULL,
|
||||
erstellt TEXT NOT NULL,
|
||||
erstellt_von INTEGER REFERENCES personen(id) ON DELETE SET NULL,
|
||||
geaendert TEXT,
|
||||
hook TEXT,
|
||||
format TEXT,
|
||||
saeule_id INTEGER,
|
||||
geplant TEXT,
|
||||
creator_extern TEXT,
|
||||
/* Die drei Event-Spalten MUESSEN hier mit stehen (07.09.2026).
|
||||
Hier stand bis heute der Bauplan der Tabelle von Hand abgeschrieben
|
||||
-- alle Spalten, zweimal (einmal fuer CREATE, einmal fuer INSERT).
|
||||
Am 07.09.2026 fiel auf, dass dabei drei Event-Spalten fehlten; sie
|
||||
wurden nachgetragen, und daneben kam ein Kommentar, der genau vor
|
||||
dieser Verlustart warnt.
|
||||
|
||||
Der Spalten-Nachtrag weiter oben laeuft frueher als dieser
|
||||
Neubau. Auf einer Datenbank, die noch den alten CHECK hat
|
||||
-- eine Sicherung von vor dem 06.09., in einen heutigen
|
||||
Stand eingespielt --, waeren die drei Spalten also erst
|
||||
angelegt und hier sofort wieder weggeworfen worden: mit
|
||||
allem, was drinsteht, ohne Fehlermeldung, und die
|
||||
Zeilenzahl haette weiterhin gestimmt. Genau die
|
||||
Verlustart, vor der der Kommentar zu dieser Umstellung
|
||||
warnt. */
|
||||
event_ende TEXT,
|
||||
event_aufgaben TEXT,
|
||||
event_regeln TEXT
|
||||
);
|
||||
INSERT INTO eintraege_neu
|
||||
(id, bereich, art, titel, text, datum, bewertung, dringlichkeit, status,
|
||||
creator_id, erstellt, erstellt_von, geaendert,
|
||||
hook, format, saeule_id, geplant, creator_extern,
|
||||
event_ende, event_aufgaben, event_regeln)
|
||||
SELECT id, bereich, art, titel, text, datum, bewertung, dringlichkeit, status,
|
||||
creator_id, erstellt, erstellt_von, geaendert,
|
||||
hook, format, saeule_id, geplant, creator_extern,
|
||||
event_ende, event_aufgaben, event_regeln
|
||||
FROM eintraege;
|
||||
DROP TABLE eintraege;
|
||||
ALTER TABLE eintraege_neu RENAME TO eintraege;
|
||||
CREATE INDEX IF NOT EXISTS idx_eintraege_bereich ON eintraege (bereich, creator_id);
|
||||
`);
|
||||
const nachher = d.prepare("SELECT COUNT(*) AS n FROM eintraege").get().n;
|
||||
if (nachher !== vorher) {
|
||||
d.exec("ROLLBACK");
|
||||
console.error(`[workspace] Umstellung abgebrochen: ${vorher} Eintraege vorher, `
|
||||
+ `${nachher} nachher. Sicherung: ${sicherung}`);
|
||||
} else {
|
||||
d.exec("COMMIT");
|
||||
const kaputt = d.prepare("PRAGMA foreign_key_check").all();
|
||||
if (kaputt.length) {
|
||||
console.error("[workspace] ACHTUNG: nach der Umstellung", kaputt.length,
|
||||
"verwaiste Verweise. Sicherung liegt unter", sicherung);
|
||||
} else {
|
||||
console.log(`[workspace] Bereich 'agentur' freigeschaltet, ${vorher} Eintraege, Verweise geprueft.`);
|
||||
}
|
||||
}
|
||||
} catch (fehler) {
|
||||
try { d.exec("ROLLBACK"); } catch { /* schon zurueckgerollt */ }
|
||||
console.error("[workspace] Umstellung 'agentur' fehlgeschlagen:", fehler?.message);
|
||||
} finally {
|
||||
d.exec("PRAGMA foreign_keys = ON");
|
||||
}
|
||||
}
|
||||
DIESELBE FALLE HAT DANACH EIN ZWEITES MAL ZUGESCHNAPPT. Seit dem
|
||||
07.09. kamen `einsatz`, `nur_leitung` und `gesendet_am` dazu. Der
|
||||
Spalten-Nachtrag legt sie an, dieser Neubau warf sie unmittelbar
|
||||
danach weg -- mit Inhalt, ohne Fehlermeldung, bei unveraenderter
|
||||
Zeilenzahl. Gemessen: 24 Spalten hinein, 21 heraus.
|
||||
|
||||
Gefunden hat es pruef-agentur, und zwar seit Tagen: 31-mal "FEHL"
|
||||
mit HTTP 503. Sagen konnte sie es erst, seit sie die
|
||||
Serverausgabe zeigt -- dort stand es dann in drei Zeilen
|
||||
untereinander.
|
||||
|
||||
DESHALB WIRD NICHTS MEHR ABGESCHRIEBEN. `checkListeErweitern` holt
|
||||
den Bauplan aus sqlite_master und die Spaltenliste aus
|
||||
PRAGMA table_info -- sie kann gar nicht veralten, weil sie nicht
|
||||
gepflegt wird. Sie sichert vorher, zaehlt die Zeilen innerhalb der
|
||||
Transaktion, nimmt die Indizes mit und prueft die Verweise, also
|
||||
alles, was der Block hier auch tat.
|
||||
|
||||
Die echten Daten auf dem Server sind nicht betroffen: Ihr CHECK
|
||||
kennt 'agentur' laengst, die Umstellung laeuft dort nie wieder.
|
||||
Gefaehrlich war es beim Zurueckspielen einer Sicherung von vor dem
|
||||
06.09.2026 -- also genau dann, wenn man sich am wenigsten einen
|
||||
stillen Datenverlust leisten kann. */
|
||||
checkListeErweitern(d, "eintraege", "bereich", "agentur",
|
||||
["live", "content", "technik", "community", "schutz", "agentur"], jetztStempel);
|
||||
|
||||
/* ---- Rolle "manager" erlauben ---- */
|
||||
const bauplan = d.prepare(
|
||||
|
||||
Reference in New Issue
Block a user