11 KiB
⛔⛔⛔ ЛЕГАСИ (2026-08-16) — СТАРЫЙ IoT (k8s-деплой). НЕ ПРИНИМАТЬ ВО ВНИМАНИЕ. Актуальное: HISTORY/2026-08-16-session-log.md
IoT REST API — Полная документация (v0.2.3)
Дата: 2026-04-12 Базовый URL: https://iot.kube5s.ru (через Ingress) или http://iot-operator.sless.svc:9090 (из кластера) Аутентификация: Bearer JWT в заголовке Authorization (authTestMode=true: любой непустой токен)
Содержание
- IoT Devices CRUD
- IoT Telemetry
- MQTT Auth (internal)
- MQTT ACL (internal)
- Admin Stats
- UI Pages
- Коды ошибок
IoT Devices CRUD
POST /v1/namespaces/{namespace}/iot/devices — Создание устройства
Создаёт IoTDevice CRD. Контроллер асинхронно генерирует MQTT credentials (Secret).
Headers:
Authorization: Bearer <token>
Content-Type: application/json
Request body:
{
"name": "sensor-01",
"device_id": "sensor-01",
"enabled": true,
"metadata": {
"model": "DHT22",
"location": "room-1"
}
}
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| name | string | да | Имя k8s объекта IoTDevice (уникальное в namespace) |
| device_id | string | да | ID устройства, pattern: ^[a-z0-9][a-z0-9-]*[a-z0-9]$ |
| enabled | bool | нет | default: true |
| metadata | map[string]string | нет | Произвольные метаданные |
Response 201 Created:
{
"name": "sensor-01",
"namespace": "tenant-abc",
"device_id": "sensor-01",
"enabled": true,
"phase": "",
"metadata": {"model": "DHT22", "location": "room-1"},
"created_at": "2026-04-12 14:30:00 UTC"
}
Response 409 Conflict: {"error": "iot device already exists"}
GET /v1/namespaces/{namespace}/iot/devices — Список устройств
Возвращает все IoTDevice в namespace. Пароли НЕ включены (security by design).
Headers:
Authorization: Bearer <token>
Response 200 OK:
[
{
"name": "sensor-01",
"namespace": "tenant-abc",
"device_id": "sensor-01",
"enabled": true,
"phase": "Active",
"mqtt_username": "tenant-abc_sensor-01",
"secret_name": "iot-sensor-01",
"topic_prefix": "tenant-abc/",
"metadata": {"model": "DHT22"},
"created_at": "2026-04-12 14:30:00 UTC"
}
]
GET /v1/namespaces/{namespace}/iot/devices/{name} — Получение устройства
Возвращает устройство включая mqtt_password из Secret. Используется для конфигурации физического устройства.
Headers:
Authorization: Bearer <token>
Response 200 OK:
{
"name": "sensor-01",
"namespace": "tenant-abc",
"device_id": "sensor-01",
"enabled": true,
"phase": "Active",
"mqtt_username": "tenant-abc_sensor-01",
"mqtt_password": "a1b2c3d4...hex64chars",
"secret_name": "iot-sensor-01",
"topic_prefix": "tenant-abc/",
"last_connected": "2026-04-12T14:35:00Z",
"metadata": {"model": "DHT22"},
"created_at": "2026-04-12 14:30:00 UTC"
}
Response 404: {"error": "iot device not found"}
Примечание: mqtt_password будет пустым если Secret ещё не создан (phase=Pending).
PATCH /v1/namespaces/{namespace}/iot/devices/{name} — Обновление устройства
Включает/отключает устройство.
Headers:
Authorization: Bearer <token>
Content-Type: application/json
Request body:
{
"enabled": false
}
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| enabled | bool | да | true=Active, false=Disabled |
Response 200 OK: полный объект устройства (без пароля).
Response 404: {"error": "iot device not found"}
DELETE /v1/namespaces/{namespace}/iot/devices/{name} — Удаление устройства
Удаляет IoTDevice CRD. Контроллер через finalizer удаляет Secret каскадно.
Headers:
Authorization: Bearer <token>
Response 204 No Content (пустое тело)
Response 404: {"error": "iot device not found"}
IoT Telemetry
GET /v1/namespaces/{namespace}/iot/telemetry — Чтение телеметрии
Возвращает записи телеметрии из per-tenant Postgres.
Headers:
Authorization: Bearer <token>
Query parameters:
| Параметр | Тип | Default | Описание |
|---|---|---|---|
| device | string | (все) | Фильтр по device_id |
| limit | int | 50 | Макс. кол-во записей (max 1000) |
Response 200 OK:
{
"items": [
{
"id": 1,
"device_id": "sensor-01",
"payload": {"temperature": 22.5, "humidity": 65},
"received_at": "2026-04-12T14:35:00Z",
"created_at": "2026-04-12T14:35:01Z"
}
],
"count": 1
}
Response 503: {"error": "IoT telemetry storage not configured"} (IOT_PG_DSN не задан)
MQTT Auth (internal)
POST /internal/mqtt/auth — Аутентификация MQTT клиента
Вызывается EMQX при каждом MQTT CONNECT. Без JWT. Доступен только из кластера.
Request body (от EMQX):
{
"username": "tenant-abc_sensor-01",
"password": "a1b2c3d4...hex64chars",
"clientid": "mqtt-client-123",
"peerhost": "10.0.1.5"
}
Логика:
- Если username == BridgeUsername → проверить BridgePassword (constant-time) → allow/deny
- Парсить username по первому "_" → namespace + deviceId
- Найти Secret
iot-{deviceId}в namespace crypto/subtle.ConstantTimeCompare(password, secret["mqtt-password"])- Проверить IoTDevice существует и enabled=true
- Обновить status.lastConnected (best-effort)
- Вернуть ACL правила для клиента
Response 200 (allow с ACL):
{
"result": "allow",
"acl": [
{"permission": "allow", "action": "publish", "topic": "tenant-abc/telemetry/sensor-01"},
{"permission": "allow", "action": "subscribe", "topic": "tenant-abc/telemetry/sensor-01"},
{"permission": "deny", "action": "all", "topic": "#"}
]
}
Response 200 (bridge allow):
{
"result": "allow"
}
Response 200 (deny):
{
"result": "deny"
}
Всегда HTTP 200. EMQX игнорирует non-200 ответы.
MQTT ACL (internal)
POST /internal/mqtt/acl — Авторизация pub/sub
Вызывается EMQX для каждого publish/subscribe. Без JWT.
Request body:
{
"username": "tenant-abc_sensor-01",
"clientid": "mqtt-client-123",
"action": "publish",
"topic": "tenant-abc/telemetry/sensor-01"
}
Логика:
- Bridge (clientid=sless-iot-bridge): только subscribe → allow. Publish → deny.
- Device: action на topic
{ns}/telemetry/{deviceId}→ allow. Всё остальное → deny.
Response 200: {"result": "allow"} или {"result": "deny"}
Admin Stats
GET /iot-admin/stats — Статистика администратора
Защищён токеном ADMIN_STATS_TOKEN (env). Не проходит через JWT middleware.
Headers:
Authorization: Bearer <ADMIN_STATS_TOKEN>
Response 200 OK:
{
"collected_at": "2026-04-12T14:40:00Z",
"postgres": {
"reachable": true,
"tenants": [
{
"namespace": "tenant-abc",
"total_count": 150,
"last_1h_count": 42,
"last_24h_count": 130,
"latest_rows": [...]
}
]
},
"sqs": {
"approximate_messages": 5,
"approximate_messages_not_visible": 2
},
"pods": {
"iot-mqtt-bridge": {
"name": "iot-mqtt-bridge-xxx",
"phase": "Running",
"ready": true,
"restarts": 0,
"age": "3h"
},
"iot-sqs-consumer": {
"name": "iot-sqs-consumer-yyy",
"phase": "Running",
"ready": true,
"restarts": 0,
"age": "3h"
}
}
}
Response 401: {"error": "unauthorized"}
Response 503: {"error": "admin stats not configured: ADMIN_STATS_TOKEN not set"}
UI Pages
GET /console — IoT Консоль
HTML-страница (go:embed) для управления устройствами и просмотра телеметрии. Включает MQTT WebSocket клиент для реального времени.
GET /iot-admin — IoT Admin Panel
HTML-страница (go:embed) администратора с графиками и мониторингом.
Коды ошибок
| Код | Значение | Когда |
|---|---|---|
| 200 | OK | Успешные GET, PATCH, MQTT auth/acl |
| 201 | Created | Успешный POST (создание устройства) |
| 204 | No Content | Успешный DELETE |
| 400 | Bad Request | Невалидный JSON, отсутствуют обязательные поля |
| 401 | Unauthorized | Невалидный/отсутствующий Bearer token |
| 404 | Not Found | Устройство не найдено |
| 409 | Conflict | Устройство уже существует |
| 500 | Internal Server Error | Ошибка k8s API или БД |
| 503 | Service Unavailable | IoTPG не сконфигурирован или AdminToken не задан |
Curl примеры
# Создать устройство
curl -X POST https://iot.kube5s.ru/v1/namespaces/test-ns/iot/devices \
-H "Authorization: Bearer test-token" \
-H "Content-Type: application/json" \
-d '{"name":"sensor-01","device_id":"sensor-01","enabled":true}'
# Список устройств
curl https://iot.kube5s.ru/v1/namespaces/test-ns/iot/devices \
-H "Authorization: Bearer test-token"
# Получить устройство с паролем
curl https://iot.kube5s.ru/v1/namespaces/test-ns/iot/devices/sensor-01 \
-H "Authorization: Bearer test-token"
# Включить/отключить
curl -X PATCH https://iot.kube5s.ru/v1/namespaces/test-ns/iot/devices/sensor-01 \
-H "Authorization: Bearer test-token" \
-H "Content-Type: application/json" \
-d '{"enabled":false}'
# Удалить
curl -X DELETE https://iot.kube5s.ru/v1/namespaces/test-ns/iot/devices/sensor-01 \
-H "Authorization: Bearer test-token"
# Телеметрия (последние 100)
curl "https://iot.kube5s.ru/v1/namespaces/test-ns/iot/telemetry?limit=100" \
-H "Authorization: Bearer test-token"
# Телеметрия по устройству
curl "https://iot.kube5s.ru/v1/namespaces/test-ns/iot/telemetry?device=sensor-01&limit=50" \
-H "Authorization: Bearer test-token"