Добавить приоритетную очередь, аутентификацию и администрирование

- Приоритетная очередь для контроля параллельных запросов
- Аутентификация по 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:
2026-03-28 15:15:51 +03:00
parent a647921841
commit 5118914823
18 changed files with 1488 additions and 47 deletions

View File

@@ -10,7 +10,9 @@ import (
"time"
)
// Универсальная функция получения значения из окружения с парсером.
// GetEnvAs загружает переменную окружения key, парсит её функцией parse
// и возвращает результат. При отсутствии переменной или ошибке парсинга
// возвращает defaultVal.
func GetEnvAs[T any](key string, defaultVal T, parse func(string) (T, error)) T {
raw, ok := os.LookupEnv(key)
if !ok || strings.TrimSpace(raw) == "" {
@@ -18,23 +20,20 @@ func GetEnvAs[T any](key string, defaultVal T, parse func(string) (T, error)) T
}
v, err := parse(raw)
if err != nil {
// одна строка лога достаточно — не дублируем
slog.Warn(fmt.Sprintf("cannot parse env %q, using default", key),
slog.Warn(fmt.Sprintf("Некорректное значение переменной %q, используется значение по умолчанию", key),
"key", key, "raw", raw, "default", defaultVal, "error", err)
return defaultVal
}
return v
}
/* ====== БАЗОВЫЕ АДАПТЕРЫ ====== */
// --- Парсеры для базовых типов ---
func ParseString(s string) (string, error) { // просто возвращаем trimmed string
return strings.TrimSpace(s), nil
}
func ParseBool(s string) (bool, error) {
return strconv.ParseBool(strings.TrimSpace(s))
}
func ParseString(s string) (string, error) { return strings.TrimSpace(s), nil }
func ParseBool(s string) (bool, error) { return strconv.ParseBool(strings.TrimSpace(s)) }
func ParseFloat64(s string) (float64, error) { return strconv.ParseFloat(strings.TrimSpace(s), 64) }
func ParseURL(s string) (*url.URL, error) { return url.Parse(strings.TrimSpace(s)) }
func ParseTimeRFC3339(s string) (time.Time, error) { return time.Parse(time.RFC3339, strings.TrimSpace(s)) }
func ParseInt(s string) (int, error) {
i64, err := strconv.ParseInt(strings.TrimSpace(s), 10, 0)
@@ -50,26 +49,14 @@ func ParseUint(s string) (uint, error) {
return uint(u64), err
}
func ParseFloat64(s string) (float64, error) {
return strconv.ParseFloat(strings.TrimSpace(s), 64)
}
// ParseDuration парсит строку длительности ("150ms", "2s", "1m", "24h").
func ParseDuration(s string) (time.Duration, error) {
// поддерживает "150ms", "2s", "1m", "24h"
return time.ParseDuration(strings.TrimSpace(s))
}
func ParseTimeRFC3339(s string) (time.Time, error) {
return time.Parse(time.RFC3339, strings.TrimSpace(s))
}
// --- Парсеры для списков ---
func ParseURL(s string) (*url.URL, error) {
return url.Parse(strings.TrimSpace(s))
}
/* ====== СПИСКИ (CSV/SEPARATOR) ====== */
// Универсальный адаптер для списков с произвольным парсером элемента.
// MakeListParser создаёт парсер для строки-списка с заданным разделителем.
func MakeListParser[T any](sep string, itemParser func(string) (T, error)) func(string) ([]T, error) {
return func(s string) ([]T, error) {
s = strings.TrimSpace(s)
@@ -82,7 +69,7 @@ func MakeListParser[T any](sep string, itemParser func(string) (T, error)) func(
for _, p := range parts {
v, err := itemParser(p)
if err != nil {
return nil, fmt.Errorf("cannot parse list item %q: %w", p, err)
return nil, fmt.Errorf("ошибка парсинга элемента %q: %w", p, err)
}
out = append(out, v)
}
@@ -90,7 +77,8 @@ func MakeListParser[T any](sep string, itemParser func(string) (T, error)) func(
}
}
// Частые случаи:
var ParseCSVStrings = MakeListParser[string](",", ParseString)
var ParseCSVInts = MakeListParser[int](",", ParseInt)
var ParseCSVFloat64 = MakeListParser[float64](",", ParseFloat64)
var (
ParseCSVStrings = MakeListParser[string](",", ParseString)
ParseCSVInts = MakeListParser[int](",", ParseInt)
ParseCSVFloat64 = MakeListParser[float64](",", ParseFloat64)
)