Server (Website, Admin-Bereich, Player-API) und Player (raspotify + Controller) mit Install-Skripten für Debian-Systeme. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
74 lines
2.5 KiB
JavaScript
74 lines
2.5 KiB
JavaScript
// Audiofy – Cloudflare Turnstile (Bot-Schutz). Lädt das Widget nur, wenn der Server einen Site-Key liefert.
|
||
"use strict";
|
||
|
||
window.AudiofyTurnstile = (() => {
|
||
let ladeVersprechen = null;
|
||
|
||
function skriptLaden() {
|
||
if (!ladeVersprechen) {
|
||
ladeVersprechen = new Promise((fertig, fehler) => {
|
||
const s = document.createElement("script");
|
||
s.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
|
||
s.async = true;
|
||
s.onload = () => fertig(window.turnstile);
|
||
s.onerror = () => fehler(new Error("Turnstile konnte nicht geladen werden"));
|
||
document.head.append(s);
|
||
});
|
||
}
|
||
return ladeVersprechen;
|
||
}
|
||
|
||
const LEER = { token: async () => null, reset() {}, bereit: () => true };
|
||
|
||
// Liefert {token(): Promise<string|null>, reset(), bereit()} – ohne Site-Key ein "leerer" Schutz.
|
||
async function einrichten(container, aktion) {
|
||
// Site-Key und Cloudflare-Skript gleichzeitig holen (spart einen Umlauf)
|
||
const skript = skriptLaden().catch(() => null);
|
||
let siteKey = null;
|
||
try {
|
||
siteKey = (await (await fetch("/api/turnstile", { credentials: "same-origin" })).json()).site_key;
|
||
} catch (_) { /* ohne Site-Key weiter */ }
|
||
if (!siteKey) return LEER;
|
||
|
||
let aktuell = null;
|
||
let warten = [];
|
||
const melden = (wert) => { aktuell = wert; warten.forEach((f) => f(wert)); warten = []; };
|
||
let ts = null;
|
||
let id = null;
|
||
try {
|
||
ts = await skript;
|
||
if (!ts) return LEER; // Skript nicht ladbar → Server entscheidet
|
||
id = ts.render(container, {
|
||
sitekey: siteKey,
|
||
action: aktion,
|
||
appearance: "interaction-only", // nur sichtbar, wenn Cloudflare eine Bestätigung braucht
|
||
language: "de",
|
||
callback: (t) => melden(t),
|
||
"expired-callback": () => { aktuell = null; },
|
||
"error-callback": () => { melden(null); },
|
||
});
|
||
} catch (_) {
|
||
return LEER; // Server entscheidet dann
|
||
}
|
||
|
||
return {
|
||
bereit: () => !!aktuell,
|
||
// wartet bis zu 15 s auf ein Token
|
||
token() {
|
||
if (aktuell) return Promise.resolve(aktuell);
|
||
return new Promise((fertig) => {
|
||
warten.push(fertig);
|
||
setTimeout(() => fertig(null), 15000);
|
||
});
|
||
},
|
||
// jedes Token gilt nur einmal → nach jeder Anfrage ein neues holen
|
||
reset() {
|
||
aktuell = null;
|
||
try { ts.reset(id); } catch (_) { /* egal */ }
|
||
},
|
||
};
|
||
}
|
||
|
||
return { einrichten };
|
||
})();
|