package api import ( "net/http" "github.com/owen/vibeproxy/internal/auth" "github.com/owen/vibeproxy/internal/database" ) func (a *API) listTokens(w http.ResponseWriter, r *http.Request) { tokens, err := database.ListAPITokens(a.db) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } // Never expose the stored hash. type dto struct { ID int64 `json:"id"` Name string `json:"name"` Prefix string `json:"prefix"` CreatedAt string `json:"created_at"` LastUsedAt string `json:"last_used_at,omitempty"` Revoked bool `json:"revoked"` } out := make([]dto, 0, len(tokens)) for _, t := range tokens { d := dto{ID: t.ID, Name: t.Name, Prefix: t.Prefix, CreatedAt: t.CreatedAt.Format("2006-01-02T15:04:05Z"), Revoked: t.RevokedAt != nil} if t.LastUsedAt != nil { d.LastUsedAt = t.LastUsedAt.Format("2006-01-02T15:04:05Z") } out = append(out, d) } writeJSON(w, http.StatusOK, out) } func (a *API) createToken(w http.ResponseWriter, r *http.Request) { var req struct { Name string `json:"name"` } if !decodeJSON(w, r, &req) { return } if req.Name == "" { writeError(w, http.StatusBadRequest, "name is required") return } plaintext, hash, prefix, err := auth.GenerateAPIToken() if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } id, err := database.CreateAPIToken(a.db, req.Name, hash, prefix) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } audit(a.db, r, "token_created", req.Name, "") writeJSON(w, http.StatusCreated, map[string]any{ "id": id, "name": req.Name, "token": plaintext, // shown once; never retrievable again }) } func (a *API) revokeToken(w http.ResponseWriter, r *http.Request) { id, err := pathID(r) if err != nil { writeError(w, http.StatusBadRequest, "invalid id") return } if err := database.RevokeAPIToken(a.db, id); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } audit(a.db, r, "token_revoked", "", "") w.WriteHeader(http.StatusNoContent) }