Files
bri-sandbox-development-pla…/control-plane/internal/api/environments.go
T
Achmad a7df9ffc6c Slice 2: sandbox, template, environment, route CRUD
- store: add tables and CRUD for sandboxes (with services), templates
  (with services, clone-into-sandbox), environments (named key/value
  sets), and routes (per-sandbox <service>_url overrides).
- api: split into one file per resource. handleSandboxes/handleSandboxByID
  covers CRUD + 'clone from template' + 'deploy one service in a sandbox'
  (which merges the sandbox's env into the request, picks the port,
  and dispatches the deploy frame to the right node).
  handleTemplates/handleTemplateByID, handleEnvironments/handleEnvironmentByID,
  handlePushRoutes cover the rest. The control plane's repo->node
  resolution still lives in resolveNode (api-gateway -> gateway,
  everything else -> micro).
2026-06-24 03:59:02 +00:00

109 lines
2.7 KiB
Go

package api
import (
"encoding/json"
"net/http"
"strings"
"github.com/sdp/control-plane/internal/store"
)
type envReq struct {
Name string `json:"name"`
Values map[string]string `json:"values"`
}
func (s *Server) handleEnvironments(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
envs, err := s.st.ListEnvironments()
if err != nil {
writeErr(w, http.StatusInternalServerError, err.Error())
return
}
if envs == nil {
envs = []store.Environment{}
}
writeJSON(w, http.StatusOK, envs)
case http.MethodPost:
var body envReq
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, "bad json", http.StatusBadRequest)
return
}
if body.Name == "" {
writeErr(w, http.StatusBadRequest, "name required")
return
}
if body.Values == nil {
body.Values = map[string]string{}
}
e := store.Environment{ID: newID(), Name: body.Name, Values: body.Values}
if err := s.st.CreateEnvironment(e); err != nil {
writeErr(w, http.StatusBadRequest, err.Error())
return
}
writeJSON(w, http.StatusOK, e)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}
func (s *Server) handleEnvironmentByID(w http.ResponseWriter, r *http.Request) {
id := strings.TrimPrefix(r.URL.Path, "/api/environments/")
if id == "" || strings.Contains(id, "/") {
http.Error(w, "not found", http.StatusNotFound)
return
}
switch r.Method {
case http.MethodGet:
e, err := s.st.GetEnvironment(id)
if err == store.ErrNotFound {
http.Error(w, "not found", http.StatusNotFound)
return
}
if err != nil {
writeErr(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusOK, e)
case http.MethodPut:
var body envReq
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, "bad json", http.StatusBadRequest)
return
}
e, err := s.st.GetEnvironment(id)
if err == store.ErrNotFound {
http.Error(w, "not found", http.StatusNotFound)
return
}
if err != nil {
writeErr(w, http.StatusInternalServerError, err.Error())
return
}
if body.Name != "" {
e.Name = body.Name
}
if body.Values != nil {
e.Values = body.Values
}
if err := s.st.UpdateEnvironment(*e); err != nil {
writeErr(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusOK, e)
case http.MethodDelete:
if err := s.st.DeleteEnvironment(id); err == store.ErrNotFound {
http.Error(w, "not found", http.StatusNotFound)
return
} else if err != nil {
writeErr(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}