package api import ( "encoding/json" "net/http" "strings" "admin-console/internal/metrics" "admin-console/internal/model" ) // Server — HTTP-сервер admin-console. type Server struct { adminToken string k8s *metrics.K8sClient } func NewServer(adminToken string) *Server { k8s, err := metrics.NewK8sClient() if err != nil { // вне кластера — работаем без k8s (для локальной разработки) k8s = nil } return &Server{adminToken: adminToken, k8s: k8s} } func (s *Server) RegisterRoutes(mux *http.ServeMux) { // статика UI mux.Handle("/admin/", http.StripPrefix("/admin/", http.FileServer(http.Dir("ui")))) // API mux.HandleFunc("/admin/api/health", s.handleHealth) mux.HandleFunc("/admin/api/namespaces", s.authMiddleware(s.handleNamespaces)) mux.HandleFunc("/admin/api/usage", s.authMiddleware(s.handleUsage)) mux.HandleFunc("/admin/api/users", s.authMiddleware(s.handleUsers)) } // authMiddleware проверяет Bearer-токен из заголовка Authorization. func (s *Server) authMiddleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { auth := r.Header.Get("Authorization") if !strings.HasPrefix(auth, "Bearer ") || strings.TrimPrefix(auth, "Bearer ") != s.adminToken { writeJSON(w, http.StatusUnauthorized, model.ErrResp("unauthorized")) return } next(w, r) } } func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) } // handleNamespaces возвращает список клиентских namespace-ов с их метриками. func (s *Server) handleNamespaces(w http.ResponseWriter, r *http.Request) { if s.k8s == nil { writeJSON(w, http.StatusServiceUnavailable, model.ErrResp("k8s unavailable")) return } ns, err := s.k8s.ListClientNamespaces(r.Context()) if err != nil { writeJSON(w, http.StatusInternalServerError, model.ErrResp(err.Error())) return } writeJSON(w, http.StatusOK, map[string]any{"namespaces": ns}) } // handleUsage возвращает агрегированные метрики использования для бухгалтерии. func (s *Server) handleUsage(w http.ResponseWriter, r *http.Request) { if s.k8s == nil { writeJSON(w, http.StatusServiceUnavailable, model.ErrResp("k8s unavailable")) return } usage, err := s.k8s.CollectUsage(r.Context()) if err != nil { writeJSON(w, http.StatusInternalServerError, model.ErrResp(err.Error())) return } writeJSON(w, http.StatusOK, map[string]any{"usage": usage}) } // handleUsers возвращает список пользователей из secrets в клиентских namespace-ах. func (s *Server) handleUsers(w http.ResponseWriter, r *http.Request) { if s.k8s == nil { writeJSON(w, http.StatusServiceUnavailable, model.ErrResp("k8s unavailable")) return } users, err := s.k8s.ListUsers(r.Context()) if err != nil { writeJSON(w, http.StatusInternalServerError, model.ErrResp(err.Error())) return } writeJSON(w, http.StatusOK, map[string]any{"users": users}) } func writeJSON(w http.ResponseWriter, status int, v any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) json.NewEncoder(w).Encode(v) }