/* ===================================================================== Verwaltung: Zugangscode bei JEDEM Öffnen (Nutzer-Wunsch 27.08.2026) Geprüft wird die Regel, die Dogi ausgewählt hat: - Code beim Öffnen der Seite/App - Neuladen im selben Tab wirft NICHT raus - jeder neue Tab / jeder App-Start verlangt wieder den Code - keine Abmeldung bei Untätigkeit (ausdrücklich nicht gewünscht) Zusätzlich: die alte, tagelang gültige Sitzung aus localStorage darf nicht mehr greifen und wird beim ersten Laden entfernt — sonst wäre Dogi nach dem Update weiterhin ohne Code drin. Der Login-Endpunkt liegt auf postfach.dogfather-universe.com; er wird hier abgefangen und beantwortet (inkl. CORS-Vorabfrage), damit kein echter Zugangscode nötig ist. ===================================================================== */ import { chromium } from "playwright"; import http from "http"; import fs from "fs"; import path from "path"; const TYP = { ".html": "text/html", ".css": "text/css", ".js": "text/javascript", ".png": "image/png", ".svg": "image/svg+xml", ".webp": "image/webp", ".json": "application/json", ".ico": "image/x-icon", ".jpg": "image/jpeg", ".webmanifest": "application/manifest+json", }; const srv = http.createServer((q, r) => { const p = path.join(process.cwd(), decodeURIComponent(q.url.split("?")[0])); if (fs.existsSync(p) && fs.statSync(p).isFile()) { r.writeHead(200, { "content-type": TYP[path.extname(p)] || "text/plain" }); return r.end(fs.readFileSync(p)); } r.writeHead(404); r.end(""); }); let ok = 0, fehl = 0; const funde = []; const pruefe = (name, gut, info) => { if (gut) { ok++; console.log(" ok " + name + (info ? " -> " + info : "")); } else { fehl++; console.log(" FEHL " + name + (info ? " -> " + info : "")); funde.push(name + " — " + info); } }; const CORS = { "access-control-allow-origin": "*", "access-control-allow-headers": "content-type,authorization", "access-control-allow-methods": "POST,OPTIONS", }; const URL_SEITE = "http://127.0.0.1:4426/verwaltung.html"; const RICHTIGER_CODE = "GEHEIM-TEST"; /** Fängt alle Aufrufe ans Postfach-Backend ab. */ async function backendAttrappe(ziel) { await ziel.route("https://postfach.dogfather-universe.com/**", async (route) => { const req = route.request(); if (req.method() === "OPTIONS") return route.fulfill({ status: 204, headers: CORS, body: "" }); const pfad = new URL(req.url()).pathname; const daten = (() => { if (pfad === "/auth/login") { const code = (req.postDataJSON() || {}).code; return code === RICHTIGER_CODE ? { ok: true, token: "test-token", isOwner: true, name: "Dogi", permissions: [] } : { ok: false, error: "Zugangscode falsch." }; } if (pfad === "/admin/supporter/list") return { ok: true, supporters: [] }; if (pfad === "/admin/overview") return { ok: true, premiums: [], payments: [] }; if (pfad === "/admin/events/list") return { ok: true, events: [null, null] }; if (pfad === "/admin/polls/list") return { ok: true, polls: [] }; if (pfad === "/admin/team/list") return { ok: true, members: [] }; if (pfad === "/admin/testimonials/list") return { ok: true, testimonials: [] }; if (pfad === "/admin/special-event") return { ok: true, event: null }; return { ok: true }; })(); await route.fulfill({ status: 200, headers: { ...CORS, "content-type": "application/json" }, body: JSON.stringify(daten) }); }); } const lage = (seite) => seite.evaluate(() => ({ gateSichtbar: getComputedStyle(document.getElementById("gate-container")).display !== "none", verwaltungSichtbar: getComputedStyle(document.getElementById("supp-root")).display !== "none", imTab: !!sessionStorage.getItem("dogfather_admin_session"), imBrowser: !!localStorage.getItem("dogfather_admin_session"), })); (async () => { await new Promise((r) => srv.listen(4426, "127.0.0.1", r)); const browser = await chromium.launch(); const kontext = await browser.newContext(); await backendAttrappe(kontext); /* ---------- 1. Frisch geöffnet ---------- */ console.log("\n== Frisch geöffnet =="); const tab = await kontext.newPage(); await tab.goto(URL_SEITE); await tab.waitForTimeout(400); let l = await lage(tab); pruefe("Zugangswand wird verlangt", l.gateSichtbar && !l.verwaltungSichtbar, JSON.stringify(l)); /* ---------- 2. Alte Tages-Sitzung aus localStorage ---------- */ console.log("\n== Alte 24-Std-Sitzung aus localStorage =="); await tab.evaluate(() => localStorage.setItem("dogfather_admin_session", JSON.stringify({ token: "alt-token", isOwner: true, name: "Dogi", permissions: [], loggedInAt: Date.now(), }))); await tab.reload(); await tab.waitForTimeout(400); l = await lage(tab); pruefe("Alte Sitzung öffnet NICHT mehr", l.gateSichtbar && !l.verwaltungSichtbar, JSON.stringify(l)); pruefe("Alte Sitzung wird aus dem Browser entfernt", !l.imBrowser, "localStorage leer: " + !l.imBrowser); /* ---------- 3. Falscher Code ---------- */ console.log("\n== Falscher Code =="); await tab.fill("#code-input-dogi", "FALSCH"); await tab.press("#code-input-dogi", "Enter"); await tab.waitForTimeout(500); l = await lage(tab); pruefe("Falscher Code kommt nicht rein", l.gateSichtbar && !l.verwaltungSichtbar, JSON.stringify(l)); /* ---------- 4. Richtiger Code ---------- */ console.log("\n== Richtiger Code =="); await tab.fill("#code-input-dogi", RICHTIGER_CODE); await tab.press("#code-input-dogi", "Enter"); await tab.waitForTimeout(800); l = await lage(tab); pruefe("Verwaltung öffnet sich", !l.gateSichtbar && l.verwaltungSichtbar, JSON.stringify(l)); pruefe("Sitzung liegt im Tab (sessionStorage)", l.imTab, "sessionStorage gesetzt"); pruefe("Sitzung liegt NICHT dauerhaft im Browser", !l.imBrowser, "localStorage leer"); /* ---------- 5. Neuladen im selben Tab ---------- */ console.log("\n== Neuladen im selben Tab (F5) =="); await tab.reload(); await tab.waitForTimeout(600); l = await lage(tab); pruefe("Neuladen wirft nicht raus", !l.gateSichtbar && l.verwaltungSichtbar, JSON.stringify(l)); /* ---------- 6. Neuer Tab ---------- */ console.log("\n== Neuer Tab / neuer App-Start =="); const tab2 = await kontext.newPage(); await tab2.goto(URL_SEITE); await tab2.waitForTimeout(400); const l2 = await lage(tab2); pruefe("Neuer Tab verlangt den Code", l2.gateSichtbar && !l2.verwaltungSichtbar, JSON.stringify(l2)); await tab2.close(); /* ---------- 7. Abmelden ---------- */ console.log("\n== Abmelden =="); await tab.click("#logout-btn"); await tab.waitForTimeout(600); l = await lage(tab); pruefe("Abmelden zeigt wieder die Zugangswand", l.gateSichtbar && !l.verwaltungSichtbar, JSON.stringify(l)); pruefe("Abmelden räumt die Sitzung weg", !l.imTab && !l.imBrowser, "beide Speicher leer"); /* ---------- 8. Kein Rauswurf beim Herumliegen ---------- */ console.log("\n== Untätigkeit (soll NICHT abmelden) =="); await tab.fill("#code-input-dogi", RICHTIGER_CODE); await tab.press("#code-input-dogi", "Enter"); await tab.waitForTimeout(800); await tab.waitForTimeout(5000); // 5 Sekunden unberührt liegen lassen l = await lage(tab); pruefe("Nach Untätigkeit weiterhin eingeloggt", !l.gateSichtbar && l.verwaltungSichtbar, JSON.stringify(l)); await browser.close(); srv.close(); console.log("\n===== " + ok + " ok, " + fehl + " Fehler ====="); if (funde.length) { console.log("\nOffene Punkte:"); funde.forEach((f) => console.log(" - " + f)); } process.exit(fehl ? 1 : 0); })();