package api import ( "fmt" "net/http" "github.com/owen/vibeproxy/internal/certificates" "github.com/owen/vibeproxy/internal/database" "github.com/owen/vibeproxy/internal/models" ) func (a *API) listCertificates(w http.ResponseWriter, r *http.Request) { certs, err := database.ListCertificates(a.db) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, certs) } func (a *API) getCertificate(w http.ResponseWriter, r *http.Request) { id, err := pathID(r) if err != nil { writeError(w, http.StatusBadRequest, "invalid id") return } c, err := database.GetCertificate(a.db, id) if err != nil { writeError(w, http.StatusNotFound, "certificate not found") return } writeJSON(w, http.StatusOK, c) } type issueCertRequest struct { Name string `json:"name"` Domains []string `json:"domains"` Email string `json:"email"` Staging bool `json:"staging"` ChallengeType models.ChallengeType `json:"challenge_type"` DNSProvider string `json:"dns_provider"` } func (a *API) issueCertificate(w http.ResponseWriter, r *http.Request) { var req issueCertRequest if !decodeJSON(w, r, &req) { return } if req.Name == "" || len(req.Domains) == 0 { writeError(w, http.StatusBadRequest, "name and domains are required") return } if req.ChallengeType == "" { req.ChallengeType = models.ChallengeHTTP01 } settings := a.store.Current().Settings email := req.Email if email == "" { email = settings.ACMEEmail } cert, err := a.certs.IssueLetsEncrypt(r.Context(), certOpts(req, email)) if err != nil { writeError(w, http.StatusBadGateway, err.Error()) return } audit(a.db, r, "certificate_issued", cert.Name, fmt.Sprintf("domains=%v challenge=%s", cert.Domains, cert.ChallengeType)) writeJSON(w, http.StatusCreated, cert) } func certOpts(req issueCertRequest, email string) certificates.IssueLetsEncryptOptions { return certificates.IssueLetsEncryptOptions{ Name: req.Name, Domains: req.Domains, Email: email, Staging: req.Staging, ChallengeType: req.ChallengeType, DNSProvider: req.DNSProvider, } } func (a *API) uploadCertificate(w http.ResponseWriter, r *http.Request) { var req struct { Name string `json:"name"` Domains []string `json:"domains"` CertPEM string `json:"cert_pem"` KeyPEM string `json:"key_pem"` } if !decodeJSON(w, r, &req) { return } if req.Name == "" || req.CertPEM == "" || req.KeyPEM == "" { writeError(w, http.StatusBadRequest, "name, cert_pem, and key_pem are required") return } cert, err := a.certs.UploadCustom(req.Name, req.Domains, []byte(req.CertPEM), []byte(req.KeyPEM)) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } audit(a.db, r, "certificate_issued", cert.Name, "custom certificate uploaded") writeJSON(w, http.StatusCreated, cert) } func (a *API) renewCertificate(w http.ResponseWriter, r *http.Request) { id, err := pathID(r) if err != nil { writeError(w, http.StatusBadRequest, "invalid id") return } c, err := database.GetCertificate(a.db, id) if err != nil { writeError(w, http.StatusNotFound, "certificate not found") return } if c.Type != models.CertTypeLetsEncrypt { writeError(w, http.StatusBadRequest, "only Let's Encrypt certificates can be renewed") return } settings := a.store.Current().Settings updated, err := a.certs.IssueLetsEncrypt(r.Context(), certOptsFromModel(*c, settings.ACMEEmail, settings.ACMEStaging)) if err != nil { writeError(w, http.StatusBadGateway, err.Error()) return } audit(a.db, r, "certificate_renewed", updated.Name, "") writeJSON(w, http.StatusOK, updated) } func certOptsFromModel(c models.Certificate, email string, staging bool) certificates.IssueLetsEncryptOptions { return certificates.IssueLetsEncryptOptions{ Name: c.Name, Domains: c.Domains, Email: email, Staging: staging, ChallengeType: c.ChallengeType, DNSProvider: c.DNSProvider, } } func (a *API) deleteCertificate(w http.ResponseWriter, r *http.Request) { id, err := pathID(r) if err != nil { writeError(w, http.StatusBadRequest, "invalid id") return } if err := database.DeleteCertificate(a.db, id); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } a.certs.LoadAll() audit(a.db, r, "certificate_changed", fmt.Sprintf("id=%d", id), "deleted") w.WriteHeader(http.StatusNoContent) }