package web import ( "net/http" "strconv" "github.com/owen/vibeproxy/internal/auth" "github.com/owen/vibeproxy/internal/database" "github.com/owen/vibeproxy/internal/logging" ) func (h *Web) handleAPITokens(w http.ResponseWriter, r *http.Request) { tokens, err := database.ListAPITokens(h.db) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } render(w, "api_tokens", PageData{ Active: "api", Title: "API", CSRFToken: h.csrf.Token(w), Flash: r.URL.Query().Get("flash"), FlashKind: r.URL.Query().Get("kind"), Data: tokens, }) } func (h *Web) handleAPITokenCreate(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } name := r.FormValue("name") if name == "" { http.Redirect(w, r, "/api-tokens?flash="+flashMsg("Name is required")+"&kind=danger", http.StatusFound) return } plaintext, hash, prefix, err := auth.GenerateAPIToken() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if _, err := database.CreateAPIToken(h.db, name, hash, prefix); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } logging.Audit(h.db, auth.ActorFromContext(r.Context()), "token_created", name, "") // Shown once: pass the plaintext token through the flash message. http.Redirect(w, r, "/api-tokens?flash="+flashMsg("Token created: "+plaintext+" (copy it now — it will not be shown again)")+"&kind=warning", http.StatusFound) } func (h *Web) handleAPITokenRevoke(w http.ResponseWriter, r *http.Request) { id, _ := strconv.ParseInt(r.PathValue("id"), 10, 64) if err := database.RevokeAPIToken(h.db, id); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } logging.Audit(h.db, auth.ActorFromContext(r.Context()), "token_revoked", "", "") http.Redirect(w, r, "/api-tokens?flash="+flashMsg("Token revoked")+"&kind=success", http.StatusFound) }