package clusteradmin import ( "context" "encoding/json" "net/http" "strings" "time" "proxui/backend/internal/cluster" ) type Repository interface { UpsertCluster(ctx context.Context, cluster cluster.Cluster) (cluster.Cluster, error) SetClusterStatus(ctx context.Context, id string, status string) (cluster.Cluster, bool, error) } type Handler struct { repository Repository } func NewHandler(repository Repository) Handler { return Handler{repository: repository} } func (h Handler) CreateCluster(w http.ResponseWriter, r *http.Request) { var request upsertClusterRequest if !decodeJSON(w, r, &request) { return } saved, err := h.repository.UpsertCluster(r.Context(), cluster.Cluster{ Name: request.Name, APIEndpoint: request.APIEndpoint, TLSFingerprint: request.TLSFingerprint, TokenID: request.TokenID, TokenSecret: request.TokenSecret, Status: request.Status, }) if err != nil { writeError(w, http.StatusBadRequest, "cluster_upsert_failed") return } writeJSON(w, http.StatusCreated, clusterResponseFromCluster(saved)) } func (h Handler) UpdateCluster(w http.ResponseWriter, r *http.Request) { clusterID := r.PathValue("clusterID") if clusterID == "" { writeError(w, http.StatusBadRequest, "cluster_id_required") return } var request upsertClusterRequest if !decodeJSON(w, r, &request) { return } saved, err := h.repository.UpsertCluster(r.Context(), cluster.Cluster{ ID: clusterID, Name: request.Name, APIEndpoint: request.APIEndpoint, TLSFingerprint: request.TLSFingerprint, TokenID: request.TokenID, TokenSecret: request.TokenSecret, Status: request.Status, }) if err != nil { writeError(w, http.StatusBadRequest, "cluster_upsert_failed") return } writeJSON(w, http.StatusOK, clusterResponseFromCluster(saved)) } func (h Handler) SetClusterStatus(w http.ResponseWriter, r *http.Request) { clusterID := r.PathValue("clusterID") if clusterID == "" { writeError(w, http.StatusBadRequest, "cluster_id_required") return } var request setStatusRequest if !decodeJSON(w, r, &request) { return } if strings.TrimSpace(request.Status) == "" { writeError(w, http.StatusBadRequest, "status_required") return } updated, found, err := h.repository.SetClusterStatus(r.Context(), clusterID, request.Status) if err != nil { writeError(w, http.StatusBadRequest, "cluster_status_update_failed") return } if !found { writeError(w, http.StatusNotFound, "cluster_not_found") return } writeJSON(w, http.StatusOK, clusterResponseFromCluster(updated)) } type upsertClusterRequest struct { Name string `json:"name"` APIEndpoint string `json:"api_endpoint"` TLSFingerprint string `json:"tls_fingerprint"` TokenID string `json:"token_id"` TokenSecret string `json:"token_secret"` Status string `json:"status"` } type setStatusRequest struct { Status string `json:"status"` } type clusterResponse struct { ID string `json:"id"` Name string `json:"name"` APIEndpoint string `json:"api_endpoint"` TLSFingerprint string `json:"tls_fingerprint"` TokenID string `json:"token_id"` Status string `json:"status"` CreatedAt time.Time `json:"created_at"` } func clusterResponseFromCluster(cluster cluster.Cluster) clusterResponse { return clusterResponse{ ID: cluster.ID, Name: cluster.Name, APIEndpoint: cluster.APIEndpoint, TLSFingerprint: cluster.TLSFingerprint, TokenID: cluster.TokenID, Status: cluster.Status, CreatedAt: cluster.CreatedAt, } } func decodeJSON(w http.ResponseWriter, r *http.Request, target any) bool { defer r.Body.Close() decoder := json.NewDecoder(r.Body) decoder.DisallowUnknownFields() if err := decoder.Decode(target); err != nil { writeError(w, http.StatusBadRequest, "invalid_json") return false } return true } func writeJSON(w http.ResponseWriter, status int, body any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(body) } func writeError(w http.ResponseWriter, status int, message string) { writeJSON(w, status, map[string]string{"error": message}) }