package server import ( "encoding/json" "fmt" "net/http" "scm.bstein.dev/bstein/soteria/internal/api" "scm.bstein.dev/bstein/soteria/internal/config" "scm.bstein.dev/bstein/soteria/internal/k8s" ) type Server struct { cfg *config.Config client *k8s.Client mux *http.ServeMux } func New(cfg *config.Config, client *k8s.Client) *Server { s := &Server{ cfg: cfg, client: client, mux: http.NewServeMux(), } s.mux.HandleFunc("/healthz", s.handleHealth) s.mux.HandleFunc("/readyz", s.handleReady) s.mux.HandleFunc("/v1/backup", s.handleBackup) s.mux.HandleFunc("/v1/restore-test", s.handleRestore) return s } func (s *Server) Handler() http.Handler { return s.mux } func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) } func (s *Server) handleReady(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, map[string]string{"status": "ready"}) } func (s *Server) handleBackup(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { writeError(w, http.StatusMethodNotAllowed, "method not allowed") return } r.Body = http.MaxBytesReader(w, r.Body, 1<<20) var req api.BackupRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, fmt.Sprintf("invalid JSON: %v", err)) return } jobName, secretName, err := s.client.CreateBackupJob(r.Context(), s.cfg, req) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } resp := api.BackupResponse{ JobName: jobName, Namespace: req.Namespace, Secret: secretName, DryRun: req.DryRun, } writeJSON(w, http.StatusOK, resp) } func (s *Server) handleRestore(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { writeError(w, http.StatusMethodNotAllowed, "method not allowed") return } r.Body = http.MaxBytesReader(w, r.Body, 1<<20) var req api.RestoreTestRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, fmt.Sprintf("invalid JSON: %v", err)) return } jobName, secretName, err := s.client.CreateRestoreJob(r.Context(), s.cfg, req) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } resp := api.RestoreTestResponse{ JobName: jobName, Namespace: req.Namespace, Secret: secretName, DryRun: req.DryRun, } writeJSON(w, http.StatusOK, resp) } func writeJSON(w http.ResponseWriter, status int, payload any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(payload) } func writeError(w http.ResponseWriter, status int, message string) { writeJSON(w, status, map[string]string{"error": message}) }