// Пакет admin реализует административный API // для управления прокси без перезапуска. package admin import ( "encoding/json" "log/slog" "net/http" "strings" "backend/src/internal/auth" ) // Handler обрабатывает административные HTTP-эндпоинты. // Доступ защищён отдельным admin API-ключом. type Handler struct { logger *slog.Logger store auth.UserStore adminAPIKey string } // NewHandler создаёт обработчик административного API. func NewHandler(logger *slog.Logger, store auth.UserStore, adminAPIKey string) *Handler { return &Handler{ logger: logger, store: store, adminAPIKey: adminAPIKey, } } // ReloadHandler возвращает обработчик POST /admin/reload, // который перечитывает users.json без перезапуска прокси. func (h *Handler) ReloadHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { w.Header().Set("Allow", "POST") jsonResponse(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"}) return } if h.adminAPIKey == "" { jsonResponse(w, http.StatusForbidden, map[string]string{"error": "admin API not configured"}) return } header := r.Header.Get("Authorization") key := strings.TrimPrefix(header, "Bearer ") if key != h.adminAPIKey { jsonResponse(w, http.StatusUnauthorized, map[string]string{"error": "invalid admin key"}) return } if err := h.store.Reload(); err != nil { h.logger.Error("Ошибка перезагрузки конфигурации пользователей", "error", err) jsonResponse(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } h.logger.Info("Конфигурация пользователей перезагружена через admin API") jsonResponse(w, http.StatusOK, map[string]string{"status": "reloaded"}) } } // jsonResponse формирует HTTP-ответ в формате JSON. func jsonResponse(w http.ResponseWriter, code int, data any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) json.NewEncoder(w).Encode(data) }