feat: add emoji reactions, light/dark themes and rollout observability
Add a set of guest-facing and operator-facing features that make the deployment-strategy demos (recreate, rolling, canary) more visual: - Emoji reactions (heart/tada/thumbsup/smile): visitors react without an account, a first-party cookie keeps counts honest, the tally streams live to every tab over SSE. Backed by a new reactions table with an idempotent upsert; whitelisted emoji so the endpoint can't be abused. - Light & dark themes: the wall follows the OS preference, visitors can toggle it (persisted locally), and a moderator can force a theme for everyone from the admin console, pushed live over SSE. - Per-replica request counter: a middleware tallies requests answered by each replica; surfaced in /healthz, /api/info and the admin replica table. Under load-balancing the counts diverge, and a rolling update resets them one container at a time — the rollout made visible. - Rolling-update wave in the Chaos lab: bounces replicas one at a time (via the crash endpoint) while polling the replica table, so a training session can watch each replica reset and rejoin — what a rolling update looks like from the inside. - Admin console: reactions tile, per-replica Requests column, reaction tallies in moderation, and a wall-theme setting. - Favicon + /favicon.ico redirect. Tests cover reactions (idempotency, tallies, whitelist) and the theme setting round-trip. Akili-Task: tsk_xgg6sfuzkzymyc6gstr4 Akili-Agent: goldfish
This commit is contained in:
@@ -93,6 +93,7 @@ func (h *Handler) AdminOverview(c *okapi.Context) error {
|
||||
"healthy": !h.unhealthy.Load(),
|
||||
"started_at": h.started.UTC().Format(time.RFC3339),
|
||||
"uptime_s": int64(time.Since(h.started).Seconds()),
|
||||
"requests": h.requests.Load(),
|
||||
"go": runtime.Version(),
|
||||
"cpus": runtime.NumCPU(),
|
||||
"goroutines": runtime.NumGoroutine(),
|
||||
@@ -167,6 +168,14 @@ func (h *Handler) AdminSaveSettings(c *okapi.Context) error {
|
||||
if len(st.Banner) > maxBannerLen {
|
||||
return c.JSON(http.StatusBadRequest, okapi.M{"error": "banner is too long (max 200 characters)"})
|
||||
}
|
||||
switch st.Theme {
|
||||
case "light", "dark":
|
||||
// forced appearance for every visitor
|
||||
case "", "system":
|
||||
st.Theme = "" // follow each visitor's preference
|
||||
default:
|
||||
return c.JSON(http.StatusBadRequest, okapi.M{"error": "theme must be system, light or dark"})
|
||||
}
|
||||
if err := h.store.SaveSettings(c.Request().Context(), st); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, okapi.M{"error": "could not save settings"})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user