Files
sless/internal/api/router.go
T
Naeel 1e53766c46 feat(iot): Этапы 2-7 — MQTT auth, IoT API, EMQX, mqtt-bridge, E2E demo
Этап 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 ./... — ошибок нет
2026-04-04 09:45:23 +03:00

93 lines
5.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Изменено: 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)
}