/* ===================================================================== WAS SEHEN DIE VIER CREW-ROLLEN HEUTE AUF willkommen.html? Ausgangsmessung vom 06.10.2026, bevor der Bauplan auf der Crew-Seite umgesetzt wird. Ohne sie waere hinterher nicht zu sagen, was die Arbeit bewirkt hat -- und ob die Seite vorher wirklich leer war oder nur anders. Gemessen wird am HOSTNAMEN, nicht an der Rolle: Erst der Hostname macht `person.haus` zu "crew", und genau daran haengt die Schranke im Servermodul. ===================================================================== */ import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { request } from "node:http"; const ordner = mkdtempSync(join(tmpdir(), "ws-anlcrew-")); process.env.WORKSPACE_DB = join(ordner, "workspace.db"); import { notbremse } from "./helfer-notbremse.mjs"; const { eigenerPort } = await import("./helfer-port.mjs"); const PORT = await eigenerPort(import.meta, "mess-anleitung-crew"); 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(120_000, "mess-anleitung-crew"); await new Promise((r) => setTimeout(r, 900)); const CREW = "crew.dogfather-universe.com"; /** Ein Aufruf mit frei waehlbarem Host-Kopf -- `fetch` laesst `Host` * nicht setzen, deshalb node:http. */ function ruf(pfad, { keks = "", methode = "GET", rumpf = null } = {}) { return new Promise((fertig) => { const kopf = { Host: CREW }; if (keks) kopf.Cookie = keks; if (rumpf) { kopf["Content-Type"] = "application/json"; } const a = request({ host: "127.0.0.1", port: PORT, path: pfad, method: methode, headers: kopf }, (r) => { let t = ""; r.on("data", (c) => { t += c; }); r.on("end", () => fertig({ status: r.statusCode, keks: (r.headers["set-cookie"] || []).map((k) => k.split(";")[0]).join("; "), text: t, json: (() => { try { return JSON.parse(t); } catch { return null; } })(), })); }); a.on("error", () => fertig({ status: 0, text: "", json: null, keks: "" })); if (rumpf) a.write(rumpf); a.end(); }); } const { DatabaseSync } = await import("node:sqlite"); const { scryptSync, randomBytes } = await import("node:crypto"); await ruf("/workspace/api/anmelden", { methode: "POST", rumpf: JSON.stringify({ rolle: "admin", code: "x" }) }); for (let i = 0; i < 60; i++) { try { const p = new DatabaseSync(process.env.WORKSPACE_DB); try { p.prepare("SELECT 1 FROM personen LIMIT 1").get(); break; } finally { p.close(); } } catch { await new Promise((r) => setTimeout(r, 250)); } } const ROLLEN = [["gast", "Community"], ["modi", "Modi"], ["linke", "Linke Hand"], ["hand", "Rechte Hand"]]; { const d = new DatabaseSync(process.env.WORKSPACE_DB); for (const [rolle] of ROLLEN) { const salt = randomBytes(16).toString("hex"); const code = `CODE-${rolle.toUpperCase()}-0001`; const hash = scryptSync(code, 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, aktiv, erstellt) VALUES (?,?,?,?,?,1,?)") .run(rolle, rolle, hash, salt, 32768, new Date().toISOString()); } d.close(); } console.log("\n=== Was die Crew-Rollen heute auf willkommen.html bekommen ==="); for (const [rolle, wie] of ROLLEN) { const an = await ruf("/workspace/api/anmelden", { methode: "POST", rumpf: JSON.stringify({ rolle, code: `CODE-${rolle.toUpperCase()}-0001` }), }); if (an.status !== 200) { console.log(` ${wie.padEnd(12)} kommt nicht herein (${an.status})`); continue; } const seite = await ruf("/workspace/willkommen.html", { keks: an.keks }); const api = await ruf("/workspace/api/anleitung", { keks: an.keks }); const karten = api.json?.karten?.length ?? "-"; console.log(` ${wie.padEnd(12)} Seite ${seite.status} · Schnittstelle ${api.status}` + ` · Karten ${karten}` + (api.json?.fehler ? ` · "${api.json.fehler}"` : "")); } /* ==== FUEHREN DIE KACHELN DER LINKEN HAND INS LEERE? ================ Der Bauplan spricht der Linken Hand vier Kacheln ab (Talente, Personen & Zugaenge, Wer sieht was, Vertraulich melden). Die Kachelliste gibt sie ihr trotzdem. Entweder irrt der Plan -- oder sie sieht vier Kacheln, die auf Seiten fuehren, die sie nicht oeffnen darf. Gefragt wird der laufende Server, nicht die Tabelle. */ { console.log("\n=== Linke Hand: fuehren ihre Kacheln irgendwohin? ==="); const an = await ruf("/workspace/api/anmelden", { methode: "POST", rumpf: JSON.stringify({ rolle: "linke", code: "CODE-LINKE-0001" }), }); for (const pfad of ["/workspace/talente.html", "/workspace/personen.html", "/workspace/rechte.html", "/workspace/hilfe.html", "/workspace/aufgaben.html"]) { const a2 = await ruf(pfad, { keks: an.keks }); const wohin = a2.status >= 300 && a2.status < 400 ? " -> umgeleitet" : ""; console.log(` ${pfad.replace("/workspace/", "").padEnd(18)} ${a2.status}${wohin}`); } } /* Und was steht in der Datenbank -- je Haus. */ { const d = new DatabaseSync(process.env.WORKSPACE_DB); console.log("\n=== Inhalte in der Datenbank ==="); for (const r of d.prepare( "SELECT haus, rolle, COUNT(*) n FROM anl_karte GROUP BY haus, rolle ORDER BY haus, rolle").all()) { console.log(` ${r.haus.padEnd(8)} ${r.rolle.padEnd(10)} ${r.n} Karten`); } const crew = d.prepare("SELECT COUNT(*) n FROM anl_karte WHERE haus = 'crew'").get().n; console.log(` -> fuer die Crew: ${crew} Karten`); d.close(); } try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ } process.exit(0);