diff --git a/assets/css/main.css b/assets/css/main.css index b1fe270..f0232e2 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -425,6 +425,29 @@ footer.site-footer { font-size: .8rem; color: var(--text-muted); } +/* ---------- Live-Status-Punkt ---------- */ +.live-dot { + display: inline-block; + width: 10px; + height: 10px; + border-radius: 50%; + background: #6b7280; /* grau = aktuell offline */ + margin-right: .45rem; + vertical-align: middle; +} +.live-dot.is-live { + background: #22c55e; /* grün = live */ + animation: live-pulse 1.6s infinite; +} +@keyframes live-pulse { + 0% { box-shadow: 0 0 0 0 rgba(34, 197, 94, .55); } + 70% { box-shadow: 0 0 0 10px rgba(34, 197, 94, 0); } + 100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); } +} +@media (prefers-reduced-motion: reduce) { + .live-dot.is-live { animation: none; } +} + /* ---------- Badges / Hierarchy ---------- */ .badge { display: inline-block; font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; padding: .2rem .6rem; border-radius: 999px; background: var(--bg-alt); border: 1px solid var(--border); color: var(--text-muted); } .badge.rank-1 { border-color: var(--accent); color: var(--accent); } diff --git a/cloudflare-worker/src/worker.js b/cloudflare-worker/src/worker.js index 84d37db..0c87478 100644 --- a/cloudflare-worker/src/worker.js +++ b/cloudflare-worker/src/worker.js @@ -24,6 +24,8 @@ POST /admin/roles/list { ownerCode } -> Helfer-Codes auflisten (nur Owner) POST /admin/roles/create { ownerCode, role, label } -> neuen Helfer-Code erzeugen (nur Owner) POST /admin/roles/revoke { ownerCode, keyId } -> Helfer-Code entziehen (nur Owner) + GET /live-status -> öffentlich: {live, updatedAt} + POST /live-status/set { ownerCode, live } -> Live-Status umschalten (nur Owner) GET /health -> einfacher Check ob der Worker läuft Secrets/Bindings (siehe wrangler.toml + README.md): @@ -33,6 +35,7 @@ const ALLOWED_TYPES = ["creator", "scout", "kooperation"]; const ROLE_KEY_PREFIX = "role:"; +const LIVE_STATUS_KEY = "meta:live"; function corsHeaders() { // TODO: sobald die Domain final steht, statt "*" die echte Domain eintragen @@ -40,7 +43,7 @@ function corsHeaders() { // an die API senden kann. return { "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "POST, OPTIONS", + "Access-Control-Allow-Methods": "GET, POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type", }; } @@ -215,6 +218,30 @@ async function handleRolesRevoke(request, env) { return json({ ok: true }); } +async function handleLiveStatusGet(env) { + const raw = await env.BEWERBUNGEN.get(LIVE_STATUS_KEY); + const data = raw ? JSON.parse(raw) : { live: false, updatedAt: null }; + return json({ ok: true, ...data }); +} + +async function handleLiveStatusSet(request, env) { + let body; + try { + body = await request.json(); + } catch { + return json({ ok: false, error: "Ungültiges JSON." }, 400); + } + if (!(await requireOwner(body, env))) { + return json({ ok: false, error: "Nur Dogi darf den Live-Status ändern." }, 401); + } + + const live = !!(body && body.live); + const record = { live, updatedAt: new Date().toISOString() }; + await env.BEWERBUNGEN.put(LIVE_STATUS_KEY, JSON.stringify(record)); + + return json({ ok: true, ...record }); +} + export default { async fetch(request, env) { const url = new URL(request.url); @@ -247,6 +274,14 @@ export default { return handleRolesRevoke(request, env); } + if (url.pathname === "/live-status" && request.method === "GET") { + return handleLiveStatusGet(env); + } + + if (url.pathname === "/live-status/set" && request.method === "POST") { + return handleLiveStatusSet(request, env); + } + return json({ ok: false, error: "Not found." }, 404); }, }; diff --git a/dogfather.html b/dogfather.html index df80bb9..ffe337a 100644 --- a/dogfather.html +++ b/dogfather.html @@ -82,7 +82,7 @@
Eigene Songs unter dem Namen „Hasidog" auf Spotify.
Eigene Songs unter dem Namen „Hasidog" auf Spotify.
Tassen, Decken, Popsockets, Schlüsselanhänger, Sticker.
Ein Buch über Hasidog & Casper.
HasiDog ist mehr als ein Spitzname — es ist die verspielte, familiäre Marke rund um Dogfathers Community. Unter dem Namen „Hasidog" erscheint auch die eigene Musik auf Spotify.
+HasiDog ist mehr als ein Spitzname — es ist die verspielte, familiäre Marke rund um Dogfathers Community. Unter dem Namen „Hasidog" erscheint auch die eigene Musik auf Spotify.
Songs unter dem Namen „Hasidog" auf Spotify.
Songs unter dem Namen „Hasidog" auf Spotify.
Der Husky, der zum Gesicht der HasiDog-Welt wurde — eigene Seite: Casper.
„Hasidog & Casper" — die Geschichte in Buchform.
Drei Welten, eine Familie: Team Dogi.
Dogfather geht jeden Tag ab 20 Uhr live — oft schon tagsüber, zu wechselnden Zeiten. Streamplan ansehen →