Этап 2+4: internal/api/handler/iot_device_handler.go
- MQTTAuth: POST /internal/mqtt/auth (без JWT, для EMQX)
- CreateIoTDevice, ListIoTDevices, GetIoTDevice (c password), DeleteIoTDevice, UpdateIoTDevice
- crypto/subtle.ConstantTimeCompare против timing attacks
Этап 4: internal/api/router.go
- /v1/namespaces/{ns}/iot/devices CRUD
- /internal/mqtt/auth (без JWT middleware)
Этап 3: deployments/k8s/emqx.yaml
- EMQX 5.5.1, emqx.conf (HOCON) с HTTP auth backend
- Сервис exposure: 1883 (MQTT), 8083 (WS), 18083 (Dashboard)
Этап 3: iot/cmd/mqtt-bridge/main.go
- paho.mqtt.golang: подписка на +/telemetry/+
- amqp091-go: publish в iot.{namespace}.telemetry
- deployments/k8s/iot-mqtt-bridge.yaml
Этап 7: examples/IOT/ — E2E demo (main.tf, handler.py, README.md)
go.mod: добавлен github.com/eclipse/paho.mqtt.golang v1.5.1
go build ./... — ошибок нет
93 lines
5.6 KiB
Go
93 lines
5.6 KiB
Go
// Изменено: 2026-03-20 (function-service-split: добавлены /services маршруты)
|
||
// router.go — регистрация всех REST-маршрутов через gorilla/mux.
|
||
// Все маршруты защищены Bearer-токеном (middleware.Auth).
|
||
// Маршруты сгруппированы по /v1/namespaces/{namespace}/...
|
||
|
||
package api
|
||
|
||
import (
|
||
"log/slog"
|
||
"net/http"
|
||
|
||
"github.com/gorilla/mux"
|
||
|
||
"gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/api/handler"
|
||
"gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/api/middleware"
|
||
)
|
||
|
||
// NewRouter собирает gorilla/mux роутер со всеми маршрутами.
|
||
// /fn/{namespace}/{name} — публичный прокси для вызова функций, без auth.
|
||
// /v1/ — защищён JWT-аутентификацией (middleware.Auth).
|
||
func NewRouter(h *handler.Handler, log *slog.Logger) http.Handler {
|
||
r := mux.NewRouter()
|
||
|
||
// Публичный прокси для вызова HTTP-триггеров — без auth токена
|
||
// Все HTTP методы разрешены (GET/POST/PUT/... — решает сама функция)
|
||
r.PathPrefix("/fn/{namespace}/{name}").HandlerFunc(h.InvokeFunction)
|
||
|
||
// Суброутер для /v1 — все маршруты API
|
||
v1 := r.PathPrefix("/v1").Subrouter()
|
||
|
||
// Namespace lifecycle — вызывается провайдером ОДИН РАЗ при Configure()
|
||
// до создания любых ресурсов; идемпотентен.
|
||
v1.HandleFunc("/namespaces/{namespace}/ensure", h.EnsureNamespace).Methods(http.MethodPost)
|
||
|
||
// Functions CRUD
|
||
v1.HandleFunc("/namespaces/{namespace}/functions", h.ListFunctions).Methods(http.MethodGet)
|
||
v1.HandleFunc("/namespaces/{namespace}/functions", h.CreateFunction).Methods(http.MethodPost)
|
||
v1.HandleFunc("/namespaces/{namespace}/functions/{name}", h.GetFunction).Methods(http.MethodGet)
|
||
v1.HandleFunc("/namespaces/{namespace}/functions/{name}", h.UpdateFunction).Methods(http.MethodPut)
|
||
v1.HandleFunc("/namespaces/{namespace}/functions/{name}", h.DeleteFunction).Methods(http.MethodDelete)
|
||
|
||
// Invocation logs
|
||
v1.HandleFunc("/namespaces/{namespace}/functions/{name}/invocations", h.ListInvocations).Methods(http.MethodGet)
|
||
|
||
// Upload code — принимает zip, генерирует Dockerfile, кладёт tar.gz в S3, запускает сборку
|
||
v1.HandleFunc("/namespaces/{namespace}/functions/{name}/upload", h.UploadCode).Methods(http.MethodPost)
|
||
|
||
// Source code — возвращает файлы из tar.gz контекста сборки (без Dockerfile)
|
||
v1.HandleFunc("/namespaces/{namespace}/functions/{name}/source", h.GetSource).Methods(http.MethodGet)
|
||
|
||
// Services CRUD — long-running Deployment + URL (sless_service)
|
||
v1.HandleFunc("/namespaces/{namespace}/services", h.ListServices).Methods(http.MethodGet)
|
||
v1.HandleFunc("/namespaces/{namespace}/services", h.CreateService).Methods(http.MethodPost)
|
||
v1.HandleFunc("/namespaces/{namespace}/services/{name}", h.GetService).Methods(http.MethodGet)
|
||
v1.HandleFunc("/namespaces/{namespace}/services/{name}", h.UpdateService).Methods(http.MethodPut)
|
||
v1.HandleFunc("/namespaces/{namespace}/services/{name}", h.DeleteService).Methods(http.MethodDelete)
|
||
v1.HandleFunc("/namespaces/{namespace}/services/{name}/upload", h.UploadServiceCode).Methods(http.MethodPost)
|
||
v1.HandleFunc("/namespaces/{namespace}/services/{name}/source", h.GetServiceSource).Methods(http.MethodGet)
|
||
|
||
// Triggers CRUD
|
||
v1.HandleFunc("/namespaces/{namespace}/triggers", h.ListTriggers).Methods(http.MethodGet)
|
||
v1.HandleFunc("/namespaces/{namespace}/triggers", h.CreateTrigger).Methods(http.MethodPost)
|
||
v1.HandleFunc("/namespaces/{namespace}/triggers/{name}", h.GetTrigger).Methods(http.MethodGet)
|
||
v1.HandleFunc("/namespaces/{namespace}/triggers/{name}", h.UpdateTrigger).Methods(http.MethodPatch)
|
||
v1.HandleFunc("/namespaces/{namespace}/triggers/{name}", h.DeleteTrigger).Methods(http.MethodDelete)
|
||
|
||
// Jobs CRUD — одноразовые запуски функций
|
||
v1.HandleFunc("/namespaces/{namespace}/jobs", h.CreateJob).Methods(http.MethodPost)
|
||
v1.HandleFunc("/namespaces/{namespace}/jobs/{name}", h.GetJob).Methods(http.MethodGet)
|
||
v1.HandleFunc("/namespaces/{namespace}/jobs/{name}", h.DeleteJob).Methods(http.MethodDelete)
|
||
v1.HandleFunc("/namespaces/{namespace}/jobs/{name}/upload", h.UploadJobCode).Methods(http.MethodPost)
|
||
|
||
// IoT Devices CRUD — защищены JWT (как все /v1/ маршруты)
|
||
v1.HandleFunc("/namespaces/{namespace}/iot/devices", h.ListIoTDevices).Methods(http.MethodGet)
|
||
v1.HandleFunc("/namespaces/{namespace}/iot/devices", h.CreateIoTDevice).Methods(http.MethodPost)
|
||
v1.HandleFunc("/namespaces/{namespace}/iot/devices/{name}", h.GetIoTDevice).Methods(http.MethodGet)
|
||
v1.HandleFunc("/namespaces/{namespace}/iot/devices/{name}", h.DeleteIoTDevice).Methods(http.MethodDelete)
|
||
v1.HandleFunc("/namespaces/{namespace}/iot/devices/{name}", h.UpdateIoTDevice).Methods(http.MethodPatch)
|
||
|
||
// MQTT Auth — БЕЗ JWT. Вызывается EMQX при MQTT CONNECT из кластера.
|
||
// /internal/ недоступен снаружи (Ingress не проксирует /internal/).
|
||
r.HandleFunc("/internal/mqtt/auth", h.MQTTAuth).Methods(http.MethodPost)
|
||
|
||
// Цепочка middleware: logging → (auth только для /v1/) → router
|
||
// /fn/ — без auth, /v1/ — с auth.
|
||
// Используем gorilla/mux Use() чтобы auth применялся только к v1 суброутеру.
|
||
v1.Use(func(next http.Handler) http.Handler {
|
||
return middleware.Auth(log, next)
|
||
})
|
||
|
||
return middleware.Logging(log, r)
|
||
}
|