package httpapi import ( "encoding/json" "errors" "fmt" "net/http" "github.com/jackc/pgx/v5/pgconn" "github.com/vasyansk/imap-copier/internal/store" ) func writeJSON(w http.ResponseWriter, code int, v any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) _ = json.NewEncoder(w).Encode(v) } func (s *Server) handleCreateEndpoint(w http.ResponseWriter, r *http.Request) { var e store.Endpoint if err := json.NewDecoder(r.Body).Decode(&e); err != nil { http.Error(w, "bad json", http.StatusBadRequest) return } if e.TLSMode != "ssl" && e.TLSMode != "starttls" && e.TLSMode != "plain" { http.Error(w, "tls_mode must be ssl|starttls|plain", http.StatusBadRequest) return } id, err := s.store.CreateEndpoint(r.Context(), e) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, http.StatusCreated, map[string]int64{"id": id}) } func (s *Server) handleUpdateEndpoint(w http.ResponseWriter, r *http.Request) { id, err := pathID(r, "id") if err != nil { http.Error(w, "bad id", http.StatusBadRequest) return } var e store.Endpoint if err := json.NewDecoder(r.Body).Decode(&e); err != nil { http.Error(w, "bad json", http.StatusBadRequest) return } if e.TLSMode != "ssl" && e.TLSMode != "starttls" && e.TLSMode != "plain" { http.Error(w, "tls_mode must be ssl|starttls|plain", http.StatusBadRequest) return } e.ID = id if err := s.store.UpdateEndpoint(r.Context(), e); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } // handleDeleteEndpoint removes an endpoint that no task references. A referenced // endpoint is refused with 409 rather than a foreign-key error, and the same // status covers the race where a task is created between check and delete. func (s *Server) handleDeleteEndpoint(w http.ResponseWriter, r *http.Request) { id, err := pathID(r, "id") if err != nil { http.Error(w, "bad id", http.StatusBadRequest) return } used, err := s.store.CountTasksUsingEndpoint(r.Context(), id) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if used > 0 { http.Error(w, fmt.Sprintf("endpoint is used by %d task(s) — delete them first", used), http.StatusConflict) return } if err := s.store.DeleteEndpoint(r.Context(), id); err != nil { var pgErr *pgconn.PgError if errors.As(err, &pgErr) && pgErr.Code == "23503" { http.Error(w, "endpoint is used by a task — delete it first", http.StatusConflict) return } http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } func (s *Server) handleListEndpoints(w http.ResponseWriter, r *http.Request) { eps, err := s.store.ListEndpoints(r.Context()) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, http.StatusOK, eps) }