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
58 lines
1.9 KiB
Go
58 lines
1.9 KiB
Go
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)
|
|
}
|