Files
IoT/legacy/doc/api/endpoints-v0.2.3.md
T

11 KiB
Raw Blame History

ЛЕГАСИ (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: любой непустой токен)


Содержание

  1. IoT Devices CRUD
  2. IoT Telemetry
  3. MQTT Auth (internal)
  4. MQTT ACL (internal)
  5. Admin Stats
  6. UI Pages
  7. Коды ошибок

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"
}

Логика:

  1. Если username == BridgeUsername → проверить BridgePassword (constant-time) → allow/deny
  2. Парсить username по первому "_" → namespace + deviceId
  3. Найти Secret iot-{deviceId} в namespace
  4. crypto/subtle.ConstantTimeCompare(password, secret["mqtt-password"])
  5. Проверить IoTDevice существует и enabled=true
  6. Обновить status.lastConnected (best-effort)
  7. Вернуть 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"