restructure: console→client-console, add admin-console skeleton, move docs to doc/
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user