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:
@@ -15,7 +15,7 @@ handy for logs, events and scaling.
|
||||
| 3 | Secrets + admin console | secret env vars |
|
||||
| 4 | Scale out → broken live updates | replicas |
|
||||
| 5 | Attach Redis → fixed | managed Redis, env prefix |
|
||||
| 6 | Break it on purpose | restarts, health gating, alerts, logs, analytics |
|
||||
| 6 | Break it on purpose (+ rolling wave) | restarts, health gating, alerts, logs, analytics |
|
||||
| 7 | Canary v1 → v2 | canary weights, promote |
|
||||
| 8 | Scheduled cleanup | cron jobs |
|
||||
|
||||
@@ -55,23 +55,24 @@ docker push <registry>/guestbook:1.0.0 && docker push <registry>/guestbook:2.0.0
|
||||
1. App → **Environment** → add `ADMIN_TOKEN` = your token, marked **secret**. Add `DEBUG_ENDPOINTS=true` too (used in act 6).
|
||||
2. Redeploy, open `/admin` and sign in with the token.
|
||||
3. Tour it:
|
||||
- **Overview**: signatures per day, replicas, and *this replica*'s runtime (host, DB, uptime, memory).
|
||||
- **Moderation**: each signature shows the signer's IP, forwarded by the gateway. Click one to filter by it. Pin a signature. It jumps to the top of every open wall, live. Hide one and it disappears.
|
||||
- **Settings**: set a banner ("Welcome to the Miabi training 👋"), then toggle **Pause signing**. The wall goes read-only instantly in every tab.
|
||||
- **Overview**: signatures per day, total reactions, replicas (with each one's **request counter**), and *this replica*'s runtime (host, DB, uptime, memory).
|
||||
- **Moderation**: each signature shows the signer's IP, forwarded by the gateway, plus its reaction tally. Click the IP to filter by it. Pin a signature. It jumps to the top of every open wall, live. Hide one and it disappears.
|
||||
- **Settings**: set a banner ("Welcome to the Miabi training 👋"), pick a **wall theme** (force light or dark — every open tab flips instantly), then toggle **Pause signing**. The wall goes read-only instantly in every tab.
|
||||
|
||||
> Talking point: the token never appears in the app's config in plain text.
|
||||
> Rotating it (edit + redeploy) signs every admin out, because sessions are
|
||||
> HMAC-signed with it.
|
||||
|
||||
Ask the audience to open the wall on their phones and sign it. The
|
||||
**online** counter climbs live.
|
||||
**online** counter climbs live. Have them tap the ❤️ / 🎉 reactions — the
|
||||
tallies move on every screen at once, with no refresh.
|
||||
|
||||
## 4. Scale out, and watch it break
|
||||
|
||||
1. App → **Replicas** → scale to **3**.
|
||||
2. In the admin **Overview**, the *served by* chip changes as you refresh, but the **Replicas** table only ever lists one host. The hint says *in-process broker*.
|
||||
3. Open the wall in two browsers (or phones) until their clock cards show **different hosts**. Sign in one: **the other doesn't update.** Each replica only pushes to its own SSE clients.
|
||||
4. Also note the `via replica-x` tag on new signatures: each one records which replica accepted it.
|
||||
3. Open the wall in two browsers (or phones) until their clock cards show **different hosts**. Sign in one: **the other doesn't update.** Each replica only pushes to its own SSE clients. Reactions are the same: a ❤️ on one host never shows on the other.
|
||||
4. Also note the `via replica-x` tag on new signatures: each one records which replica accepted it. And `curl -s https://<domain>/healthz` a few times — the `requests` counter climbs on whichever replica answered, so you can watch the load-balancer spread traffic even though live updates stay broken.
|
||||
|
||||
> Talking point: this is the classic "works on one box" bug. Stateless apps
|
||||
> need shared state for fan-out. Scaling is a platform feature; being
|
||||
@@ -81,8 +82,8 @@ Ask the audience to open the wall on their phones and sign it. The
|
||||
|
||||
1. App → **Databases** → attach the Redis with prefix **`REDIS`**. Miabi injects `REDIS_DATABASE_URL`, which the app reads (so does `REDIS_URL`).
|
||||
2. Redeploy. Startup logs show `live updates ready broker=redis`.
|
||||
3. Repeat the two-browser test: signatures now appear everywhere. The **Replicas** table lists all 3 hosts with their client counts, and the wall header says *3 replicas*.
|
||||
4. **Chaos lab → Traffic probe** → *Run probe*: the bars show requests spread across the three hosts.
|
||||
3. Repeat the two-browser test: signatures **and reactions** now appear everywhere. The **Replicas** table lists all 3 hosts with their client counts **and request counters**, and the wall header says *3 replicas*.
|
||||
4. **Chaos lab → Traffic probe** → *Run probe*: the bars show requests spread across the three hosts. Compare with the per-replica `requests` counters in the table — they tell the same story server-side.
|
||||
|
||||
## 6. Break it on purpose
|
||||
|
||||
@@ -111,15 +112,29 @@ curl -XPOST -H "Authorization: Bearer $T" "$D/api/debug/memory?mb=400&seconds=30
|
||||
|
||||
Remember to click **Healthy** afterwards, or redeploy.
|
||||
|
||||
### The rolling-update wave
|
||||
|
||||
Still in the Chaos lab, **Rolling-update wave → Bounce each replica, one at a
|
||||
time** restarts the fleet exactly like a rolling update does, but driven by the
|
||||
crash endpoint. Keep the **Replicas** table in view: each replica disappears,
|
||||
comes back, and its **request counter restarts from ~0** while the others keep
|
||||
climbing. The wall never goes down — which is the whole point of rolling
|
||||
updates, shown from the inside.
|
||||
|
||||
> Talking point: recreate would drop every replica at once (all counters would
|
||||
> reset together, with a gap of downtime). Rolling keeps capacity by replacing
|
||||
> one at a time. Same mechanism, two strategies.
|
||||
|
||||
## 7. Canary v1 → v2
|
||||
|
||||
1. In a terminal: `scripts/hammer.sh https://<domain>`. It prints a live count and, on Ctrl-C, a tally of `status version host`.
|
||||
2. Set **deploy strategy** to *canary* (initial weight 10%), and deploy image `2.0.0`.
|
||||
3. On the wall, reload a few times: ~1 in 10 loads shows the **v2 badge** in a different colour. In the admin **Traffic probe**, the bars split by version.
|
||||
3. On the wall, reload a few times: ~1 in 10 loads shows the **v2 badge** in a different colour **and the new live server-time card**. In the admin **Traffic probe**, the bars split by version; in the **Replicas** table, the two versions sit side by side with their own request counters.
|
||||
4. **Promote now.** The probe goes 100% v2, and `hammer.sh` should show `0 failed`.
|
||||
|
||||
> The v2 build is the same code with a different `VERSION`. Nothing needs to
|
||||
> differ for the demo to work.
|
||||
> differ for the demo to work — but the clock card (absent in v1) makes the
|
||||
> split visible to the audience without a terminal.
|
||||
|
||||
## 8. Scheduled cleanup
|
||||
|
||||
@@ -141,6 +156,6 @@ want migrations as an explicit step rather than on boot.
|
||||
|
||||
## Reset between sessions
|
||||
|
||||
- Admin → Settings: clear the banner, un-pause.
|
||||
- Admin → Settings: clear the banner, set the theme back to *System*, un-pause.
|
||||
- Chaos lab: **Healthy**.
|
||||
- To wipe the wall: detach/recreate the database, or `DELETE FROM entries;` and restart (the seed runs when the table is empty).
|
||||
- To wipe the wall: detach/recreate the database, or `DELETE FROM entries; DELETE FROM reactions;` and restart (the seed runs when the table is empty).
|
||||
|
||||
Reference in New Issue
Block a user