Self-contained HTTP/HTTPS/TCP/UDP reverse proxy manager written in Go, with SQLite storage, a Bootstrap 5 admin UI (Go templates, no JS framework), and a REST API under /api/v1. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QTXFrNP1imCW7jtfL5SZrw
78 lines
2.1 KiB
Go
78 lines
2.1 KiB
Go
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)
|
|
}
|