feat: add visitor identity cookie and database reset job
Three connected features for the deployment-strategy demo: 1. Visitor identity cookie (v3). A new visitors table backs a first-party gb_visitor cookie: middleware on /api checks the cookie's UUID against the database on every request and regenerates + persists it when the id is missing, malformed, or unknown (fresh browser, pruned identity, or a reset database). The identity remembers the name a visitor signed with (pre-filled next time, used for unsigned posts), replaces the standalone reaction cookie for reaction de-dupe, and feeds a live "N visiting" counter (identities seen in the last 5 minutes) shown on the wall, in /api/info, the admin overview, and the presence/tick SSE events. The admin console gains Visitors/Visiting tiles and a visitors table, plus a GET /api/me endpoint and GET /api/admin/visitors. 2. guestbook reset command. Wipes entries (including soft-deleted rows), reactions and visitor identities under the schema advisory lock, with --dry-run, --hard (also reset wall settings) and --reseed flags; meant to run as a one-off Miabi Job. After a reset every outstanding cookie is invalidated, so browsers get a fresh identity on their next request. 3. cleanup learns --prune-visitors (default 30 days): the cron job now also forgets idle identities together with their reactions. Docs (README, DEMO) updated: act 8 covers reset, the visitor-identity talking point is in act 3, and the API tables list the new endpoints. Akili-Task: tsk_ptdi5ufndrzwlljk4v5g Akili-Agent: goldfish
This commit is contained in:
@@ -81,6 +81,7 @@ func (h *Handler) AdminOverview(c *okapi.Context) error {
|
||||
"stats": stats,
|
||||
"settings": settings,
|
||||
"online": h.broker.Online(),
|
||||
"visitors": h.visitorsActive(ctx),
|
||||
"replicas": h.broker.Replicas(),
|
||||
"debug": h.debug,
|
||||
"runtime": okapi.M{
|
||||
@@ -127,6 +128,18 @@ func (h *Handler) AdminListEntries(c *okapi.Context) error {
|
||||
return c.OK(okapi.M{"entries": out, "total": total, "limit": limit, "offset": offset})
|
||||
}
|
||||
|
||||
// AdminVisitors lists the most recently active visitor identities, so the
|
||||
// console can show the cookies the app has issued and who is around now.
|
||||
func (h *Handler) AdminVisitors(c *okapi.Context) error {
|
||||
limit := atoiDefault(c.Query("limit"), 50)
|
||||
visitors, err := h.store.AdminVisitors(c.Request().Context(), limit)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, okapi.M{"error": "could not load visitors"})
|
||||
}
|
||||
fresh := time.Now().Add(-visitorFreshWindow)
|
||||
return c.OK(okapi.M{"visitors": visitors, "active_since": fresh.UTC().Format(time.RFC3339)})
|
||||
}
|
||||
|
||||
// AdminUpdateEntry pins/unpins or hides/unhides an entry and broadcasts it.
|
||||
func (h *Handler) AdminUpdateEntry(c *okapi.Context) error {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
|
||||
Reference in New Issue
Block a user