forked from templates/template-go-backend
- Приоритетная очередь для контроля параллельных запросов - Аутентификация по API-ключу из URL (/auth/<key>/v1/...) - Роли пользователей с белым списком моделей и ограничением контекста (num_ctx) - Sliding window rate limiting - Admin API для горячей перезагрузки users.json без перезапуска прокси - Graceful shutdown с таймаутом завершения активных запросов - Маскировка API-ключа в логах - Подробная инструкция по установке для Windows и Linux (SETUP_WIN_SERVER.md)
70 lines
2.3 KiB
Go
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)
|
|
}
|