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)
This commit is contained in:
69
src/internal/admin/handler.go
Normal file
69
src/internal/admin/handler.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// Пакет 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)
|
||||
}
|
||||
Reference in New Issue
Block a user