Header: Statuszeile "Ohne Datenverlust seit n Tagen" + Kennzahlen zurückholen

Schlanke Wiederbelebung des alten Uptime-Schriftzugs aus der Python-Version,
jetzt als kleine Zeile direkt unter dem Titel: Tage seit fixem Startpunkt
(13.12.2025, Kontinuität) plus aktuelle Profil- und Beitragszahlen.

- /stats: neuer öffentlicher Endpunkt (COUNT user / COUNT entry WHERE deleted=0)
- app.js: renderStats() rechnet die Tage und füllt #stats per textContent
- index.html: <p id="stats" class="tagline"> unter der h1
- app.css: kleine graue .tagline-Zeile

Kein Inline-JS/CSS -> strikte CSP bleibt unangetastet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 08:46:05 +02:00
parent 21911e84e0
commit d9028a8400
5 changed files with 42 additions and 0 deletions
+1
View File
@@ -87,6 +87,7 @@ func routes() http.Handler {
r.Get("/auth/logout", handleLogout) r.Get("/auth/logout", handleLogout)
r.Get("/auth/sessioninfo", handleSessionInfo) r.Get("/auth/sessioninfo", handleSessionInfo)
r.Get("/auth/headerbar", handleHeaderbar) r.Get("/auth/headerbar", handleHeaderbar)
r.Get("/stats", handleStats)
// --- Entries --- // --- Entries ---
r.Get("/entry/feed/{page}", handleFeed) r.Get("/entry/feed/{page}", handleFeed)
+12
View File
@@ -70,6 +70,18 @@ func handleUserPage(w http.ResponseWriter, r *http.Request) {
}) })
} }
// handleStats liefert die Kennzahlen für die Statuszeile unter dem Titel:
// registrierte Nutzer und nicht-gelöschte Beiträge.
func handleStats(w http.ResponseWriter, r *http.Request) {
var users, entries int64
db.QueryRow(`SELECT COUNT(*) FROM user`).Scan(&users)
db.QueryRow(`SELECT COUNT(*) FROM entry WHERE deleted = 0`).Scan(&entries)
writeJSON(w, http.StatusOK, map[string]any{
"users": users,
"entries": entries,
})
}
func handleUserInfo(w http.ResponseWriter, r *http.Request) { func handleUserInfo(w http.ResponseWriter, r *http.Request) {
uid := uidFromContext(r.Context()) uid := uidFromContext(r.Context())
+7
View File
@@ -80,6 +80,13 @@ h2 {
padding: 0 0.3rem; padding: 0 0.3rem;
} }
/* Kleine Statuszeile direkt unter dem Titel (Tage ohne Datenverlust + Kennzahlen). */
.tagline {
margin: 0.2rem 0 0;
font-size: 0.85rem;
color: gray;
}
#headerbar { #headerbar {
background-color: white; background-color: white;
text-align: center; text-align: center;
+1
View File
@@ -14,6 +14,7 @@
<a href="/static/docs/beitragsordnung.html">Beitragsordnung</a> <a href="/static/docs/beitragsordnung.html">Beitragsordnung</a>
</nav> </nav>
<a href="/"><h1>Kontrollverlust</h1></a> <a href="/"><h1>Kontrollverlust</h1></a>
<p id="stats" class="tagline"></p>
<div id="headerbar"></div> <div id="headerbar"></div>
</header> </header>
+21
View File
@@ -95,5 +95,26 @@ document.getElementById("entry-form").addEventListener("submit", async (e) => {
} }
}); });
// --- Statuszeile unter dem Titel ---
// Tage ohne Datenverlust (fester Startpunkt, Kontinuität seit der alten
// Version) plus aktuelle Kennzahlen aus /stats.
const dataLossStart = new Date("2025-12-13T12:44:00").getTime();
function plural(n, one, many) {
return `${n} ${n === 1 ? one : many}`;
}
async function renderStats() {
const el = document.getElementById("stats");
if (!el) return;
const days = Math.floor((Date.now() - dataLossStart) / 86400000);
const s = await api.get("/stats");
el.textContent =
`Ohne Datenverlust seit ${plural(days, "Tag", "Tagen")} · ` +
`${plural(s.users ?? 0, "Profil", "Profile")} · ` +
`${plural(s.entries ?? 0, "Beitrag", "Beiträge")}`;
}
// --- Start --- // --- Start ---
refreshHeader(); refreshHeader();
renderStats();