homelab/services/games-console/backend/internal/api/router.go
Dan V deb6c38d7b chore: commit homelab setup — deployment, services, orchestration, skill
- Add .gitignore: exclude compiled binaries, build artifacts, and Helm
  values files containing real secrets (authentik, prometheus)
- Add all Kubernetes deployment manifests (deployment/)
- Add services source code: ha-sync, device-inventory, games-console,
  paperclip, parts-inventory
- Add Ansible orchestration: playbooks, roles, inventory, cloud-init
- Add hardware specs, execution plans, scripts, HOMELAB.md
- Add skills/homelab/SKILL.md + skills/install.sh to preserve Copilot skill
- Remove previously-tracked inventory-cli binary from git index

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-09 08:10:32 +02:00

204 lines
5.8 KiB
Go

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()})
}