package api import ( "encoding/json" "net/http" "strconv" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" k8sclient "github.com/vandachevici/games-console/internal/k8s" "github.com/vandachevici/games-console/internal/health" ) const nodeHost = "192.168.2.100" // NewRouter returns a chi router with all API routes registered. func NewRouter(client *k8sclient.Client) http.Handler { r := chi.NewRouter() r.Use(middleware.Logger) r.Use(middleware.Recoverer) r.Use(corsMiddleware) r.Get("/healthz", handleHealthz) r.Route("/api/servers", func(r chi.Router) { r.Get("/", makeListServers(client)) r.Post("/", makeCreateServer(client)) r.Get("/{name}", makeGetServer(client)) r.Put("/{name}", makeUpdateServer(client)) r.Delete("/{name}", makeDeleteServer(client)) r.Get("/{name}/logs", makeGetLogs(client)) r.Get("/{name}/health", makeHealthCheck(client)) r.Post("/{name}/start", makeStart(client)) r.Post("/{name}/stop", makeStop(client)) r.Post("/{name}/restart", makeRestart(client)) }) return r } func corsMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type") if r.Method == http.MethodOptions { w.WriteHeader(http.StatusNoContent) return } next.ServeHTTP(w, r) }) } func handleHealthz(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) } func makeListServers(c *k8sclient.Client) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { servers, err := c.ListServers(r.Context()) if err != nil { writeError(w, http.StatusInternalServerError, err) return } writeJSON(w, http.StatusOK, servers) } } func makeGetServer(c *k8sclient.Client) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { name := chi.URLParam(r, "name") server, err := c.GetServer(r.Context(), name) if err != nil { writeError(w, http.StatusNotFound, err) return } writeJSON(w, http.StatusOK, server) } } func makeCreateServer(c *k8sclient.Client) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req k8sclient.CreateServerRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, err) return } server, err := c.CreateServer(r.Context(), req) if err != nil { writeError(w, http.StatusInternalServerError, err) return } writeJSON(w, http.StatusCreated, server) } } func makeUpdateServer(c *k8sclient.Client) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { name := chi.URLParam(r, "name") var req k8sclient.UpdateServerRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, err) return } server, err := c.UpdateServer(r.Context(), name, req) if err != nil { writeError(w, http.StatusInternalServerError, err) return } writeJSON(w, http.StatusOK, server) } } func makeDeleteServer(c *k8sclient.Client) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { name := chi.URLParam(r, "name") if err := c.DeleteServer(r.Context(), name); err != nil { writeError(w, http.StatusInternalServerError, err) return } w.WriteHeader(http.StatusNoContent) } } func makeGetLogs(c *k8sclient.Client) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { name := chi.URLParam(r, "name") lines := int64(100) if l := r.URL.Query().Get("lines"); l != "" { if n, err := strconv.ParseInt(l, 10, 64); err == nil { lines = n } } logs, err := c.GetLogs(r.Context(), name, lines) if err != nil { writeError(w, http.StatusInternalServerError, err) return } writeJSON(w, http.StatusOK, map[string]string{"logs": logs}) } } func makeHealthCheck(c *k8sclient.Client) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { name := chi.URLParam(r, "name") server, err := c.GetServer(r.Context(), name) if err != nil { writeError(w, http.StatusNotFound, err) return } tcpReachable := false if server.NodePort > 0 { tcpReachable = health.Probe(nodeHost, server.NodePort) } writeJSON(w, http.StatusOK, map[string]interface{}{ "status": server.Status, "tcpReachable": tcpReachable, "nodePort": server.NodePort, }) } } func makeStart(c *k8sclient.Client) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { name := chi.URLParam(r, "name") if err := c.ScaleServer(r.Context(), name, 1); err != nil { writeError(w, http.StatusInternalServerError, err) return } writeJSON(w, http.StatusOK, map[string]string{"status": "started"}) } } func makeStop(c *k8sclient.Client) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { name := chi.URLParam(r, "name") if err := c.ScaleServer(r.Context(), name, 0); err != nil { writeError(w, http.StatusInternalServerError, err) return } writeJSON(w, http.StatusOK, map[string]string{"status": "stopped"}) } } func makeRestart(c *k8sclient.Client) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { name := chi.URLParam(r, "name") if err := c.RestartServer(r.Context(), name); err != nil { writeError(w, http.StatusInternalServerError, err) return } writeJSON(w, http.StatusOK, map[string]string{"status": "restarting"}) } } func writeJSON(w http.ResponseWriter, status int, v interface{}) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) json.NewEncoder(w).Encode(v) } func writeError(w http.ResponseWriter, status int, err error) { writeJSON(w, status, map[string]string{"error": err.Error()}) }