pruef-agentur kann jetzt sagen, woran sie scheitert

Sie meldete 31-mal "FEHL" und HTTP 503 -- und kein einziges Wort dazu,
warum. Die Serverausgabe hat sie immer schon mitgeschrieben, aber nur
gezeigt, wenn der Server gar nicht erst hochkam. Faellt er spaeter mit
einem Datenbankfehler um, war sie weg.

Dazu kam: Playwright wirft bei einem fehlenden Knopf eine Ausnahme, der
Prozess stirbt, und mit ihm die Zusammenfassung. Genau dann braucht man
sie am dringendsten. Deshalb haengt die Ausgabe jetzt auch an
uncaughtException und unhandledRejection.

Mit SERVERLOG=<datei> kommt das Protokoll vollstaendig heraus -- die
letzten 1800 Zeichen zeigen den Schaden, nicht immer seine Ursache.
Genau daran lag es hier: Der Verlust stand ganz oben, der Fehler ganz
unten.

WAS DAMIT IN EINEM LAUF SICHTBAR WURDE (Befund, noch nicht repariert):

  [workspace] Spalte 'einsatz' in eintraege ergaenzt.
  [workspace] Bereich 'agentur' freigeschaltet, 5 Eintraege
  [workspace] Bereich lesen: no such column: e.einsatz

Die Spalte wird angelegt und unmittelbar danach wieder verworfen. Die
einmalige 'agentur'-Umstellung baut `eintraege` mit einer FEST
EINGETRAGENEN Spaltenliste neu -- 24 Spalten gehen hinein, 21 kommen
heraus. Verloren gehen einsatz, nur_leitung und gesendet_am: die drei,
die nach dem 07.09.2026 dazukamen.

Der Kommentar an genau dieser Stelle warnt woertlich vor dieser
Verlustart -- damals fuer die drei Event-Spalten, die deshalb
nachgetragen wurden. Die Liste war am 07.09. richtig und ist seither
still falsch geworden. Dieselbe feste Zahl, dieselbe Falle, drittes
Mal.

DIE ECHTEN DATEN SIND NICHT BETROFFEN, nachgemessen statt vermutet: Die
Datenbank auf dem Server hat alle drei Spalten, und ihr CHECK kennt
'agentur' bereits -- die Umstellung laeuft dort nie wieder. Gefaehrlich
wird es erst beim Zurueckspielen einer Sicherung von vor dem
06.09.2026: Dann werden die drei Spalten angelegt und sofort samt
Inhalt weggeworfen, ohne Fehler, bei unveraenderter Zeilenzahl.

Die Reparatur waere, die Liste nicht zu schreiben, sondern aus
PRAGMA table_info abzuleiten -- so wie es checkListeErweitern fuer alle
spaeteren Bereiche schon macht. Das ist ein Eingriff in einen
Wanderungsweg auf echten Daten und wartet auf Filipes Wort.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-11 14:36:09 +02:00
co-authored by Claude Opus 5
parent 915a4ae07c
commit c98aa06554
+44 -2
View File
@@ -26,7 +26,7 @@
schlimmere Ausgang.
===================================================================== */
import { mkdtempSync, rmSync, appendFileSync } from "node:fs";
import { mkdtempSync, rmSync, appendFileSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
@@ -61,6 +61,38 @@ const ok = (b, t) => { gemacht++; melde((b ? " ok " : " FEHL ") + t); if (!b
nicht: Ein Modul wird nur einmal geladen.
--------------------------------------------------------------------- */
let laufend = null;
/* DER DRITTE AUSGANG (11.09.2026). Diese Datei schreibt die
Serverausgabe immer schon mit -- und zeigte sie nur, wenn der Server
gar nicht erst hochkam. Faellt er spaeter mit 503 um (die Antwort bei
einem Datenbankfehler), standen hier 31-mal "FEHL" und kein einziges
Wort dazu, WARUM. Eine Pruefung, die nicht sagen kann, woran sie
scheitert, kostet eine Stunde Suchen. */
let letzteAusgabe = () => "";
/* UND SIE MUSS AUCH DANN KOMMEN, WENN DER LAUF ABSTUERZT. Playwright
wirft bei einem fehlenden Knopf eine Ausnahme -- der Prozess stirbt,
und mit ihm die Zusammenfassung samt Diagnose. Genau dann braucht
man sie am dringendsten. */
function serverAusgabeZeigen(warum) {
const t = letzteAusgabe();
console.log("");
console.log(`=== Was der Server dazu gesagt hat (${warum}, letzte 1800 Zeichen) ===`);
console.log(t ? t.slice(-1800) : "(nichts -- der Server hat geschwiegen)");
/* Und vollstaendig auf Zuruf, falls die Ursache weiter oben steht:
SERVERLOG=<datei>. Die letzten 1800 Zeichen zeigen den Schaden,
nicht immer seine Ursache. */
if (t && process.env.SERVERLOG) {
writeFileSync(process.env.SERVERLOG, t);
console.log(`(vollstaendig in ${process.env.SERVERLOG})`);
}
}
for (const art of ["uncaughtException", "unhandledRejection"]) {
process.on(art, (e) => {
console.log(`
!! ${art}: ${e?.message || e}`);
serverAusgabeZeigen(art);
process.exit(1);
});
}
async function serverStarten() {
const kind = spawn(process.execPath, [join(HIER, "index.js")], {
env: {
@@ -80,7 +112,11 @@ async function serverStarten() {
if (kind.exitCode !== null) break;
try {
const a = await fetch(BASIS + "/workspace/", { redirect: "manual" });
if (a.status < 500) { laufend = kind; return { kind, aus: () => aus }; }
if (a.status < 500) {
laufend = kind;
letzteAusgabe = () => aus;
return { kind, aus: () => aus };
}
} catch { /* noch nicht oben */ }
}
throw new Error("Server startete nicht:\n" + aus.slice(-1200));
@@ -709,5 +745,11 @@ await serverStoppen();
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ }
melde(`\n${gemacht} Pruefungen`);
if (fehler) {
const t = letzteAusgabe();
melde("");
melde("=== Was der Server dazu gesagt hat (letzte 1800 Zeichen) ===");
melde(t ? t.slice(-1800) : "(nichts -- der Server hat geschwiegen)");
}
melde(fehler === 0 ? "ALLES IN ORDNUNG" : `${fehler} FEHLER`);
process.exit(fehler ? 1 : 0);