Commit Graph
14 Commits
Author SHA1 Message Date
Repinoid 7c80c7b7f8 test(iot): юнит-тесты валидации идентификаторов и разбора DSN (P1-11)
- handler/devices_test.go: validateIdentifier (charset/лимиты, запрет _ + # /).
- iotpg/dsn_test.go: replaceDSNDatabase (вкл. sslrootcert со слэшем, ошибка для
  key=value) + safeNamespaceRe.
- gofmt для затронутых файлов. go test ./... зелёный.
2026-10-10 19:23:09 +03:00
Repinoid 3a73207abd fix(iot): 409 различает конфликт name vs device_id (ревью)
store.Create классифицирует 23505 по имени constraint: (namespace,device_id) →
ErrDeviceIDExists, иначе ErrAlreadyExists. Handler отдаёт разные 409-сообщения.
Помогает Terraform понять, какое поле конфликтует.
2026-10-10 19:21:47 +03:00
Repinoid 3b7c8a0a63 feat(iot): bcrypt-хэш паролей устройств и бриджа + EMQX на bcrypt (P0-3 ч.1)
- handler CreateIoTDevice: пароль устройства хранится bcrypt-хэшем (hashPassword),
  открытый пароль отдаётся один раз в 201.
- store.EnsureBridgeDevice: пароль бриджа тоже bcrypt-хэшем.
- emqx/emqx.conf: password_hash_algorithm name=bcrypt, salt_rounds=10 (подтверждено
  по emqx/emqx: HOCON-ключ salt_rounds; Go bcrypt.DefaultCost=10).
- Добавлена зависимость golang.org/x/crypto v0.42.0.
2026-10-10 19:21:10 +03:00
Repinoid 59bd743c87 feat(iot): PATCH обновляет metadata (Terraform)
iotDeviceUpdateRequest получил поле metadata; UpdateIoTDevice обновляет переданные
поля (enabled и/или metadata). Добавлен store.UpdateMetadata. Хотя бы одно поле
обязательно. Раньше metadata нельзя было изменить (только enabled).
2026-10-10 19:18:01 +03:00
Repinoid 38e3f65c21 fix(iot): пагинация — tiebreaker ts DESC,id DESC + потолок limit; точный лог (Этап 5)
- QueryTelemetry: ORDER BY ts DESC, id DESC (был без tiebreaker → нестабильная
  пагинация при одинаковых ts).
- ListIoTDevices: limit дефолт 100, потолок 1000 (ошибка парсинга больше не даёт
  безлимитный запрос).
- Логи 'batch dropped' → 'валидные строки — построчно' (после fallback батч НЕ
  теряется целиком).
2026-10-10 19:17:25 +03:00
Repinoid 4a0372ff05 chore(iot): лимит тела запроса + запрет неизвестных полей + таймауты сервера (Этап 5)
- decodeJSON: http.MaxBytesReader (64 КиБ) + DisallowUnknownFields; используется
  в Create/Update устройства. Ловит опечатки в полях (важно для Terraform).
- http.Server: добавлены ReadTimeout=15с, WriteTimeout=60с, IdleTimeout=120с
  (был только ReadHeaderTimeout).
2026-10-10 19:14:47 +03:00
Repinoid e429f11e7f chore(iot): убрать мёртвый mqtt_auth.go + constant-time админ-токен (Этап 5)
- Удалён internal/service/api/handler/mqtt_auth.go: методы MQTTAuth/MQTTAcl
  нигде не подключены (мёртвый код; при подключении небезопасен — доверял
  клиентскому clientid и разрешал subscribe на '#').
- admin.go: сравнение ADMIN_STATS_TOKEN через subtle.ConstantTimeCompare.
2026-10-10 19:14:16 +03:00
Repinoid 8335afb617 fix(iot): валидация namespace/name/device_id (запрет _, +, #, /)
- Добавлен identRe ^[a-zA-Z0-9][a-zA-Z0-9-]*$ и validateIdentifier.
- CreateIoTDevice теперь проверяет namespace (≤56), name (≤64), device_id (≤64).
- Причины: _ ломает username «namespace_device_id» (auth EMQX LIMIT 1 без
  ORDER BY), +/# — wildcard в ACL-топике, / — лишний уровень топика.
2026-10-10 18:50:56 +03:00
Repinoid 2aef884d02 fix(iot): telemetry_topic в ответе + нормализация metadata
- iotDeviceResponse: добавлено telemetry_topic = «ns/telemetry/device» (явный
  топик для Terraform; topic_prefix = «ns/» был неполным).
- metadata теперь без omitempty и всегда «{}» при пустом — убран drift
  «{}» против null (ревью 2026-10-10, «Что стабилизировать для Terraform»).
2026-10-10 18:49:40 +03:00
Repinoid e2bb642df2 fix(iot): created_at в RFC3339 + перечитывание после Create
- deviceToResponse: created_at теперь RFC3339 (был «2006-01-02 15:04:05 UTC»)
  — единый формат с last_connected, без drift для Terraform.
- CreateIoTDevice: после INSERT перечитываем устройство из БД (GetByName),
  чтобы в 201 отдавался реальный created_at (in-memory структура его не имеет).
2026-10-10 18:48:52 +03:00
Repinoid 76f0230f5d fix(iot): отдавать MQTT-пароль при создании + писать phase в БД
- handler/devices.go: CreateIoTDevice отдаёт mqtt_password один раз в 201
  (было deviceToResponse(device, "") — пароль терялся, устройство нельзя
  подключить; блокер для Terraform). Заголовок поправлен (GET — без пароля).
- store/devices.go: Create пишет phase явно через phaseOf(enabled) — иначе
  enabled=false лежал в БД как 'Active' (DEFAULT), расходясь с POST/GET.
2026-10-10 18:48:07 +03:00
Repinoid a4e786d03b P0-3 (часть 2): не отдавать пароль устройства повторно через GET
GetIoTDevice больше не возвращает mqtt_password (deviceToResponse получает ).
Пароль остаётся доступен только один раз — при создании устройства.
Хэширование паролей в БД + перевод EMQX на алгоритм хэша (часть 1) требует
изменения emqx.conf и миграции — отдельным шагом.
2026-10-06 19:06:02 +03:00
“Naeel” 68becfd6bc fix: review findings (async SQS dispatcher, backoff, batch insert, shutdown order, pagination, HMAC) + loadtest fixes; v0.1.6 2026-08-16 18:24:09 +04:00
“Naeel” 7647627052 feat: монолит iot-service (Фаза 1) — config/store/api/bridge/consumer без k8s, устройства в PG, новые Dockerfile/Makefile 2026-08-16 10:07:51 +04:00