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:
@@ -85,7 +85,7 @@ func runServer(cmd *okapicli.Command, cfg Config) error {
|
||||
}
|
||||
|
||||
host, _ := os.Hostname()
|
||||
broker := NewBroker(host, cfg.Version)
|
||||
broker := NewBroker(host, cfg.Version, nil)
|
||||
if cfg.RedisURL != "" {
|
||||
if err := broker.UseRedis(cfg.RedisURL); err != nil {
|
||||
return err
|
||||
@@ -106,6 +106,7 @@ func runServer(cmd *okapicli.Command, cfg Config) error {
|
||||
debug: cfg.DebugEndpoints,
|
||||
started: time.Now(),
|
||||
}
|
||||
broker.requests = h.requests.Load
|
||||
if !h.auth.Enabled() {
|
||||
logger.Warn("admin console disabled: set ADMIN_TOKEN to enable /admin")
|
||||
}
|
||||
@@ -116,7 +117,7 @@ func runServer(cmd *okapicli.Command, cfg Config) error {
|
||||
app := cmd.Okapi()
|
||||
app.WithPort(cmd.GetInt("port"))
|
||||
app.WithDebug()
|
||||
app.Use(okapi.LoggerMiddleware, okapi.RequestID())
|
||||
app.Use(okapi.LoggerMiddleware, okapi.RequestID(), h.countRequests)
|
||||
registerRoutes(app, h)
|
||||
|
||||
return cmd.CLI().RunServer(&okapicli.RunOptions{
|
||||
@@ -144,6 +145,8 @@ func registerRoutes(app *okapi.Okapi, h *Handler) {
|
||||
api.Get("/settings", h.PublicSettings)
|
||||
api.Get("/entries", h.ListEntries)
|
||||
api.Post("/entries", h.CreateEntry)
|
||||
api.Post("/entries/{id:int}/reactions", h.React)
|
||||
api.Delete("/entries/{id:int}/reactions", h.Unreact)
|
||||
api.Get("/stream", h.Stream)
|
||||
|
||||
api.Get("/admin/session", h.AdminSession)
|
||||
@@ -176,6 +179,12 @@ func registerRoutes(app *okapi.Okapi, h *Handler) {
|
||||
return c.Data(http.StatusOK, "text/html; charset=utf-8", adminPage)
|
||||
})
|
||||
|
||||
// Browsers ask for /favicon.ico on their own; the logo serves as the icon.
|
||||
app.Get("/favicon.ico", func(c *okapi.Context) error {
|
||||
c.Redirect(http.StatusFound, "/badges/icon.svg")
|
||||
return nil
|
||||
})
|
||||
|
||||
// Okapi serves real files directly and falls back to index.html for any
|
||||
// other path so the client-side router can take over.
|
||||
app.WebFS("/", webFS, okapi.WebConfig{Root: "web"})
|
||||
|
||||
Reference in New Issue
Block a user