package api import ( "context" "log/slog" "net/http" "time" "github.com/go-chi/chi/v5" "github.com/alexei/docker-watcher/internal/proxy" ) // validateProxy runs the validation pipeline without creating a proxy. // POST /api/proxies/validate func (s *Server) validateProxy(w http.ResponseWriter, r *http.Request) { var req struct { Host string `json:"host"` Port int `json:"port"` } if !decodeJSON(w, r, &req) { return } if req.Host == "" { respondError(w, http.StatusBadRequest, "host is required") return } if req.Port < 1 || req.Port > 65535 { respondError(w, http.StatusBadRequest, "port must be between 1 and 65535") return } ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second) defer cancel() result := proxy.ValidateDestination(ctx, req.Host, req.Port) respondJSON(w, http.StatusOK, result) } // createProxy creates a new standalone proxy. // POST /api/proxies func (s *Server) createProxy(w http.ResponseWriter, r *http.Request) { if s.proxyManager == nil { respondError(w, http.StatusServiceUnavailable, "proxy manager not configured") return } var req proxy.CreateProxyRequest if !decodeJSON(w, r, &req) { return } if req.Domain == "" { respondError(w, http.StatusBadRequest, "domain is required") return } if req.DestinationURL == "" { respondError(w, http.StatusBadRequest, "destination_url is required") return } if req.DestinationPort < 1 || req.DestinationPort > 65535 { respondError(w, http.StatusBadRequest, "destination_port must be between 1 and 65535") return } p, err := s.proxyManager.CreateProxy(r.Context(), req) if err != nil { slog.Error("failed to create proxy", "domain", req.Domain, "error", err) respondError(w, http.StatusInternalServerError, "failed to create proxy") return } respondJSON(w, http.StatusCreated, p) } // listProxies returns all standalone proxies. // GET /api/proxies func (s *Server) listProxies(w http.ResponseWriter, r *http.Request) { if s.proxyManager == nil { respondError(w, http.StatusServiceUnavailable, "proxy manager not configured") return } proxies, err := s.proxyManager.ListProxies() if err != nil { slog.Error("proxy operation failed", "error", err) respondError(w, http.StatusInternalServerError, "proxy operation failed") return } respondJSON(w, http.StatusOK, proxies) } // getProxy returns a single standalone proxy. // GET /api/proxies/{id} func (s *Server) getProxy(w http.ResponseWriter, r *http.Request) { if s.proxyManager == nil { respondError(w, http.StatusServiceUnavailable, "proxy manager not configured") return } id := chi.URLParam(r, "id") p, err := s.proxyManager.GetProxy(id) if err != nil { if proxy.IsNotFound(err) { respondNotFound(w, "proxy") return } slog.Error("proxy operation failed", "error", err) respondError(w, http.StatusInternalServerError, "proxy operation failed") return } respondJSON(w, http.StatusOK, p) } // updateProxy updates an existing standalone proxy. // PUT /api/proxies/{id} func (s *Server) updateProxy(w http.ResponseWriter, r *http.Request) { if s.proxyManager == nil { respondError(w, http.StatusServiceUnavailable, "proxy manager not configured") return } id := chi.URLParam(r, "id") var req proxy.UpdateProxyRequest if !decodeJSON(w, r, &req) { return } if req.Domain == "" { respondError(w, http.StatusBadRequest, "domain is required") return } if req.DestinationURL == "" { respondError(w, http.StatusBadRequest, "destination_url is required") return } if req.DestinationPort < 1 || req.DestinationPort > 65535 { respondError(w, http.StatusBadRequest, "destination_port must be between 1 and 65535") return } p, err := s.proxyManager.UpdateProxy(r.Context(), id, req) if err != nil { if proxy.IsNotFound(err) { respondNotFound(w, "proxy") return } slog.Error("proxy operation failed", "error", err) respondError(w, http.StatusInternalServerError, "proxy operation failed") return } respondJSON(w, http.StatusOK, p) } // deleteProxy removes a standalone proxy. // DELETE /api/proxies/{id} func (s *Server) deleteProxy(w http.ResponseWriter, r *http.Request) { if s.proxyManager == nil { respondError(w, http.StatusServiceUnavailable, "proxy manager not configured") return } id := chi.URLParam(r, "id") if err := s.proxyManager.DeleteProxy(r.Context(), id); err != nil { if proxy.IsNotFound(err) { respondNotFound(w, "proxy") return } slog.Error("proxy operation failed", "error", err) respondError(w, http.StatusInternalServerError, "proxy operation failed") return } respondJSON(w, http.StatusOK, map[string]string{"deleted": id}) } // listAllProxies returns a merged view of standalone and deploy-managed proxies. // GET /api/proxies/all func (s *Server) listAllProxies(w http.ResponseWriter, r *http.Request) { if s.proxyManager == nil { respondError(w, http.StatusServiceUnavailable, "proxy manager not configured") return } views, err := s.proxyManager.ListAllProxies() if err != nil { slog.Error("proxy operation failed", "error", err) respondError(w, http.StatusInternalServerError, "proxy operation failed") return } respondJSON(w, http.StatusOK, views) }