Files
owenandClaude Sonnet 5 ba1f7c4c03 Initial vibeproxy implementation
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
2026-08-17 10:28:59 -05:00

64 lines
1.5 KiB
Go

// 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
}