docs: copilot-instructions, архитектура, API, progress, decisions
This commit is contained in:
@@ -0,0 +1,126 @@
|
|||||||
|
# Правила работы агента в проекте IoT
|
||||||
|
|
||||||
|
## ГЛАВНОЕ ПРАВИЛО
|
||||||
|
|
||||||
|
**НЕ "СОВЕРШЕНСТВОВАТЬ" РАБОЧИЙ КОД БЕЗ ЯВНОГО УКАЗАНИЯ.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ЗАПРЕТ НА ВЫДУМКИ
|
||||||
|
|
||||||
|
**КАТЕГОРИЧЕСКИ ЗАПРЕЩАЕТСЯ придумывать, догадываться или предполагать:**
|
||||||
|
- значения параметров, которые не видны в коде или документации
|
||||||
|
- допустимые значения enum/ролей/типов — если не взяты из реального источника
|
||||||
|
- поведение API, провайдеров, библиотек — если не подтверждено кодом или документацией
|
||||||
|
- любые факты о системе, которые агент "знает" из общих соображений
|
||||||
|
|
||||||
|
**Если информации нет — спросить у пользователя. Не угадывать.**
|
||||||
|
|
||||||
|
Если код работает — не трогать. Никаких:
|
||||||
|
- рефакторингов "попутно"
|
||||||
|
- улучшений стиля
|
||||||
|
- добавления комментариев / docstring
|
||||||
|
- переименований переменных
|
||||||
|
- "пока уж заодно поправлю"
|
||||||
|
|
||||||
|
Делай только то, о чём явно попросили. Ничего лишнего.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## О проекте
|
||||||
|
|
||||||
|
IoT managed service — отдельная репа, вынесенная из sless.
|
||||||
|
Go модуль: `gitea.services.ngcloud.ru/Nail/IoT`
|
||||||
|
Репа: https://gitea.services.ngcloud.ru/Nail/IoT
|
||||||
|
|
||||||
|
### Компоненты (3 бинарника из одного образа):
|
||||||
|
1. **iot-operator** (`cmd/iot-operator/`) — controller-manager (IoTDevice CRD) + REST API на :9090
|
||||||
|
2. **mqtt-bridge** (`cmd/mqtt-bridge/`) — MQTT (EMQX) → Kafka bridge
|
||||||
|
3. **kafka-consumer** (`cmd/kafka-consumer/`) — Kafka → Postgres pipeline
|
||||||
|
|
||||||
|
### Стек:
|
||||||
|
- Go 1.25, controller-runtime v0.14, gorilla/mux
|
||||||
|
- CRD: `iot.kube5s.ru/v1alpha1` (IoTDevice)
|
||||||
|
- EMQX — MQTT брокер, Kafka — очередь телеметрии
|
||||||
|
- PostgreSQL — per-tenant databases для телеметрии
|
||||||
|
- Docker Hub: `naeel/iot-operator`
|
||||||
|
|
||||||
|
### Структура:
|
||||||
|
```
|
||||||
|
cmd/iot-operator/ — точка входа (controller + API сервер)
|
||||||
|
cmd/mqtt-bridge/ — MQTT→Kafka bridge
|
||||||
|
cmd/kafka-consumer/ — Kafka→Postgres
|
||||||
|
api/v1alpha1/ — CRD Go types (IoTDevice)
|
||||||
|
controllers/ — IoTDevice reconciler
|
||||||
|
internal/api/ — REST handlers, router, middleware, UI (go:embed)
|
||||||
|
internal/storage/ — iotpg (per-tenant Postgres)
|
||||||
|
config/crd/ — CRD YAML manifests
|
||||||
|
deployments/k8s/ — k8s deployment YAMLs
|
||||||
|
doc/ — документация
|
||||||
|
examples/ — примеры (Terraform, handler.py)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Комментарии в коде
|
||||||
|
|
||||||
|
Комментарии — обязательны:
|
||||||
|
- В начале каждого файла при создании или правке — дата и время изменения
|
||||||
|
- На каждой функции/методе — краткое назначение
|
||||||
|
- На нетривиальной логике — **почему** сделано именно так (не "что делает", а "зачем")
|
||||||
|
|
||||||
|
Цель: любой агент в новом чате должен понять логику без дополнительных вопросов.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Темп работы
|
||||||
|
|
||||||
|
Не спешить. Перед каждым шагом — убедиться что предыдущий понят и согласован.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Документация
|
||||||
|
|
||||||
|
Всё важное фиксировать в `doc/`:
|
||||||
|
- `doc/architecture/` — архитектура, стек, схемы
|
||||||
|
- `doc/api/` — дизайн API
|
||||||
|
- `doc/decisions/` — принятые решения с обоснованием
|
||||||
|
- `doc/infrastructure/` — инфраструктура, кластер, сервисы
|
||||||
|
- `doc/errors/` — ошибки и как решили
|
||||||
|
- `doc/progress.md` — трекер задач
|
||||||
|
|
||||||
|
Обновлять после каждого значимого изменения.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Именование
|
||||||
|
|
||||||
|
Имена должны быть **уникальными и осмысленными по всему проекту**:
|
||||||
|
- имена файлов
|
||||||
|
- имена функций/методов
|
||||||
|
- имена переменных/констант
|
||||||
|
- имена ресурсов (Terraform, Kubernetes и т.д.)
|
||||||
|
|
||||||
|
Цель: чтобы поиск по проекту находил нужные сущности без неоднозначности, а имя сразу отражало назначение.
|
||||||
|
|
||||||
|
Запрещены безликие и повторяющиеся имена вида `handler.py`, `handle`, `data`, `value`, `temp` без контекста.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Лог мышления (обязательно)
|
||||||
|
|
||||||
|
Каждый агент в каждом чате **обязан** вести лог своих рассуждений:
|
||||||
|
- Папка: `doc/thinking/`
|
||||||
|
- Файл: `ГГГГ-ММ-ДД.md` (по дате сессии)
|
||||||
|
- В начале файла указать имя агента и модель
|
||||||
|
- Если файл на текущую дату уже существует — дописывать в конец, добавив разделитель `---` и имя агента
|
||||||
|
- Записывать **полный** ход мыслей: что анализирую, какие гипотезы, что нашёл, что отбросил, к чему пришёл, почему
|
||||||
|
- Записывать **до** начала действий (план) и **после** (результат)
|
||||||
|
|
||||||
|
Цель: пользователь должен видеть весь процесс рассуждений в читаемом виде.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Git
|
||||||
|
|
||||||
|
Коммитить и пушить после каждого завершённого этапа.
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# IoT API Endpoints
|
||||||
|
|
||||||
|
## Публичные (без auth)
|
||||||
|
|
||||||
|
| Метод | Путь | Описание |
|
||||||
|
|-------|------|----------|
|
||||||
|
| GET | `/console` | IoT Консоль (HTML SPA) |
|
||||||
|
| GET | `/iot-admin` | Страница администратора (HTML) |
|
||||||
|
|
||||||
|
## Защищённые JWT (/v1/)
|
||||||
|
|
||||||
|
| Метод | Путь | Описание |
|
||||||
|
|-------|------|----------|
|
||||||
|
| POST | `/v1/namespaces/{ns}/iot/devices` | Создать IoT устройство |
|
||||||
|
| GET | `/v1/namespaces/{ns}/iot/devices` | Список устройств (без паролей) |
|
||||||
|
| GET | `/v1/namespaces/{ns}/iot/devices/{name}` | Устройство + MQTT password |
|
||||||
|
| DELETE | `/v1/namespaces/{ns}/iot/devices/{name}` | Удалить устройство |
|
||||||
|
| PATCH | `/v1/namespaces/{ns}/iot/devices/{name}` | Обновить (enabled) |
|
||||||
|
| GET | `/v1/namespaces/{ns}/iot/telemetry` | Телеметрия (?device=&limit=) |
|
||||||
|
|
||||||
|
## Внутренние (без JWT, только из кластера)
|
||||||
|
|
||||||
|
| Метод | Путь | Описание |
|
||||||
|
|-------|------|----------|
|
||||||
|
| POST | `/internal/mqtt/auth` | MQTT auth для EMQX |
|
||||||
|
| POST | `/internal/mqtt/acl` | MQTT ACL для EMQX |
|
||||||
|
|
||||||
|
## Администратор (ADMIN_STATS_TOKEN)
|
||||||
|
|
||||||
|
| Метод | Путь | Описание |
|
||||||
|
|-------|------|----------|
|
||||||
|
| GET | `/iot-admin/stats` | JSON статистика (PG, Kafka lag, pods) |
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# IoT Managed Service — Архитектура
|
||||||
|
|
||||||
|
## Общая схема
|
||||||
|
|
||||||
|
```
|
||||||
|
IoT Device (MQTT CONNECT)
|
||||||
|
│ username="{ns}_{deviceId}", password=hex
|
||||||
|
▼
|
||||||
|
EMQX 5.5.1 (sless/emqx)
|
||||||
|
│ POST /internal/mqtt/auth → iot-operator:9090
|
||||||
|
│ POST /internal/mqtt/acl → iot-operator:9090
|
||||||
|
▼
|
||||||
|
│ MQTT PUBLISH → topic: "{ns}/telemetry/{deviceId}"
|
||||||
|
▼
|
||||||
|
iot-mqtt-bridge (cmd/mqtt-bridge)
|
||||||
|
│ подписка на "+/telemetry/+"
|
||||||
|
│ → Kafka topic "iot.telemetry"
|
||||||
|
▼
|
||||||
|
Kafka
|
||||||
|
▼
|
||||||
|
iot-kafka-consumer (cmd/kafka-consumer)
|
||||||
|
│ consumer group "iot-pg-consumer"
|
||||||
|
│ → per-tenant Postgres DB
|
||||||
|
▼
|
||||||
|
IoT Postgres (iot-postgres.sless.svc)
|
||||||
|
│ DB: tenant_{namespace}
|
||||||
|
│ Table: iot_telemetry
|
||||||
|
▼
|
||||||
|
REST API (iot-operator:9090)
|
||||||
|
│ GET /v1/namespaces/{ns}/iot/telemetry
|
||||||
|
▼
|
||||||
|
Пользователь (Terraform / UI Console)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Компоненты
|
||||||
|
|
||||||
|
| Компонент | Бинарник | Порт | Назначение |
|
||||||
|
|-----------|----------|------|------------|
|
||||||
|
| iot-operator | cmd/iot-operator | :9090 (API), :8081 (health) | Controller-manager + REST API |
|
||||||
|
| mqtt-bridge | cmd/mqtt-bridge | — | MQTT→Kafka bridge |
|
||||||
|
| kafka-consumer | cmd/kafka-consumer | — | Kafka→Postgres pipeline |
|
||||||
|
|
||||||
|
## CRD
|
||||||
|
|
||||||
|
- **IoTDevice** (`iot.kube5s.ru/v1alpha1`)
|
||||||
|
- Создание: пользователь через API / Terraform
|
||||||
|
- Controller генерирует MQTT credentials → k8s Secret
|
||||||
|
- Secret удаляется каскадно через OwnerReference
|
||||||
|
|
||||||
|
## Хранение
|
||||||
|
|
||||||
|
- **IoT Postgres** — отдельный от sless PG
|
||||||
|
- Management DB: `iot_platform` (таблица `tenant_credentials`)
|
||||||
|
- Per-tenant DB: `tenant_{namespace}` (таблица `iot_telemetry`)
|
||||||
|
|
||||||
|
## Аутентификация
|
||||||
|
|
||||||
|
- REST API (/v1/): Bearer JWT (middleware.Auth)
|
||||||
|
- MQTT Auth (/internal/): вызывается EMQX, без JWT
|
||||||
|
- Admin (/iot-admin/stats): ADMIN_STATS_TOKEN
|
||||||
|
|
||||||
|
## Docker образ
|
||||||
|
|
||||||
|
- `naeel/iot-operator` (Docker Hub)
|
||||||
|
- Все 3 бинарника в одном образе
|
||||||
|
- `command: ["/iot-operator"]` или `["/mqtt-bridge"]` или `["/kafka-consumer"]`
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Решение: Вынос IoT в отдельную репу
|
||||||
|
|
||||||
|
**Дата:** 2026-04-12
|
||||||
|
**Статус:** Принято и реализовано
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
|
||||||
|
IoT managed service был частью sless (основной serverless operator).
|
||||||
|
Код IoT жил в нескольких местах:
|
||||||
|
- `iot/` — CRD types, controller, cmd (bridge, consumer)
|
||||||
|
- `internal/storage/iotpg/` — Postgres store
|
||||||
|
- `internal/api/handler/iot_*.go` — REST handlers
|
||||||
|
- `internal/api/ui/iot-*.html` — UI
|
||||||
|
- `main.go`, `Dockerfile` — IoT интегрирован в основной бинарник
|
||||||
|
|
||||||
|
## Проблемы
|
||||||
|
|
||||||
|
1. IoT и sless — разные домены с разными циклами разработки
|
||||||
|
2. Сборка sless включала IoT — лишние зависимости (Kafka, MQTT)
|
||||||
|
3. Деплой любого IoT изменения требовал пересборки всего sless
|
||||||
|
|
||||||
|
## Решение
|
||||||
|
|
||||||
|
Вынести IoT в отдельную репу `gitea.services.ngcloud.ru/Nail/IoT`:
|
||||||
|
- Свой Go модуль, go.mod, Dockerfile
|
||||||
|
- 3 бинарника в одном образе (`naeel/iot-operator`)
|
||||||
|
- Свой controller-manager + REST API (cmd/iot-operator)
|
||||||
|
- Независимый CI/CD цикл
|
||||||
|
|
||||||
|
## Что перенесено
|
||||||
|
|
||||||
|
- CRD types, controller, mqtt-bridge, kafka-consumer — as-is
|
||||||
|
- Handler struct упрощён (убраны S3, PG от sless)
|
||||||
|
- Router — только IoT маршруты
|
||||||
|
- Middleware (auth, logging) — скопированы как есть
|
||||||
|
- K8s manifests, CRD YAML, документация, примеры
|
||||||
|
|
||||||
|
## Риски
|
||||||
|
|
||||||
|
- IoT код в sless нужно будет убрать (или оставить заглушки)
|
||||||
|
- K8s manifests могут требовать обновления (новое имя образа)
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# IoT Managed Service — Трекер прогресса
|
||||||
|
|
||||||
|
## 2026-04-12: Перенос из sless в отдельную репу
|
||||||
|
|
||||||
|
### Выполнено
|
||||||
|
- [x] Создана репа https://gitea.services.ngcloud.ru/Nail/IoT
|
||||||
|
- [x] Go модуль: `gitea.services.ngcloud.ru/Nail/IoT`
|
||||||
|
- [x] Перенесены все IoT-компоненты из sless:
|
||||||
|
- `iot/api/v1alpha1/` → `api/v1alpha1/` (CRD types: IoTDevice)
|
||||||
|
- `iot/controllers/` → `controllers/` (IoTDevice reconciler)
|
||||||
|
- `iot/cmd/mqtt-bridge/` → `cmd/mqtt-bridge/` (MQTT→Kafka)
|
||||||
|
- `iot/cmd/kafka-consumer/` → `cmd/kafka-consumer/` (Kafka→Postgres)
|
||||||
|
- `internal/storage/iotpg/` → `internal/storage/iotpg/` (per-tenant PG)
|
||||||
|
- `internal/api/handler/iot_*.go` → `internal/api/handler/` (REST handlers)
|
||||||
|
- `internal/api/ui/iot-*.html` → `internal/api/ui/` (embedded HTML)
|
||||||
|
- `internal/api/middleware/` → `internal/api/middleware/` (auth, logging)
|
||||||
|
- `deployments/k8s/iot-*.yaml` + `kafka.yaml` → `deployments/k8s/`
|
||||||
|
- `config/crd/bases/` → `config/crd/bases/`
|
||||||
|
- `doc/iot/`, `examples/IOT/` → `doc/`, `examples/`
|
||||||
|
- [x] Созданы новые файлы:
|
||||||
|
- `cmd/iot-operator/main.go` — точка входа (controller-manager + REST API)
|
||||||
|
- `internal/api/handler/handler.go` — упрощённый Handler (без S3/PG от sless)
|
||||||
|
- `internal/api/router.go` — только IoT маршруты
|
||||||
|
- `Dockerfile` — multi-stage build, 3 бинарника, distroless
|
||||||
|
- `Makefile` — build/docker/deploy команды
|
||||||
|
- `.gitignore`
|
||||||
|
- `.github/copilot-instructions.md`
|
||||||
|
- [x] Все import paths заменены: `sless/iot/...` → `Nail/IoT/...`
|
||||||
|
- [x] Все 3 бинарника собираются без ошибок (go build ./...)
|
||||||
|
- [x] Запушено в Gitea
|
||||||
|
|
||||||
|
### Следующие шаги
|
||||||
|
- [ ] Docker build + push на Docker Hub (`naeel/iot-operator`)
|
||||||
|
- [ ] Обновить k8s manifests для нового образа
|
||||||
|
- [ ] Деплой в кластер
|
||||||
|
- [ ] E2E тест: создание устройства → MQTT → Kafka → Postgres → API
|
||||||
|
- [ ] Убрать IoT-код из sless (опционально, после подтверждения что всё работает)
|
||||||
Reference in New Issue
Block a user