/* ===================================================================== WAS PASSIERT BEIM TEILEN WIRKLICH? (05.10.2026) Filipe: "ich lade video hoch und es erscheint immer noch nicht", dazu auf Nachfrage: die Seite bleibt haengen, es kommt KEINE Meldung -- und auf der Crew-Adresse ist das Brett voll (49 Videos). Gemessen war vorher schon: Server unveraendert, Sitzung gueltig, TikTok antwortet (auch auf Kurzlinks), und im Protokoll steht heute kein einziges `video_eingelesen` -- die Anfrage erreicht den Server also gar nicht. `los()` in teilen.js macht keine Netzanfrage; wenn die Seite dort stehenbleibt, ist das Skript gestolpert. Diese Messung spielt genau das nach: Teilen-Seite mit einem Link aufrufen, Browsermeldungen mitschreiben, und nachsehen, ob die Ladezeile verschwindet. Dann denselben Weg bis zum Absenden. KEIN PRUEFLAUF, sondern eine Messung -- sie stellt fest, was ist, und sagt nicht, was sein soll. ===================================================================== */ import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; const ordner = mkdtempSync(join(tmpdir(), "ws-teilen-")); 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-teilen"); 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, "mess-teilen"); await new Promise((r) => setTimeout(r, 900)); const BASIS = `http://127.0.0.1:${PORT}`; const { DatabaseSync } = await import("node:sqlite"); const { scryptSync, randomBytes } = await import("node:crypto"); await fetch(BASIS + "/workspace/api/anmelden", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ rolle: "admin", code: "x" }), }).catch(() => {}); 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 d = new DatabaseSync(process.env.WORKSPACE_DB); 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, aktiv, erstellt) VALUES (?,?,?,?,?,1,?)") .run("Filipe", "admin", hash, salt, 32768, new Date().toISOString()); d.close(); } const { chromium } = await import("playwright"); const browser = await chromium.launch(); const kontext = await browser.newContext({ viewport: { width: 390, height: 844 }, hasTouch: true, isMobile: true, }); const seite = await kontext.newPage(); const klagen = []; seite.on("console", (m) => { if (m.type() === "error") klagen.push("Konsole: " + m.text()); }); seite.on("pageerror", (f) => klagen.push("ABSTURZ: " + String(f?.message || f))); seite.on("requestfailed", (r) => klagen.push("Anfrage gescheitert: " + r.url().replace(BASIS, ""))); await seite.goto(BASIS + "/workspace/", { waitUntil: "domcontentloaded" }); await seite.click('.rolle[data-rolle="admin"]'); await seite.fill("#code", "CODE-DOGI-0001"); await seite.click("button[type=submit]"); await seite.waitForURL("**/start.html", { timeout: 20000 }); /* ---- Genau so, wie die App beim Teilen aufruft ---------------------- */ const LINK = "https://vm.tiktok.com/ZN8h3hbHr/"; await seite.goto(`${BASIS}/workspace/teilen.html?url=${encodeURIComponent(LINK)}`, { waitUntil: "domcontentloaded" }); await seite.waitForTimeout(1500); const lage = await seite.evaluate(() => { const z = (id) => document.getElementById(id); return { ladezeileWeg: z("ladezeile")?.hidden === true, wahlDa: z("wahl")?.hidden === false, knoepfeDa: z("knoepfe")?.hidden === false, fundDa: z("fund")?.hidden === false, linkText: z("url-text")?.textContent || "", knopfText: z("uebernehmen")?.textContent || "", fehlerText: z("fehler")?.textContent || "", }; }); console.log("\n=== Die Teilen-Seite nach dem Aufruf ==="); console.log(` Ladezeile verschwunden : ${lage.ladezeileWeg}`); console.log(` Auswahl sichtbar : ${lage.wahlDa}`); console.log(` Knopfreihe sichtbar : ${lage.knoepfeDa}`); console.log(` Link erkannt : ${lage.fundDa} "${lage.linkText}"`); console.log(` Knopf : "${lage.knopfText.trim()}"`); console.log(` Fehlerzeile : "${lage.fehlerText.trim()}"`); console.log(` Browsermeldungen : ${klagen.length ? klagen.join(" | ") : "keine"}`); /* ---- Und jetzt absenden -------------------------------------------- */ if (lage.knoepfeDa) { const antwort = seite.waitForResponse( (r) => /\/api\/bereich\/[a-z]+\/video$/.test(r.url()), { timeout: 20000 }).catch(() => null); await seite.click("#uebernehmen"); const a = await antwort; await seite.waitForTimeout(1500); const danach = await seite.evaluate(() => ({ fertig: document.getElementById("fertig")?.hidden === false, satz: document.getElementById("fertig-satz")?.textContent || "", fehler: document.getElementById("fehler")?.textContent || "", knopf: document.getElementById("uebernehmen")?.textContent || "", })); console.log("\n=== Nach dem Absenden ==="); console.log(` Antwort vom Server : ${a ? a.status() : "KEINE -- es ging nichts hinaus"}`); console.log(` Erfolg sichtbar : ${danach.fertig} "${danach.satz.trim()}"`); console.log(` Fehlerzeile : "${danach.fehler.trim()}"`); console.log(` Knopf steht auf : "${danach.knopf.trim()}"`); console.log(` Browsermeldungen : ${klagen.length ? klagen.join(" | ") : "keine"}`); const d = new DatabaseSync(process.env.WORKSPACE_DB); const n = d.prepare("SELECT COUNT(*) AS n FROM eintraege WHERE bereich = 'highlight'").get().n; d.close(); console.log(` Eintraege im Brett : ${n}`); } else { console.log("\n Die Seite ist gar nicht erst fertig geworden -- kein Absenden moeglich."); } /* ===================================================================== GEGENPROBE: Was passiert, wenn der Aufbau WIRKLICH stolpert? Ohne sie waere die Sicherung von heute eine Behauptung. Gepruefft wird am haerteren Fall: Ein Element, das `los()` sofort anfasst, wird aus der Seite entfernt, bevor das Skript laeuft. Vorher blieb die Seite danach fuer immer auf "wird geladen" stehen -- genau Filipes Bild. ===================================================================== */ { const s2 = await kontext.newPage(); const klagen2 = []; s2.on("pageerror", (f) => klagen2.push(String(f?.message || f))); /* DIE ZERSTOERUNG MUSS SICHER ANKOMMEN (berichtigt 05.10.2026). Der erste Versuch tauschte die Kennung im HTML unterwegs aus -- und kam nie an: Die Seite lud normal, der Browser meldete nichts, und die Messung schloss daraus "die Sicherung greift nicht". Eine Gegenprobe, die ihren eigenen Schaden nicht anrichtet, misst gar nichts. Jetzt wird `document.getElementById` ersetzt, bevor irgendein Skript laeuft -- genau das, was `$` in teilen.js benutzt. Damit wirft `los()` zuverlaessig an derselben Stelle wie bei einem echten Fehler. */ await s2.addInitScript(() => { const echt = document.getElementById.bind(document); document.getElementById = (id) => { if (id === "bretter") throw new Error("Probe: Element fehlt"); return echt(id); }; }); await s2.goto(`${BASIS}/workspace/teilen.html?url=${encodeURIComponent(LINK)}`, { waitUntil: "domcontentloaded" }); await s2.waitForTimeout(1200); const kaputt = await s2.evaluate(() => { const z = (id) => document.getElementById(id); return { ladezeileWeg: z("ladezeile")?.hidden === true, meldung: (z("fehler")?.textContent || "").trim(), eingabeDa: z("haendisch")?.hidden === false, knopfDa: z("uebernehmen") !== null, }; }); console.log("\n=== Gegenprobe: Seite absichtlich zerstoert ==="); console.log(` Ladezeile verschwunden : ${kaputt.ladezeileWeg}`); console.log(` Meldung sichtbar : "${kaputt.meldung.slice(0, 96)}"`); console.log(` Einfuegefeld offen : ${kaputt.eingabeDa}`); console.log(` (Browser meldete : ${klagen2.length ? klagen2[0].slice(0, 60) : "nichts"})`); console.log(kaputt.ladezeileWeg && kaputt.meldung && kaputt.eingabeDa ? " -> Die Seite sagt, dass etwas kaputt ist, und bietet den Weg von Hand an." : " -> SIE HAENGT IMMER NOCH. Die Sicherung greift nicht."); await s2.close(); } await kontext.close(); await browser.close(); try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ } process.exit(0);