/* ===================================================================== DER WEG NACH DRAUSSEN ZU TIKTOK — gemeinsam genutzt (07.10.2026) Diese Datei ist ein VERSCHIEBEN, keine Neuerfindung. `istTikTok` und `holeMitFrist` standen bis heute in `workspace-video.js` und haben sich dort bewährt — inklusive der Reparatur vom 05.10.2026, bei der auffiel, dass die Frist nur den Anfang der Anfrage bewachte. Ab jetzt holt auch die Kampagnen-Kachel eine Seite von TikTok. Zwei Fassungen derselben Regel wären der Fehler, den CLAUDE.md am 11.09.2026 beschreibt: eine abgeschriebene Liste, die auseinander- läuft. Gerade bei `istTikTok` wäre das gefährlich — es ist die eine Stelle, die entscheidet, welchen fremden Rechner unser Server überhaupt anfragt. Die zweite Fassung würde beim nächsten Fund vergessen. --------------------------------------------------------------------- WAS SICH BEIM VERSCHIEBEN GEÄNDERT HAT — und nur das 1. `holeMitFrist` nimmt die Frist jetzt als Zusatz entgegen, mit 8000 ms als Vorgabe. Das Video bleibt damit Zeichen für Zeichen beim alten Verhalten; die Kampagnenseite braucht mehr (1,1 MB statt einer kleinen Auskunft) und sagt das am Aufrufort, nicht über eine zweite Konstante irgendwo. 2. `istTikTok` erkennt die Prüfumgebung jetzt an BEIDEN Umstellschaltern — `TIKTOK_OEMBED_BASIS` (Video) und `TIKTOK_KAMPAGNE_BASIS` (Kampagne). Ohne das zweite müsste die Kampagnenprüfung die echte Regel umgehen und prüfte damit eine andere als die, die im Betrieb gilt. Sonst nichts. Kein neues Verhalten, keine neue Grenze, keine neue Entscheidung. Wer hier etwas vermisst (Umleitungen Station für Station, Größengrenze beim Lesen), findet es in Etappe 2 — nicht hier, und nicht halb. --------------------------------------------------------------------- KEINE DATENBANK, KEIN EXPRESS Absichtlich: Diese Datei lässt sich in einer Prüfung einzeln einbinden, ohne dass ein Server hochfährt. Dasselbe Argument wie bei `helfer-tag.mjs`. ===================================================================== */ /* GRENZEN FÜR DEN WEG NACH DRAUSSEN. Ein fremder Dienst, der nicht antwortet, darf unsere Anfrage nicht mit sich ziehen -- genau daran ist in diesem Haus schon ein ganzer Pruefdurchlauf drei Stunden lang haengengeblieben. */ export const FRIST_MS = 8000; /** Ist das ueberhaupt eine TikTok-Adresse? Keine Zeichenkettensuche, * sondern der Rechnername -- "tiktok.com.beispiel.de" enthaelt * "tiktok.com" und gehoert jemand anderem. */ export function istTikTok(roh) { try { const u = new URL(String(roh)); const h = u.hostname.toLowerCase(); /* NUR IN EINER PRUEFUNG: Dann liegt der nachgebaute Dienst auf 127.0.0.1, und die Adresse des "Videos" auch. Ohne diese Zeile muesste die Pruefung die echte Regel umgehen -- und pruefte damit eine andere als die, die im Betrieb gilt. */ if ((process.env.TIKTOK_OEMBED_BASIS || process.env.TIKTOK_KAMPAGNE_BASIS) && (h === "127.0.0.1" || h === "localhost")) { return true; } if (u.protocol !== "https:") return false; return h === "tiktok.com" || h.endsWith(".tiktok.com"); } catch { return false; } } /** Holt etwas von draussen -- MIT Frist ueber den ganzen Vorgang. * * DIE ALTE FASSUNG HAT NUR DEN ANFANG BEWACHT (behoben 05.10.2026). * * Sie gab die Antwort zurueck, sobald die Kopfzeilen da waren, und * raeumte im `finally` die Uhr weg. Was danach kam -- `a.json()` beim * Abruf der Videodaten, `a.arrayBuffer()` beim Vorschaubild -- lief * OHNE jede Frist. Bleibt der fremde Server mitten im Koerper * stehen, wartet unsere Anfrage fuer immer. * * Genau das sieht man als "der Knopf bleibt auf `wird geholt ...` * stehen": keine Fehlermeldung, kein Eintrag, kein Protokolleintrag * -- weil der Vorgang nie endet. Und es braucht keine Aenderung bei * uns, um anzufangen: Es haengt am fremden Netz. "Es ging doch immer * bis jetzt" ist bei so einem Fehler die Regel, nicht die Ausnahme. * * Der Koerper wird deshalb GELESEN, solange die Uhr laeuft, und erst * danach abgeraeumt. Die Frist gilt damit fuer alles zusammen. */ export async function holeMitFrist(adresse, kopf = {}, { frist = FRIST_MS } = {}) { const abbruch = new AbortController(); const uhr = setTimeout(() => abbruch.abort(), frist); try { const a = await fetch(adresse, { signal: abbruch.signal, headers: kopf, redirect: "follow" }); /* Bei einem Fehlschlag interessiert der Koerper nicht -- aber er muss trotzdem weg, sonst bleibt die Verbindung offen. */ const roh = Buffer.from(await a.arrayBuffer()); return { ok: a.ok, status: a.status, headers: a.headers, roh, text: () => roh.toString("utf8"), }; } finally { clearTimeout(uhr); } }