// Package api implements the REST API under /api/v1, authenticated with // bearer API tokens (separate from the web UI's Basic Auth). package api import ( "database/sql" "encoding/json" "net/http" "strconv" "github.com/owen/vibeproxy/internal/auth" "github.com/owen/vibeproxy/internal/logging" ) const maxJSONBody = 10 << 20 // 10 MiB is plenty for config payloads func writeJSON(w http.ResponseWriter, status int, v any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) json.NewEncoder(w).Encode(v) } func writeError(w http.ResponseWriter, status int, msg string) { writeJSON(w, status, map[string]string{"error": msg}) } func decodeJSON(w http.ResponseWriter, r *http.Request, v any) bool { r.Body = http.MaxBytesReader(w, r.Body, maxJSONBody) dec := json.NewDecoder(r.Body) dec.DisallowUnknownFields() if err := dec.Decode(v); err != nil { writeError(w, http.StatusBadRequest, "invalid request body: "+err.Error()) return false } return true } func pathID(r *http.Request) (int64, error) { return strconv.ParseInt(r.PathValue("id"), 10, 64) } func audit(db *sql.DB, r *http.Request, action, target, details string) { logging.Audit(db, auth.ActorFromContext(r.Context()), action, target, details) } func queryInt(r *http.Request, key string, def int) int { v := r.URL.Query().Get(key) if v == "" { return def } n, err := strconv.Atoi(v) if err != nil { return def } return n } func nilIfEmptyID(id int64) *int64 { if id == 0 { return nil } return &id }