101 lines
3.2 KiB
Go
101 lines
3.2 KiB
Go
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)
|
|
}
|