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
225 lines
10 KiB
JavaScript
225 lines
10 KiB
JavaScript
/* =====================================================================
|
|
JEDE SEITE TRÄGT IHRE FARBE — auch ganz oben in der Leiste
|
|
=====================================================================
|
|
|
|
Filipe am 22.09.2026:
|
|
|
|
„je nachdem auf welcher seite ich bin wechseln gaaanz oben in der
|
|
leiste gewissene symbole und sachen mit ... ich will das auf
|
|
jeder seite ... nicht mehr wie auf screen3 sondern genau gleich
|
|
wie da."
|
|
|
|
Sein Vorbild war die Highlights-Seite. Und das ist der Schlüssel zum
|
|
Befund: Highlights ist `bereich.html`, und `bereich.html` ist eine
|
|
von DREI Seiten, die `bereich.css` laden. Dort stand die Regel, die
|
|
aus `data-ton` eine Akzentfarbe macht.
|
|
|
|
Gemessen: `kopf.js` setzt `data-ton` am <body> auf jeder Seite. Auf
|
|
zweiunddreißig von fünfunddreißig las es niemand — die Farbe war da
|
|
und kam nicht an. Seit dem 22.09. steht die Regel in `start.css`,
|
|
und die liegt auf allen fünfunddreißig.
|
|
|
|
---------------------------------------------------------------------
|
|
WAS HIER GEMESSEN WIRD — und warum nicht der Quelltext
|
|
|
|
Ob eine CSS-Regel WIRKT, steht nicht in der Datei. `--akzent` könnte
|
|
von einer späteren Regel überschrieben werden, `data-ton` könnte
|
|
fehlen, das Haus könnte etwas anderes sagen. Deshalb ein echter
|
|
Browser und `getComputedStyle` — die Farbe, die auf dem Schirm
|
|
ankommt.
|
|
|
|
ZWEI SEITEN MIT VERSCHIEDENEN TÖNEN, und beide Richtungen:
|
|
* auf jeder kommt eine Farbe an
|
|
* und es ist nicht dieselbe
|
|
Ohne den zweiten Teil wäre eine Seite, die überall dieselbe Farbe
|
|
zeigt, ebenfalls grün — und genau das war der Zustand vorher.
|
|
===================================================================== */
|
|
import { mkdtempSync, rmSync, readFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { request as httpAnfrage } from "node:http";
|
|
import { execFileSync } from "node:child_process";
|
|
import { createServer as httpsServer } from "node:https";
|
|
import { notbremse } from "./helfer-notbremse.mjs";
|
|
import { eigenerPort } from "./helfer-port.mjs";
|
|
import { zertifikatBauen } from "./helfer-openssl.mjs";
|
|
|
|
const CREW = "crew.dogfather-universe.com";
|
|
const PORT = await eigenerPort(import.meta, "pruef-kopfleiste-farbe");
|
|
const ordner = mkdtempSync(join(tmpdir(), "ws-kopffarbe-"));
|
|
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
|
process.env.PORT = `${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 }); };
|
|
await import("./index.js");
|
|
notbremse(180_000, "pruef-kopfleiste-farbe");
|
|
await new Promise((r) => setTimeout(r, 900));
|
|
|
|
let fehler = 0, geprueft = 0, offen = 0;
|
|
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
|
const unklar = (t) => { offen++; console.log(" -- " + t); };
|
|
|
|
/* ---------- Ein Mensch ------------------------------------------------- */
|
|
const { DatabaseSync } = await import("node:sqlite");
|
|
const { scryptSync, randomBytes, createHmac } = await import("node:crypto");
|
|
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
const jetzt = new Date().toISOString();
|
|
const k = randomBytes(32).toString("hex");
|
|
d.prepare("INSERT INTO einstellungen (schluessel,wert,geaendert,von) VALUES (?,?,?,NULL)"
|
|
+ " ON CONFLICT(schluessel) DO NOTHING").run("code_kennung_schluessel", k, jetzt);
|
|
{
|
|
const salt = randomBytes(16).toString("hex");
|
|
const hash = scryptSync("CODE-DOGI-0001", salt, 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,code_kennung,aktiv,erstellt)"
|
|
+ " VALUES (?,?,?,?,?,?,1,?)")
|
|
.run("Filipe", "admin", hash, salt, 32768,
|
|
createHmac("sha256", k).update("CODE-DOGI-0001").digest("hex"), jetzt);
|
|
}
|
|
d.close();
|
|
|
|
/* ---------- Der Vorbau ------------------------------------------------- */
|
|
const schl = join(ordner, "p.key"), zert = join(ordner, "p.crt");
|
|
zertifikatBauen(schl, zert, CREW);
|
|
const HP = PORT + 1;
|
|
const vorbau = httpsServer({ key: readFileSync(schl), cert: readFileSync(zert) }, (von, nach) => {
|
|
const w = httpAnfrage({ host: "127.0.0.1", port: PORT, path: von.url, method: von.method,
|
|
headers: { ...von.headers, host: `${CREW}:${HP}` } },
|
|
(a) => { nach.writeHead(a.statusCode || 502, a.headers); a.pipe(nach); });
|
|
von.pipe(w);
|
|
});
|
|
await new Promise((r) => vorbau.listen(HP, "127.0.0.1", r));
|
|
const SEITE = `https://${CREW}:${HP}`;
|
|
|
|
const pw = await import(
|
|
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
|
const browser = await pw.chromium.launch({
|
|
args: [`--host-resolver-rules=MAP ${CREW} 127.0.0.1`, "--ignore-certificate-errors"] });
|
|
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
const seite = await ctx.newPage();
|
|
const meckern = [];
|
|
seite.on("pageerror", (f) => meckern.push(String(f).slice(0, 140)));
|
|
|
|
console.log("=== Jede Seite traegt ihre Farbe ===");
|
|
console.log("");
|
|
|
|
await seite.goto(`${SEITE}/workspace/`, { waitUntil: "networkidle" });
|
|
await seite.click('.rolle[data-rolle="admin"]');
|
|
await seite.fill("#code", "CODE-DOGI-0001");
|
|
await seite.click("button[type=submit]");
|
|
await seite.waitForTimeout(900);
|
|
|
|
/* WELCHE SEITEN GEMESSEN WERDEN, STEHT NICHT HIER.
|
|
-----------------------------------------------------------------
|
|
Die Kacheln der Startseite sagen, welche Seiten es gibt und welchen
|
|
Ton jede hat. Eine Liste in dieser Datei waere die, die beim
|
|
naechsten Umbau eine Seite nennt, die es nicht mehr gibt -- oder,
|
|
schlimmer, die neue nicht kennt. */
|
|
await seite.goto(`${SEITE}/workspace/start.html`, { waitUntil: "networkidle" });
|
|
await seite.waitForTimeout(1800);
|
|
/* DIE KACHEL IST DAS <li>, DER VERWEIS LIEGT DARIN.
|
|
Beim ersten Anlauf stand hier `.kachel[href]` -- das trifft nichts,
|
|
und die Pruefung meldete "0 Kacheln". Das war richtig gemeldet und
|
|
trotzdem ein Fehler von mir: Die Struktur steht in start.js
|
|
(`li.kachel` mit `a.kachel__link` darin), nachgesehen statt
|
|
geraten. */
|
|
const kacheln = await seite.evaluate(() =>
|
|
[...document.querySelectorAll('li.kachel[data-ton]')]
|
|
.map((li) => ({
|
|
ziel: li.querySelector('a')?.getAttribute("href") || null,
|
|
ton: li.dataset.ton || null,
|
|
}))
|
|
.filter((x) => x.ziel && /^[a-z-]+\.html$/.test(x.ziel) && x.ton !== null));
|
|
|
|
ok(kacheln.length >= 4,
|
|
`${kacheln.length} Kacheln mit eigenem Ton auf der Startseite gefunden`);
|
|
|
|
/* Zwei mit VERSCHIEDENEN Toenen -- ohne Unterschied liesse sich nicht
|
|
zeigen, dass die Farbe der Seite folgt und nicht der Leiste. */
|
|
const nachTon = new Map();
|
|
for (const x of kacheln) if (!nachTon.has(x.ton)) nachTon.set(x.ton, x);
|
|
const auswahl = [...nachTon.values()].slice(0, 3);
|
|
ok(auswahl.length >= 2,
|
|
`davon ${auswahl.length} mit verschiedenen Toenen (${auswahl.map((x) => x.ton).join(", ")})`);
|
|
|
|
const gemessen = [];
|
|
for (const x of auswahl) {
|
|
await seite.goto(`${SEITE}/workspace/${x.ziel}`, { waitUntil: "networkidle" });
|
|
await seite.waitForTimeout(1500);
|
|
const w = await seite.evaluate(() => {
|
|
const koerper = getComputedStyle(document.body);
|
|
const leiste = document.querySelector(".kopfleiste");
|
|
return {
|
|
adresse: location.pathname.split("/").pop(),
|
|
ton: document.body.dataset.ton || null,
|
|
akzent: koerper.getPropertyValue("--akzent").trim(),
|
|
tonFarbe: koerper.getPropertyValue("--ton").trim(),
|
|
leisteDa: !!leiste,
|
|
/* Die Kante unter der Leiste -- gemessen an dem, was wirklich
|
|
gezeichnet wird, nicht an der Regel im Quelltext. */
|
|
kante: leiste ? getComputedStyle(leiste, "::before").backgroundImage : "",
|
|
};
|
|
});
|
|
gemessen.push(w);
|
|
}
|
|
|
|
console.log("");
|
|
console.log("--- Was auf den Seiten ankommt ---");
|
|
for (const w of gemessen) {
|
|
console.log(` ${w.adresse.padEnd(18)} Ton ${String(w.ton).padStart(2)}`
|
|
+ ` --akzent ${w.akzent}`);
|
|
}
|
|
|
|
ok(gemessen.length >= 2 && gemessen.every((w) => w.ton !== null),
|
|
`auf allen ${gemessen.length} Seiten steht ein Ton am Koerper`);
|
|
/* DIE ZAHL GEHOERT IN DIE BEDINGUNG. `every()` auf einem leeren Feld
|
|
liefert `true` -- beim ersten Lauf waren drei Zeilen hier gruen,
|
|
ohne dass eine einzige Seite gemessen worden war. Genau der Fehler,
|
|
vor dem die Hausregel warnt, und er ist mir trotzdem passiert. */
|
|
ok(gemessen.length >= 2 && gemessen.every((w) => w.akzent && w.akzent !== "none"),
|
|
`auf allen ${gemessen.length} kommt daraus eine Akzentfarbe an`);
|
|
|
|
/* DER ENTSCHEIDENDE VERGLEICH. Vorher war --akzent auf 32 von 35
|
|
Seiten die feste Hausfarbe -- also ueberall dieselbe. */
|
|
const verschieden = new Set(gemessen.map((w) => w.akzent)).size;
|
|
ok(gemessen.length >= 2 && verschieden === gemessen.length,
|
|
`und sie ist auf jeder Seite eine andere (${verschieden} von ${gemessen.length})`);
|
|
|
|
ok(gemessen.length >= 2 && gemessen.every((w) => w.leisteDa),
|
|
`die Kopfleiste steht auf allen ${gemessen.length} gemessenen Seiten`);
|
|
ok(gemessen.length >= 2 && gemessen.every((w) => /gradient/i.test(w.kante || "")),
|
|
"und traegt unten eine Kante in der Farbe der Seite");
|
|
|
|
/* GEGENPROBE: Eine Seite OHNE Ton darf die Hausfarbe behalten. Eine
|
|
Regel, die auch dort zuschlaegt, wuerde eine Farbe erfinden. */
|
|
console.log("");
|
|
console.log("--- Gegenprobe ---");
|
|
{
|
|
const ohne = await seite.evaluate(() => {
|
|
const vorher = document.body.dataset.ton;
|
|
delete document.body.dataset.ton;
|
|
const a = getComputedStyle(document.body).getPropertyValue("--akzent").trim();
|
|
if (vorher !== undefined) document.body.dataset.ton = vorher;
|
|
return a;
|
|
});
|
|
const mit = gemessen.at(-1)?.akzent || null;
|
|
ok(!!ohne && !!mit && ohne !== mit,
|
|
`ohne Ton gilt wieder die Hausfarbe (${ohne} statt ${mit})`);
|
|
}
|
|
|
|
ok(meckern.length === 0, `der Browser meldet nichts (${meckern[0] || "still"})`);
|
|
|
|
/* ---------- Ende ------------------------------------------------------ */
|
|
await browser.close();
|
|
await new Promise((r) => vorbau.close(r));
|
|
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Windows haelt manchmal */ }
|
|
|
|
console.log("");
|
|
console.log(`${fehler === 0 ? "BESTANDEN" : "FEHLGESCHLAGEN"}`
|
|
+ ` -- ${geprueft} geprueft, ${fehler} Fehler, ${offen} nicht nachsehbar`);
|
|
process.exit(fehler === 0 ? 0 : 1);
|