Redis-backed visit tracking (src/lib/visit-tracking.ts): daily SADD
sets keyed "visits:YYYY-MM-DD", same fail-open idiom as the existing
rate-limit.ts. Recorded once per authenticated (app) layout render —
SADD is idempotent so no client-side dedup is needed. DAU = today's
SCARD, MAU = SUNION across the trailing 30 daily sets.
Dashboard additions:
- 6 new stat cards: DAU, MAU, shadowban count, blacklist entry count,
verifications open/closed.
- 3 new time-series charts (reusing the existing TimeSeriesChart/
GranularityToggle components): visits, comments, and reactions
over time. Comments/reactions are plain Postgres bucket queries
cloned from the existing getPostsOverTime pattern; visits reroutes
through the new Redis helpers per bucket.
This closes out Gitea #10 Phase 3 (hide-without-delete, shadowban,
blacklist, and now analytics — all four parts shipped).