| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- // M13a W5: admin HTTP routes for routerd. Two routes are
- // registered when BA_AUTHD_JWT_SECRET is set:
- //
- // GET /v1/admin/dedupe/state — number of pending collapse
- // windows. Read-only. Any authenticated user.
- // POST /v1/admin/dedupe/flush — force-flush every pending
- // collapse immediately (calls Collapser.FlushAll). Destructive:
- // re-publishes all held alerts to NATS. super_admin or
- // tenant_admin only.
- //
- // When BA_AUTHD_JWT_SECRET is unset, no admin routes are
- // registered and the existing /health + /metrics keep working
- // unchanged (LAN deploy path).
- package main
- import (
- "encoding/json"
- "log/slog"
- "net/http"
- "git3.techno-world.net/lrosales/broad-announce/internal/authd"
- "git3.techno-world.net/lrosales/broad-announce/internal/dedupe"
- )
- // wireAdminRoutes wires the admin routes onto mux. When the JWT
- // gate is disabled (env not set), the function logs a warning and
- // returns without registering anything — backward compat for
- // non-M13 deploys.
- func wireAdminRoutes(
- mux *http.ServeMux,
- collapser *dedupe.Collapser,
- state *fanoutState,
- logger *slog.Logger,
- ) {
- if !authd.EnvEnabled() {
- logger.Warn("admin routes are DISABLED (set BA_AUTHD_JWT_SECRET to enable)")
- return
- }
- ad, err := authd.NewFromEnv()
- if err != nil {
- logger.Error("BA_AUTHD_JWT_SECRET set but authd init failed; admin routes DISABLED", "err", err)
- return
- }
- logger.Info("admin routes enabled with JWT gate")
- mux.Handle("GET /v1/admin/dedupe/state",
- ad.RequireAuth(http.HandlerFunc(handleDedupeState(collapser, state, logger))))
- mux.Handle("POST /v1/admin/dedupe/flush",
- ad.RequireRole("super_admin", "tenant_admin")(
- http.HandlerFunc(handleDedupeFlush(collapser, state, logger))))
- }
- // handleDedupeState returns the count of pending collapse windows.
- // `collapser.Pending()` is the source of truth (it owns the
- // per-(source,key) map); `state.len()` mirrors it but tracks
- // the cached targets, so we report both for visibility.
- func handleDedupeState(collapser *dedupe.Collapser, state *fanoutState, logger *slog.Logger) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- pending := collapser.Pending()
- cached := state.len()
- w.Header().Set("Content-Type", "application/json")
- _ = json.NewEncoder(w).Encode(map[string]any{
- "pending_collapses": pending,
- "cached_target_lists": cached,
- })
- }
- }
- // handleDedupeFlush force-flushes every pending collapse. The
- // Collapser's Run loop also reads its own pending map and fires
- // onFlush, so this is just a no-wait path to the same outcome.
- // Safe to call when the map is empty (no-op).
- func handleDedupeFlush(collapser *dedupe.Collapser, state *fanoutState, logger *slog.Logger) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- before := collapser.Pending()
- collapser.FlushAll()
- actor := ""
- if c := authd.ClaimsFromContext(r.Context()); c != nil {
- actor = c.UserID
- }
- logger.Info("admin dedupe flush",
- "pending_before", before,
- "actor", actor,
- )
- w.Header().Set("Content-Type", "application/json")
- _ = json.NewEncoder(w).Encode(map[string]any{
- "flushed": true,
- "pending_before": before,
- })
- }
- }
|