package api import ( "context" "encoding/json" "net/http" "strings" "time" "proxmox-router/internal/certs" ) func (s *Server) handleListCertificates(w http.ResponseWriter, r *http.Request) { if s.certMgr == nil { writeError(w, http.StatusServiceUnavailable, "certificates manager not configured") return } list := s.certMgr.ListCertificates() writeJSON(w, http.StatusOK, list) } func (s *Server) handleGetCertificate(w http.ResponseWriter, r *http.Request) { if s.certMgr == nil { writeError(w, http.StatusServiceUnavailable, "certificates manager not configured") return } name := r.PathValue("name") c, err := s.certMgr.GetCertificate(name) if err != nil { writeError(w, http.StatusNotFound, err.Error()) return } writeJSON(w, http.StatusOK, c) } func (s *Server) handleCreateCA(w http.ResponseWriter, r *http.Request) { if s.certMgr == nil { writeError(w, http.StatusServiceUnavailable, "certificates manager not configured") return } var req certs.CreateCARequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request payload") return } info, err := s.certMgr.CreateCA(req) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } writeJSON(w, http.StatusCreated, info) } func (s *Server) handleCreateCertificate(w http.ResponseWriter, r *http.Request) { if s.certMgr == nil { writeError(w, http.StatusServiceUnavailable, "certificates manager not configured") return } var req certs.CreateCertRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request payload") return } info, err := s.certMgr.CreateCertificate(req) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } writeJSON(w, http.StatusCreated, info) } func (s *Server) handleImportCertificate(w http.ResponseWriter, r *http.Request) { if s.certMgr == nil { writeError(w, http.StatusServiceUnavailable, "certificates manager not configured") return } var req certs.ImportCertRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request payload") return } info, err := s.certMgr.ImportCertificate(req) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } writeJSON(w, http.StatusCreated, info) } func (s *Server) handleIssueLetsEncrypt(w http.ResponseWriter, r *http.Request) { if s.certMgr == nil { writeError(w, http.StatusServiceUnavailable, "certificates manager not configured") return } var req certs.LetsEncryptRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request payload") return } ctx, cancel := context.WithTimeout(r.Context(), 3*time.Minute) defer cancel() info, err := s.certMgr.IssueLetsEncrypt(ctx, req) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } writeJSON(w, http.StatusCreated, info) } func (s *Server) handleExportCertificate(w http.ResponseWriter, r *http.Request) { if s.certMgr == nil { writeError(w, http.StatusServiceUnavailable, "certificates manager not configured") return } name := r.PathValue("name") certPEM, keyPEM, err := s.certMgr.ExportPEM(name) if err != nil { writeError(w, http.StatusNotFound, err.Error()) return } writeJSON(w, http.StatusOK, map[string]string{ "name": name, "cert_pem": certPEM, "key_pem": keyPEM, }) } func (s *Server) handleDeleteCertificate(w http.ResponseWriter, r *http.Request) { if s.certMgr == nil { writeError(w, http.StatusServiceUnavailable, "certificates manager not configured") return } name := r.PathValue("name") // Prevent deletion if in use by www-ssl if s.servicesMgr != nil { if sslSvc, ok := s.servicesMgr.GetService("www-ssl"); ok { if strings.EqualFold(sslSvc.Certificate, name) && !sslSvc.Disabled { writeError(w, http.StatusBadRequest, "cannot delete certificate: actively in use by enabled www-ssl service") return } } } if err := s.certMgr.DeleteCertificate(name); err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } writeJSON(w, http.StatusOK, map[string]string{"message": "certificate deleted"}) } func (s *Server) handleACMEChallenge(w http.ResponseWriter, r *http.Request) { token := r.PathValue("token") if token == "" { token = strings.TrimPrefix(r.URL.Path, "/.well-known/acme-challenge/") } if s.certMgr == nil { http.NotFound(w, r) return } keyAuth, ok := s.certMgr.GetHTTP01Challenge(token) if !ok { http.NotFound(w, r) return } w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(keyAuth)) }