Automatischer öffentlicher Start um 21 Uhr + Countdown auf der Zugangsseite
Nutzer-Wunsch 21.08.2026: "ich will dass du auch ein countdown zu der seite hinzufügst wo wir den zugangscode eingeben müssen. den um 21h heute abend geht die seite live. kannst du das sogar so anpassen dass es automatisch läuft?" server/gate.js: - Neue Einstellung SITE_PUBLIC_LAUNCH_AT (ISO-Zeitstempel mit Zeitzone). Ab diesem Moment lässt gateMiddleware ausnahmslos jeden durch -- ganz ohne Neustart oder manuellen Eingriff, weil jede Anfrage die aktuelle Serverzeit live neu prüft. Die Freischaltung "passiert" also von selbst in der Sekunde, in der die Uhrzeit erreicht wird. Vorher bleiben die Zugangscodes unverändert nötig, damit das Team schon vorher rein kann. Fail-safe statt fail-open geprüft: ein kaputter/unparsbarer Zeitwert (z.B. Tippfehler in der .env) lässt die Schranke aktiv, statt die Seite versehentlich für alle zu öffnen. - Neuer öffentlicher Endpunkt GET /gate-launch-info (immer erreichbar, auch ohne gültige Sitzung) liefert launchAt/isLive/serverTime für die Countdown-Anzeige im Frontend. gate.html: - Neue Countdown-Box zwischen Titel-Karte und den Zugangscode-Kacheln (bleibt unsichtbar, solange kein Starttermin konfiguriert ist). Rechnet auf der SERVERZEIT statt der eigenen Uhr (einmaliger Zeit-Abgleich beim Laden), damit eine falsch gehende Besucher-Uhr weder zu früh noch zu spät zählt. Bei Erreichen von Null folgt ein letzter Abgleich mit dem Server, bevor automatisch zur Zielseite weitergeleitet wird -- kein Klick, kein Neuladen nötig. - Zugangscode-Kacheln (Dogi/VanVan/Diene) bleiben während des Countdowns unverändert nutzbar. Getestet: 18 Middleware-Tests (inkl. Fail-safe bei kaputtem Zeitwert, weiterhin funktionierender Zugangscode vor dem Start) + 10 Playwright- Tests der Countdown-Oberfläche (Anzeige, Format, automatischer Sprung bei Ablauf, sofortige Weiterleitung falls schon live, stiller Fallback bei Netzwerkfehler). Zusätzlich alle 32 echten Seiten auf PC-Installierbarkeit geprüft (Manifest, Icons, Service Worker, Install-Knopf, echter Install-Klick-Ablauf simuliert) -- keine Probleme gefunden. Cache-Busting-Version auf 20260821s erhöht. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -26,8 +26,8 @@
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="DogFather" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821r" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821r" />
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20260821s" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821s" />
|
||||
<style>
|
||||
/* =====================================================================
|
||||
Zugangsseite — bewusst kein braves Standard-Login-Formular, sondern
|
||||
@@ -246,6 +246,37 @@
|
||||
.gate-cards { flex-direction: column; align-items: center; }
|
||||
.gate-card { max-width: 300px; width: 100%; }
|
||||
}
|
||||
|
||||
/* ---------- Countdown zum öffentlichen Start (21.08.2026) ----------
|
||||
Nutzer-Wunsch: "ich will dass du auch ein countdown zu der seite
|
||||
hinzufügst wo wir den zugangscode eingeben müssen. den um 21h heute
|
||||
abend geht die seite live." Eigene, ruhige Optik statt einer reinen
|
||||
Zahlenanzeige — passt zur restlichen Gold/Aurora-Bühne der Seite.
|
||||
[hidden] per Default: erscheint erst, wenn server/gate.js per
|
||||
/gate-launch-info wirklich einen Start-Zeitpunkt meldet (siehe Skript
|
||||
unten) — ohne das bleibt die Seite unverändert wie vorher. */
|
||||
.gate-countdown {
|
||||
position: relative; z-index: 2; width: 100%; text-align: center;
|
||||
margin-top: 1.3rem; padding: 1.1rem 1.4rem;
|
||||
border-radius: 16px; border: 1.5px solid rgba(212,175,55,.35);
|
||||
background: linear-gradient(180deg, rgba(20,23,29,.85), rgba(10,12,15,.9));
|
||||
box-shadow: 0 20px 50px -25px rgba(0,0,0,.7);
|
||||
}
|
||||
.gate-countdown[hidden] { display: none; }
|
||||
.gate-countdown-label {
|
||||
margin: 0 0 .5rem; font-size: .78rem; letter-spacing: .1em; text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.gate-countdown-zeit {
|
||||
margin: 0; font-family: var(--font-head); font-weight: 800; letter-spacing: .04em;
|
||||
font-size: clamp(1.7rem, 6vw, 2.4rem); font-variant-numeric: tabular-nums;
|
||||
background: linear-gradient(135deg, #fff, #ffe9a8 55%, #d4af37);
|
||||
-webkit-background-clip: text; background-clip: text; color: transparent;
|
||||
animation: gate-countdown-puls 2.4s ease-in-out infinite;
|
||||
}
|
||||
@keyframes gate-countdown-puls { 0%,100% { opacity: .92; } 50% { opacity: 1; } }
|
||||
@media (prefers-reduced-motion: reduce) { .gate-countdown-zeit { animation: none; } }
|
||||
.gate-countdown-hinweis { margin: .55rem 0 0; font-size: .82rem; color: var(--text-muted); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -269,6 +300,15 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Countdown zum öffentlichen Start (21.08.2026) — bleibt leer/unsichtbar,
|
||||
solange server/gate.js keinen SITE_PUBLIC_LAUNCH_AT meldet (siehe
|
||||
/gate-launch-info im Skript unten). -->
|
||||
<div class="gate-countdown" id="gate-countdown" hidden>
|
||||
<p class="gate-countdown-label">🎉 Startet automatisch in</p>
|
||||
<p class="gate-countdown-zeit" id="gate-countdown-zeit">--:--:--</p>
|
||||
<p class="gate-countdown-hinweis">Kein Neuladen nötig — die Seite öffnet sich von selbst.</p>
|
||||
</div>
|
||||
|
||||
<!-- Nutzer-Wunsch 07.08.2026: statt einem gemeinsamen Eingabefeld für
|
||||
Dogi/VanVan jetzt 2 getrennte Kacheln, gleiches Muster wie schon in
|
||||
verwaltung.html — beide Formulare rufen weiterhin denselben
|
||||
@@ -405,6 +445,84 @@
|
||||
bindeGateKachel("gate-form-vanvan", "gate-code-vanvan", "gate-fehler-vanvan");
|
||||
bindeGateKachel("gate-form-diene", "gate-code-diene", "gate-fehler-diene");
|
||||
|
||||
/* ---------- Countdown zum öffentlichen Start (21.08.2026) ----------
|
||||
Nutzer-Wunsch: "um 21h heute abend geht die seite live. kannst du das
|
||||
sogar so anpassen dass es automatisch läuft?" -- server/gate.js meldet
|
||||
über /gate-launch-info den echten Start-Zeitpunkt (SITE_PUBLIC_LAUNCH_AT
|
||||
in der .env) und lässt ab genau diesem Moment jeden ohne Code durch.
|
||||
Diese Uhr hier ist rein die SICHTBARE Anzeige dafür + der automatische
|
||||
Sprung zur Zielseite, sobald es soweit ist -- die eigentliche
|
||||
Freischaltung passiert unabhängig davon serverseitig.
|
||||
|
||||
Läuft bewusst auf der SERVERZEIT, nicht der eigenen Uhr: viele Handys/
|
||||
PCs gehen ein paar Minuten falsch, das würde sonst entweder zu früh
|
||||
"00:00:00" zeigen (und dann an der noch verschlossenen Schranke
|
||||
scheitern) oder unnötig lange nachzählen. offsetMs wird einmal beim
|
||||
Laden bestimmt (Server-Zeit minus eigene Zeit) und auf die eigene,
|
||||
lokal tickende Uhr addiert -- kein Dauer-Nachfragen nötig, nur beim
|
||||
Erreichen von Null ein letzter Abgleich als Sicherheitsnetz. */
|
||||
const countdownBox = document.getElementById("gate-countdown");
|
||||
const countdownZeit = document.getElementById("gate-countdown-zeit");
|
||||
|
||||
function formatiereCountdown(ms) {
|
||||
const gesamtSekunden = Math.max(0, Math.round(ms / 1000));
|
||||
const std = Math.floor(gesamtSekunden / 3600);
|
||||
const min = Math.floor((gesamtSekunden % 3600) / 60);
|
||||
const sek = gesamtSekunden % 60;
|
||||
const zweistellig = (n) => String(n).padStart(2, "0");
|
||||
return `${zweistellig(std)}:${zweistellig(min)}:${zweistellig(sek)}`;
|
||||
}
|
||||
|
||||
async function holeLaunchInfo() {
|
||||
try {
|
||||
const res = await fetch("/gate-launch-info", { cache: "no-store" });
|
||||
const data = await res.json();
|
||||
return data && data.ok ? data : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (countdownBox && countdownZeit) {
|
||||
async function richteCountdownEin(info) {
|
||||
const launchAtMs = new Date(info.launchAt).getTime();
|
||||
const offsetMs = new Date(info.serverTime).getTime() - Date.now();
|
||||
countdownBox.hidden = false;
|
||||
|
||||
const timerId = setInterval(async () => {
|
||||
const serverJetzt = Date.now() + offsetMs;
|
||||
const rest = launchAtMs - serverJetzt;
|
||||
if (rest > 0) {
|
||||
countdownZeit.textContent = formatiereCountdown(rest);
|
||||
return;
|
||||
}
|
||||
countdownZeit.textContent = "00:00:00";
|
||||
clearInterval(timerId);
|
||||
// Letzter Abgleich mit dem Server statt blind der eigenen Uhr zu
|
||||
// vertrauen -- erst wenn ER bestätigt "live", wird weitergeleitet.
|
||||
// Sagt er "noch nicht" (z.B. Uhr wich ein paar Sekunden ab, oder
|
||||
// der Zeitpunkt wurde inzwischen verschoben), richtet sich der
|
||||
// Countdown mit den frischen Werten einfach neu ein, statt
|
||||
// stumm bei "00:00:00" stehen zu bleiben.
|
||||
const bestaetigung = await holeLaunchInfo();
|
||||
if (bestaetigung && bestaetigung.isLive) location.href = next;
|
||||
else if (bestaetigung && bestaetigung.launchAt) richteCountdownEin(bestaetigung);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
(async function starteCountdown() {
|
||||
const info = await holeLaunchInfo();
|
||||
if (!info || !info.launchAt) return; // kein Starttermin gesetzt -> Box bleibt versteckt
|
||||
|
||||
if (info.isLive) {
|
||||
location.href = next; // schon live (z.B. Seite kurz nach dem Start neu geöffnet)
|
||||
return;
|
||||
}
|
||||
|
||||
richteCountdownEin(info);
|
||||
})();
|
||||
}
|
||||
|
||||
/* Service-Worker-Registrierung — auf allen anderen Seiten macht das main.js, diese Seite
|
||||
ist aber bewusst eigenstaendig und laedt main.js nicht. Ohne registrierten Service
|
||||
Worker bietet Chrome hier gar kein "App installieren" an (siehe Kommentar im Kopfbereich).
|
||||
|
||||
Reference in New Issue
Block a user