DER BEFUND
Im naechtlichen Lauf standen dreizehn Pruefungen mit derselben Zeile
rot:
[uncaughtException] Error: spawnSync openssl ENOENT
Kein Programmfehler. Die Pruefungen brauchen einen https-Vorbau (der
Sitzungskeks des Hauses ist `secure` und kommt ueber http nicht an),
und dafuer erzeugen sie ein Wegwerf-Zertifikat mit openssl.
GEMESSEN, NICHT VERMUTET
openssl liegt hier: C:\Program Files\Git\mingw64\bin\openssl.exe
C:\Program Files\Git\usr\bin\openssl.exe
im System-PATH steht: C:\Program Files\Git\cmd (nur git.exe)
im Benutzer-PATH: nichts mit Git
Wer aus Git Bash startet, erbt die mingw-Pfade und merkt nie etwas.
Die Aufgabenplanung startet `node.exe` DIREKT, ohne Shell -- und
bekommt sie nicht. Bei mir gruen, nachts rot, und der Grund steht
nicht im Code.
Das Schlimmste daran ist nicht der Ausfall: Dreizehn dauerhaft rote
Zeilen in einer Notiz, die Filipe morgens liest, gewoehnen einem das
Hinsehen ab -- und decken dabei die echten Befunde zu.
DIE REPARATUR
`server/helfer-openssl.mjs` sucht openssl erst im PATH (ohne `where`
oder `which` -- beides sind selbst Programme und koennen genauso
fehlen), danach an den Orten, an denen es auf einem Windows-Rechner
mit Git wirklich liegt.
Dazu `zertifikatBauen(schluessel, zertifikat, host)`. Die acht
Aufrufformen im Haus waren identisch bis auf die Variablennamen
(schl/zert, schluesselDatei/zertDatei, zKey/zCrt, sk/zt, key/crt) --
eine Stelle traegt alle neunundzwanzig. Zwei Tage zuvor war eine
davon um ein `-addext` aermer als die anderen; gemerkt hat es
niemand, weil der Browser den fehlenden Alternativnamen erst bei
einer Weiterleitung anmahnt. Eine Stelle kann nicht von sich selbst
abweichen.
KEIN EINTRAG IN DEN SYSTEM-PATH. `mingw64\bin` enthaelt rund hundert
Programme mit Unix-Namen (`find`, `sort`, `link`), die gleichnamige
Windows-Befehle verdecken. Das fuer eine Pruefung zu aendern haette
an ganz anderen Stellen Fehler verursacht, die niemand hierher
zurueckverfolgt.
GEGENPROBE IN BEIDE RICHTUNGEN, mit dem PATH des Nachtlaufs:
vorher (direkter Aufruf): ABSTURZ: spawnSync openssl ENOENT
nachher (ueber den Helfer): Zertifikat gebaut, 1236 Bytes
Und eine echte Pruefung unter denselben Bedingungen:
PATH ohne mingw64 -> pruef-chat-neu-stelle
63 Pruefungen, 0 Fehler, 0 Abstuerze
Genau diese Datei stand im Nachtlauf mit ENOENT rot.
EINE WACHE DAGEGEN
pruef-struktur prueft ab jetzt, dass niemand openssl wieder direkt
ruft -- der naechste merkt es dort und nicht erst in einem
Nachtlauf, den niemand liest. Drei Gegenproben.
Beim ersten Anlauf schlug sie auf ihre EIGENEN Probetexte an: Sie
liest alle Serverdateien, und dazu gehoert sie selbst. Die Texte
werden jetzt zusammengesetzt. Derselbe Selbsttreffer ist mir heute
schon zweimal passiert.
DER DRITTE AUSGANG BLEIBT, WO ER HINGEHOERT
`helfer-kachel-echtfarbe.mjs` faengt den Fehler weiterhin ab und
meldet `moeglich: false` mit Grund, statt abzubrechen. Eine
Sicherung abzuschaffen, weil ihr Anlass gerade behoben ist, ist der
Anfang des naechsten stillen Fehlschlags.
NEBENBEI: pruef-community-sicht
Sie war rot mit "ZU VIEL: reaktion.html" -- mein eigener Rueckstand
vom 28.09. Die Reaction steht als Kachel im Community-Bereich; die
Liste war nicht nachgezogen. Jetzt 10 / 0.
Die Liste bleibt bewusst von Hand gepflegt: Sie ist eine ABSICHT,
keine Ableitung. Aus rechte.js gelesen verglichen sich zwei Kopien
derselben Quelle -- immer gruen, nie ein Beweis.
GEPRUEFT
pruef-struktur ALLES IN ORDNUNG (mit der neuen Wache)
pruef-community-sicht 10 / 0
pruef-chat-neu-stelle 63 / 0 (mit dem PATH des Nachtlaufs)
pruef-notizen, -teamlage-karten, -werdegang: EXIT 0, keine Abstuerze
mess-quer EXIT 0
29 Dateien: node --check auf allen, kein direkter Aufruf mehr
346 lines
15 KiB
JavaScript
346 lines
15 KiB
JavaScript
/* =====================================================================
|
|
WAS EIN MITGLIED DER COMMUNITY SIEHT -- SYSTEMATISCH (18.09.2026)
|
|
|
|
Filipe: „perfektioniere jede einzelne kategorie der community."
|
|
|
|
---------------------------------------------------------------------
|
|
WARUM ES DIESE PRÜFUNG GIBT
|
|
|
|
An einem Tag habe ich in der Community elf Dinge gefunden, die dort
|
|
nicht hingehörten: Aufgabenzähler, „gönn dir was", „0 betreut",
|
|
sieben Links auf eine Seite, die ein Mitglied gar nicht öffnen darf.
|
|
Jeder einzelne Fund kam daher, dass ich zufällig hingesehen habe.
|
|
|
|
Das ist keine Methode. Der Workspace ist für die Agentur gebaut
|
|
worden, und die Community hat ihn geerbt -- überall dort, wo
|
|
niemand hingesehen hat, steht noch die Sprache und die Logik eines
|
|
Arbeitsplatzes. Solche Reste findet man nicht durch Nachdenken,
|
|
sondern indem man ALLE Seiten durchgeht, die ein Mitglied erreichen
|
|
kann.
|
|
|
|
---------------------------------------------------------------------
|
|
ZWEI FRAGEN, BEIDE MESSBAR
|
|
|
|
1. TOTE WEGE: Führt irgendein Link, Knopf oder Formular auf eine
|
|
Seite, die diese Person nicht öffnen darf? Sie landet dann wieder
|
|
auf der Startseite -- ohne Meldung, ohne Grund. Man drückt, es
|
|
passiert nichts, und beim nächsten Knopf zweifelt man an sich.
|
|
|
|
2. FREMDE SPRACHE: Stehen dort Wörter, die es hier nicht gibt --
|
|
„Review", „betreut", „Pipeline", „Creator anlegen"? Sie sind
|
|
nicht falsch, sie gehören nur jemand anderem.
|
|
|
|
Aufrufen mit: node server/pruef-community-sicht.mjs
|
|
===================================================================== */
|
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { request as httpAnfrage } from "node:http";
|
|
import { eigenerPort } from "./helfer-port.mjs";
|
|
|
|
const PORT = await eigenerPort(import.meta, "pruef-community-sicht");
|
|
const ordner = mkdtempSync(join(tmpdir(), "ws-cs-"));
|
|
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
|
process.env.PORT = String(PORT);
|
|
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
|
process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00";
|
|
|
|
const express = (await import("express")).default;
|
|
const ec = express.response.cookie;
|
|
express.response.cookie = function (n, w, o) { return ec.call(this, n, w, { ...(o || {}), secure: false }); };
|
|
import { notbremse } from "./helfer-notbremse.mjs";
|
|
await import("./index.js");
|
|
notbremse(180_000, "pruef-community-sicht");
|
|
await new Promise((r) => setTimeout(r, 700));
|
|
|
|
process.on("uncaughtException", (f) => { console.error("ABSTURZ:", f); process.exit(7); });
|
|
|
|
let fehler = 0, geprueft = 0;
|
|
const melde = (t) => console.log(t);
|
|
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
|
|
|
const CREW = "crew.dogfather-universe.com";
|
|
|
|
/* ---------------------------------------------------------------------
|
|
WELCHE SEITEN DARF EIN MITGLIED? -- aus der Rechtetabelle abgeleitet.
|
|
|
|
NICHT VON HAND AUFGEZÄHLT. Eine Liste hier müsste bei jeder neuen
|
|
Seite nachgezogen werden, und genau das vergisst man. Die Tabelle
|
|
in rechte.js ist ohnehin die Wahrheit -- also wird sie gefragt.
|
|
--------------------------------------------------------------------- */
|
|
const { darfSeite } = await import("./rechte.js");
|
|
const { readFileSync, readdirSync } = await import("node:fs");
|
|
const alleSeiten = readdirSync(join(import.meta.dirname, "..", "workspace"))
|
|
.filter((d) => d.endsWith(".html"));
|
|
|
|
const GAST = { rolle: "gast", haus: "crew" };
|
|
const erlaubt = alleSeiten.filter((d) => darfSeite(GAST, `/workspace/${d}`));
|
|
const verboten = alleSeiten.filter((d) => !darfSeite(GAST, `/workspace/${d}`));
|
|
|
|
melde("");
|
|
melde("=== 1. Was ueberhaupt offensteht ===");
|
|
/* ==== EINE LISTE STATT EINER SPANNE (25.09.2026) ===================
|
|
|
|
Hier stand `>= 3 && <= 8`. Eine Spanne sagt nichts darueber, WELCHE
|
|
Seiten offen sind -- und genau das ist die Frage. Sie war ausserdem
|
|
seit dem 24.09. rot: Mit dem Support sind es zwoelf geworden, und
|
|
jede einzelne davon gehoert dorthin (nachgesehen, nicht vermutet --
|
|
dieselben zwoelf stehen auf der Kachelwand eines Mitglieds).
|
|
|
|
AB JETZT WIRD DIE LISTE FESTGEHALTEN. Kommt eine Seite dazu, wird
|
|
diese Zeile rot und nennt sie beim Namen; wer sie hinzugefuegt hat,
|
|
traegt sie hier nach und hat damit BESTAETIGT, dass die Community
|
|
sie sehen soll. Verschwindet eine, faellt es genauso auf. Eine
|
|
Spanne kann beides nicht: Sie haette elf statt zwoelf stillschweigend
|
|
durchgehen lassen -- und zwar in beide Richtungen.
|
|
|
|
DAS IST DIESELBE BAUART wie die festgelegten Kachelfarben: nicht
|
|
„ungefaehr so viele", sondern „genau diese, und hier steht warum". */
|
|
const COMMUNITY_SEITEN = [
|
|
"bereich.html", // die Bretter (Highlights, Anschlag, Wunschliste ...)
|
|
"bewerben.html", // Mitmachen
|
|
"chat.html", // der Treff
|
|
"hilfe.html", // Regeln & Hilfe
|
|
"material.html", // Dogi-Media
|
|
/* DIE REACTION kam am 28.09.2026 dazu und steht im
|
|
Community-Bereich als eigene Kachel hinter Dogi-Media. Wer
|
|
zusieht, braucht die Seite -- sonst gaebe es niemanden im Saal.
|
|
|
|
WARUM DIESE LISTE TROTZDEM VON HAND GEPFLEGT WIRD: Sie ist eine
|
|
ABSICHT, keine Ableitung. Wuerde sie aus `rechte.js` gelesen,
|
|
verglichen zwei Kopien derselben Quelle miteinander -- dann
|
|
waere sie immer gruen und wuerde nichts beweisen. Der Preis ist
|
|
dieser Eintrag hier; die Pruefung sagt beim Anschlagen selbst,
|
|
welche Seite fehlt oder zu viel ist. */
|
|
"reaktion.html", // zusammen schauen (seit 28.09.2026)
|
|
"start.html", // die Wand
|
|
"steckbrief.html", // der eigene Steckbrief
|
|
"support.html", // etwas ist kaputt (seit 24.09. fuer alle)
|
|
"treff-regeln.html", // die Regeln des Rudels
|
|
"unsere-seiten.html", // Draussen
|
|
"unterstuetzen.html", // Unterstuetzen
|
|
"willkommen.html", // die Begruessung
|
|
];
|
|
const zuViel = erlaubt.filter((s) => !COMMUNITY_SEITEN.includes(s));
|
|
const fehltDa = COMMUNITY_SEITEN.filter((s) => !erlaubt.includes(s));
|
|
ok(zuViel.length === 0 && fehltDa.length === 0,
|
|
`ein Mitglied darf genau die ${COMMUNITY_SEITEN.length} vorgesehenen Seiten`
|
|
+ (zuViel.length ? ` -- ZU VIEL: ${zuViel.join(", ")}` : "")
|
|
+ (fehltDa.length ? ` -- ES FEHLT: ${fehltDa.join(", ")}` : ""));
|
|
ok(verboten.length >= 15,
|
|
`und ${verboten.length} nicht -- so viele Wege gibt es, ins Leere zu zeigen`);
|
|
|
|
/* ---------------------------------------------------------------------
|
|
DIE MESSUNG IM BROWSER
|
|
--------------------------------------------------------------------- */
|
|
function roh(pfad, host, kopf = {}, koerper = null) {
|
|
return new Promise((f, s) => {
|
|
const a = httpAnfrage({
|
|
host: "127.0.0.1", port: PORT, path: pfad,
|
|
method: koerper ? "POST" : "GET",
|
|
headers: { Host: host, ...(koerper ? { "Content-Type": "application/json" } : {}), ...kopf },
|
|
}, (an) => {
|
|
let t = "";
|
|
an.on("data", (c) => { t += c; });
|
|
an.on("end", () => f({ code: an.statusCode, text: t, kopf: an.headers }));
|
|
});
|
|
a.on("error", s);
|
|
if (koerper) a.write(JSON.stringify(koerper));
|
|
a.end();
|
|
});
|
|
}
|
|
|
|
const { DatabaseSync } = await import("node:sqlite");
|
|
const { scryptSync, randomBytes } = await import("node:crypto");
|
|
{
|
|
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
for (const [name, rolle, code] of [
|
|
["Filipe", "admin", "CODE-DOGI-0001"],
|
|
["Mara", "gast", "CODE-GAST-0001"],
|
|
]) {
|
|
const salz = randomBytes(16).toString("hex");
|
|
const hash = scryptSync(code, salz, 64,
|
|
{ N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
|
|
d.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n, aktiv, erstellt)"
|
|
+ " VALUES (?,?,?,?,?,1,?)")
|
|
.run(name, rolle, hash, salz, 32768, new Date().toISOString());
|
|
}
|
|
d.close();
|
|
}
|
|
|
|
async function anmelden(rolle, code) {
|
|
let a = await roh("/workspace/api/anmelden", CREW, {}, { rolle, code });
|
|
if (a.code === 400 && a.text.includes("alter_offen")) {
|
|
a = await roh("/workspace/api/anmelden", CREW, {}, { rolle, code, alter_ok: true });
|
|
}
|
|
return [].concat(a.kopf["set-cookie"] || []).map((z) => z.split(";")[0]).join("; ");
|
|
}
|
|
const kGast = await anmelden("gast", "CODE-GAST-0001");
|
|
const kDogi = await anmelden("admin", "CODE-DOGI-0001");
|
|
|
|
/* Damit die Bretter nicht leer sind -- auf einer leeren Seite findet
|
|
man keine Wege, die ins Leere fuehren. */
|
|
for (const b of ["treff", "anschlag", "ansteht", "wunsch", "highlight", "regeln", "mitmachen"]) {
|
|
await roh(`/workspace/api/bereich/${b}/start`, CREW,
|
|
{ cookie: kDogi, origin: `http://${CREW}:${PORT}` }, {});
|
|
}
|
|
|
|
let pw = null;
|
|
try {
|
|
pw = await import("file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
|
} catch { /* dritter Ausgang unten */ }
|
|
|
|
if (!pw) {
|
|
melde("");
|
|
melde("KANN NICHT NACHSEHEN: Playwright fehlt -- die Seiten wurden nicht geoeffnet.");
|
|
melde(`${geprueft} Pruefungen, ${fehler} Fehler`);
|
|
process.exit(3);
|
|
}
|
|
|
|
/* WÖRTER, DIE JEMAND ANDEREM GEHÖREN.
|
|
|
|
Keine Rollennamen -- die stehen bewusst nirgends in ausgelieferten
|
|
Dateien. Gesucht wird nach der SPRACHE eines Arbeitsplatzes. */
|
|
const FREMDE_WOERTER = [
|
|
"Vorarbeiten", "gönn dir was", "betreut", "Review",
|
|
"Pipeline", "überfällig", "In Arbeit", "Abgebrochen",
|
|
];
|
|
|
|
/* ZWEI BREITEN, NICHT EINE.
|
|
|
|
Die Community ist ueberwiegend mit dem Telefon unterwegs. Eine
|
|
Messung auf 1400 px beantwortet fuer sie die falsche Frage -- und
|
|
genau auf dem Handy sind heute die haesslichsten Sachen aufgefallen
|
|
(drei verschiedene Kartenanordnungen, ein Hinweis quer ueber der
|
|
naechsten Beschriftung). Was auf einer Breite in Ordnung ist, muss
|
|
es auf der anderen nicht sein. */
|
|
const BREITEN = [
|
|
{ name: "Rechner", breite: 1400, hoehe: 1000 },
|
|
{ name: "Handy", breite: 412, hoehe: 915 },
|
|
];
|
|
|
|
const browser = await pw.chromium.launch();
|
|
try {
|
|
for (const geraet of BREITEN) {
|
|
const ctx = await browser.newContext({
|
|
viewport: { width: geraet.breite, height: geraet.hoehe },
|
|
isMobile: geraet.breite < 700, hasTouch: geraet.breite < 700 });
|
|
await ctx.addCookies(kGast.split("; ").filter(Boolean).map((z) => {
|
|
const [name, ...rest] = z.split("=");
|
|
return { name, value: rest.join("="), domain: "127.0.0.1", path: "/" };
|
|
}));
|
|
const seite = await ctx.newPage();
|
|
const skriptfehler = [];
|
|
seite.on("pageerror", (e) => skriptfehler.push(String(e).slice(0, 100)));
|
|
|
|
/* Jede erlaubte Seite -- und bei bereich.html jedes der sieben
|
|
Bretter, denn sie sehen völlig verschieden aus. */
|
|
const ziele = [];
|
|
for (const d of erlaubt) {
|
|
if (d === "bereich.html") {
|
|
for (const b of ["treff", "anschlag", "ansteht", "wunsch", "highlight", "regeln", "mitmachen"]) {
|
|
ziele.push(`${d}?b=${b}`);
|
|
}
|
|
} else ziele.push(d);
|
|
}
|
|
|
|
melde("");
|
|
melde(`=== 2. Wege, die ins Leere fuehren (${geraet.name}) ===`);
|
|
let toteWege = 0, geprueftLinks = 0;
|
|
const gefunden = [];
|
|
for (const ziel of ziele) {
|
|
await seite.goto(`http://127.0.0.1:${PORT}/workspace/${ziel}`, { waitUntil: "networkidle" });
|
|
await seite.waitForTimeout(900);
|
|
const wege = await seite.evaluate(() =>
|
|
[...document.querySelectorAll("a[href]")]
|
|
.filter((a) => a.offsetParent !== null)
|
|
.map((a) => ({ ziel: a.getAttribute("href"), text: a.textContent.trim().slice(0, 30) })));
|
|
for (const w of wege) {
|
|
const datei = String(w.ziel).split("?")[0].split("#")[0].replace(/^\.?\//, "");
|
|
if (!datei.endsWith(".html")) continue;
|
|
geprueftLinks++;
|
|
if (verboten.includes(datei)) {
|
|
toteWege++;
|
|
gefunden.push(`${ziel}: „${w.text}" -> ${datei}`);
|
|
}
|
|
}
|
|
}
|
|
/* DIE ANZAHL GEHÖRT IN DIE BEDINGUNG. Ohne sie wäre „0 tote Wege"
|
|
auch dann wahr, wenn gar keine Seite geladen hätte. */
|
|
/* DIE UNTERGRENZE WIRD ABGELEITET, NICHT GESETZT.
|
|
|
|
Hier stand `>= 20`. Auf dem Rechner sind es 27 Wege, auf dem Handy
|
|
18 -- dort klappt die Kopfleiste um, und ein Teil der Links ist
|
|
nicht sichtbar. Die feste Zahl machte die Pruefung auf der
|
|
zweiten Breite rot, ohne dass irgendetwas falsch war.
|
|
|
|
Sie einfach auf 15 zu senken waere derselbe Fehler mit einer
|
|
anderen Zahl. Verlangt wird jetzt, was die Sache selbst hergibt:
|
|
mindestens ein sichtbarer Weg je Seite. Findet sich weniger, hat
|
|
etwas nicht geladen -- und dann ist „0 tote Wege" keine Auskunft,
|
|
sondern ein Messfehler. */
|
|
ok(geprueftLinks >= ziele.length && toteWege === 0,
|
|
`${geprueftLinks} sichtbare Wege auf ${ziele.length} Seiten, davon ${toteWege} ins Leere`);
|
|
for (const g of gefunden.slice(0, 10)) console.log(` -> ${g}`);
|
|
|
|
melde("");
|
|
melde(`=== 3. Sprache, die jemand anderem gehoert (${geraet.name}) ===`);
|
|
let fremd = 0;
|
|
const wo = [];
|
|
for (const ziel of ziele) {
|
|
await seite.goto(`http://127.0.0.1:${PORT}/workspace/${ziel}`, { waitUntil: "networkidle" });
|
|
await seite.waitForTimeout(700);
|
|
const text = await seite.evaluate(() => document.body.innerText);
|
|
for (const w of FREMDE_WOERTER) {
|
|
if (text.includes(w)) { fremd++; wo.push(`${ziel}: „${w}"`); }
|
|
}
|
|
}
|
|
ok(fremd === 0, `keine fremde Arbeitssprache auf ${ziele.length} Seiten (${fremd} Stellen)`);
|
|
for (const g of wo.slice(0, 10)) console.log(` -> ${g}`);
|
|
|
|
/* =====================================================================
|
|
4. DIE GEGENPROBE: BEHAELT DAS TEAM SEINE WEGE?
|
|
|
|
Was hier weggenommen wird, weil es fuer ein Mitglied ins Leere
|
|
fuehrt, muss fuer die anderen stehen bleiben. Ohne diesen
|
|
Abschnitt saehe eine abgeschaffte Funktion genauso aus wie eine,
|
|
die richtig entscheidet -- und zwar in beiden Faellen gruen.
|
|
===================================================================== */
|
|
melde("");
|
|
melde("=== 4. Und behaelt das Team seine Wege? ===");
|
|
if (geraet.breite > 700) {
|
|
const ctx2 = await browser.newContext({ viewport: { width: 1400, height: 1000 } });
|
|
await ctx2.addCookies(kDogi.split("; ").filter(Boolean).map((z) => {
|
|
const [name, ...rest] = z.split("=");
|
|
return { name, value: rest.join("="), domain: "127.0.0.1", path: "/" };
|
|
}));
|
|
const seite2 = await ctx2.newPage();
|
|
await seite2.goto(`http://127.0.0.1:${PORT}/workspace/start.html`, { waitUntil: "networkidle" });
|
|
await seite2.waitForTimeout(1200);
|
|
const z = await seite2.evaluate(() => ({
|
|
chat: !!document.querySelector(".chat-knopf"),
|
|
wege: [...document.querySelectorAll("a[href$='.html']")].length,
|
|
}));
|
|
ok(z.chat, "DogFather hat seinen Chat-Knopf weiterhin");
|
|
ok(z.wege >= 5, `und weiterhin Wege auf andere Seiten (${z.wege})`);
|
|
await ctx2.close();
|
|
}
|
|
|
|
melde("");
|
|
melde(`=== 5. Und laeuft ueberhaupt alles? (${geraet.name}) ===`);
|
|
ok(skriptfehler.length === 0,
|
|
`kein Skriptfehler auf ${ziele.length} Seiten (${geraet.name})`
|
|
+ (skriptfehler.length ? ` -- ${skriptfehler[0]}` : ""));
|
|
|
|
await ctx.close();
|
|
}
|
|
} finally {
|
|
await browser.close();
|
|
}
|
|
|
|
melde("");
|
|
melde(`${geprueft} Pruefungen, ${fehler} Fehler`);
|
|
melde(fehler ? "NICHT IN ORDNUNG" : "ALLES IN ORDNUNG");
|
|
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Rest */ }
|
|
process.exit(fehler ? 1 : 0);
|