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
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/owen/vibeproxy/internal/auth"
|
||||
"github.com/owen/vibeproxy/internal/backup"
|
||||
"github.com/owen/vibeproxy/internal/database"
|
||||
"github.com/owen/vibeproxy/internal/logging"
|
||||
)
|
||||
|
||||
func (h *Web) handleSettings(w http.ResponseWriter, r *http.Request) {
|
||||
s := h.store.Current().Settings
|
||||
render(w, "settings", PageData{
|
||||
Active: "settings", Title: "Settings", CSRFToken: h.csrf.Token(w),
|
||||
Flash: r.URL.Query().Get("flash"), FlashKind: r.URL.Query().Get("kind"),
|
||||
Data: s,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Web) handleSettingsUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
kv := map[string]string{
|
||||
"acme_email": r.FormValue("acme_email"),
|
||||
"acme_staging": boolStr(r.FormValue("acme_staging") == "on"),
|
||||
"acme_renewal_window_days": r.FormValue("acme_renewal_window_days"),
|
||||
"cert_storage_path": r.FormValue("cert_storage_path"),
|
||||
"log_retention_days": r.FormValue("log_retention_days"),
|
||||
"trusted_proxy_cidrs": strings.Join(splitLines(r.FormValue("trusted_proxy_cidrs")), ","),
|
||||
"backup_enabled": boolStr(r.FormValue("backup_enabled") == "on"),
|
||||
"backup_interval_hours": r.FormValue("backup_interval_hours"),
|
||||
"backup_retention_count": r.FormValue("backup_retention_count"),
|
||||
"backup_path": r.FormValue("backup_path"),
|
||||
}
|
||||
for k, v := range kv {
|
||||
if err := database.SetSetting(h.db, k, v); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
h.reload(r)
|
||||
logging.Audit(h.db, auth.ActorFromContext(r.Context()), "settings_changed", "settings", "")
|
||||
http.Redirect(w, r, "/settings?flash="+flashMsg("Settings saved")+"&kind=success", http.StatusFound)
|
||||
}
|
||||
|
||||
func boolStr(b bool) string {
|
||||
if b {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
func (h *Web) handlePasswordChange(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
username := auth.ActorFromContext(r.Context())
|
||||
current := r.FormValue("current_password")
|
||||
newPass := r.FormValue("new_password")
|
||||
confirm := r.FormValue("confirm_password")
|
||||
|
||||
user, err := database.GetUserByUsername(h.db, username)
|
||||
if err != nil {
|
||||
http.Error(w, "user not found", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
ok, _ := auth.VerifyPassword(current, user.PasswordHash)
|
||||
if !ok {
|
||||
http.Redirect(w, r, "/settings?flash="+flashMsg("Current password is incorrect")+"&kind=danger", http.StatusFound)
|
||||
return
|
||||
}
|
||||
if len(newPass) < 8 || newPass != confirm {
|
||||
http.Redirect(w, r, "/settings?flash="+flashMsg("New passwords must match and be at least 8 characters")+"&kind=danger", http.StatusFound)
|
||||
return
|
||||
}
|
||||
hash, err := auth.HashPassword(newPass)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := database.UpdateUserPassword(h.db, username, hash, false); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
logging.Audit(h.db, username, "password_changed", username, "")
|
||||
http.Redirect(w, r, "/settings?flash="+flashMsg("Password changed")+"&kind=success", http.StatusFound)
|
||||
}
|
||||
|
||||
func (h *Web) handleBackupNow(w http.ResponseWriter, r *http.Request) {
|
||||
s := h.store.Current().Settings
|
||||
path, err := backup.Run(h.db, s.BackupPath, s.BackupRetentionCount)
|
||||
if err != nil {
|
||||
http.Redirect(w, r, "/settings?flash="+flashMsg("Backup failed: "+err.Error())+"&kind=danger", http.StatusFound)
|
||||
return
|
||||
}
|
||||
logging.Audit(h.db, auth.ActorFromContext(r.Context()), "settings_changed", "backup", "manual backup: "+path)
|
||||
http.Redirect(w, r, "/settings?flash="+flashMsg("Backup created: "+path)+"&kind=success", http.StatusFound)
|
||||
}
|
||||
Reference in New Issue
Block a user