Files
Proxy_for_codex/src/internal/admin/handler.go
fedos 5118914823 Добавить приоритетную очередь, аутентификацию и администрирование
- Приоритетная очередь для контроля параллельных запросов
- Аутентификация по API-ключу из URL (/auth/<key>/v1/...)
- Роли пользователей с белым списком моделей и ограничением контекста (num_ctx)
- Sliding window rate limiting
- Admin API для горячей перезагрузки users.json без перезапуска прокси
- Graceful shutdown с таймаутом завершения активных запросов
- Маскировка API-ключа в логах
- Подробная инструкция по установке для Windows и Linux (SETUP_WIN_SERVER.md)
2026-03-28 15:17:40 +03:00

70 lines
2.3 KiB
Go

// Пакет 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)
}