chore(canary-phase11): manage page API complete (GET + DELETE /m/{id})
Phase 11 deliverables (audited PASS):
Service primitives
- event.Service.CountActiveDedup(ctx, tokenID): Redis SCAN over
dedup:trigger:{tokenID}:* (token-scoped pattern), batched 100,
returns sum(value-1) — value=1 (first trigger fired) contributes 0,
value=N (N-1 INCRs) contributes N-1 silenced events. Matches
spec §10.5 "counter = total silenced" semantics.
- token.Service.DeleteByManageID: thin wrapper around
Repository.DeleteByManageID; ServiceRepository interface gains the
method.
DTOs
- ManageTokenView (slimmer than the create-time Response — drops
manage_id / manage_url / metadata since the client is already on
the manage page); ManagePage{NextCursor, HasMore}; ManageResponse
shape exactly matches spec §8.4 example. event.Response (already
in place from Phase 1) reused for the events array.
Handlers
- GET /api/m/{manage_id}: parses ?cursor (int64, must be ≥ 0; 400
BAD_CURSOR otherwise) and ?limit (default 20, capped at 100).
Calls GetByManageID + ListByToken + CountByToken + CountActiveDedup
via the new EventQuery + DedupCounter interfaces, assembles
ManageResponse. Page derived from list.HasMore + list.NextCursor
(audit-fix ca67cf63 — was re-deriving from len before).
- DELETE /api/m/{manage_id}: 204 on success; 404 envelope on miss.
Cascade is via the existing tokens-events FK ON DELETE CASCADE
(verified by integration test).
- 404 on unknown manage_id returns the JSON envelope (not bare
http.NotFound) so the frontend gets a parseable error code.
- NewHandler signature 5 → 6 args (added eventQuery + dedupCounter).
Both test sites and main.go updated.
Wire-up
- cmd/canary/main.go: tokenH.RegisterManageRoutes(api) inside the
/api subrouter. Inherits CORS + read-tier rate limit from /api;
no Turnstile, no create-tier limits (read-style endpoint per
spec §8.1).
- Extracted buildHTTPDeps to keep run() under the funlen ceiling.
Integration tests (testcontainers-Postgres + miniredis):
- ManagePageReturnsTokenAndEventsAndSilencedCount: 3 same-IP triggers
→ events_total=3, events_silenced_active=2, one NotifySent + two
NotifyDeduped, no next page.
- ManagePageReturns404OnUnknownManageID
- ManageDeleteCascadesEvents: DELETE → CountByToken==0 + token gone
Audited: superpowers:code-reviewer + general-purpose spec adherence —
PASS. Both audits flagged one non-blocking nit (buildPage was
re-deriving NextCursor from len rather than using the repo's HasMore
flag); cleaned up in ca67cf63 to avoid future drift if the repo's
pagination logic evolves.
This commit is contained in:
parent
ca67cf6313
commit
de236328f8