// Изменено: 2026-04-05 (добавлено поле IoTPG для IoT телеметрии) // Handler — общий контейнер зависимостей для всех REST handlers. // Все handlers получают доступ к k8s, S3 и Postgres через эту структуру. // Логирование через slog, маршрутизация через gorilla/mux. // // Этот файл — чистая инфраструктура: только Handler struct + вспомогательные функции. // Бизнес-логика по доменам — в отдельных файлах: // - namespace.go — EnsureNamespace (создание k8s namespace) // - functions.go — CRUD функций // - triggers.go — CRUD триггеров // - jobs.go — CRUD одноразовых запусков // - upload.go — загрузка кода, сборка образа // - invoke.go — прокси вызова функций // // Архитектура namespace: // - Namespace создаётся ОДИН РАЗ через EnsureNamespace при инициализации провайдера. // - Resource-хендлеры namespace не трогают — это не их ответственность. // - defaultNamespace используется только как fallback для dev/тестов. package handler import ( "encoding/json" "log/slog" "net/http" "github.com/gorilla/mux" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" "gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/storage/iotpg" "gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/storage/postgres" "gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/storage/s3" ) // defaultNamespace — fallback namespace для dev/тестов. // В production namespace определяется из JWT-токена провайдером. // Все обращения к "default" строке идут через эту константу — одно место замены. const defaultNamespace = "default" // Handler содержит зависимости для всех REST-обработчиков. type Handler struct { K8s client.Client Scheme *runtime.Scheme S3 *s3.Client PG *postgres.Store // IoTPG — хранилище IoT телеметрии (per-tenant Postgres). nil если IOT_PG_DSN не задан. IoTPG *iotpg.IoTPostgresStore Log *slog.Logger } // writeJSON отправляет JSON-ответ с указанным статусом. func writeJSON(w http.ResponseWriter, status int, v any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(v) } // errResp возвращает структуру ошибки для JSON. func errResp(msg string) map[string]string { return map[string]string{"error": msg} } // pathVar читает переменную пути из gorilla/mux. func pathVar(r *http.Request, key string) string { return mux.Vars(r)[key] } // namespace читает {namespace} из пути URL. // Fallback — defaultNamespace (используется только в dev/тестах). func namespace(r *http.Request) string { if ns := mux.Vars(r)["namespace"]; ns != "" { return ns } return defaultNamespace }