From e70c1642036df609bccdd16daf49f5c336a0e059 Mon Sep 17 00:00:00 2001 From: Naeel Date: Sun, 12 Apr 2026 14:34:34 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20copilot-instructions,=20=D0=B0=D1=80?= =?UTF-8?q?=D1=85=D0=B8=D1=82=D0=B5=D0=BA=D1=82=D1=83=D1=80=D0=B0,=20API,?= =?UTF-8?q?=20progress,=20decisions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/copilot-instructions.md | 126 ++++++++++++++++++++++ doc/api/endpoints.md | 32 ++++++ doc/architecture/overview.md | 66 ++++++++++++ doc/decisions/2026-04-12-separate-repo.md | 41 +++++++ doc/progress.md | 37 +++++++ 5 files changed, 302 insertions(+) create mode 100644 .github/copilot-instructions.md create mode 100644 doc/api/endpoints.md create mode 100644 doc/architecture/overview.md create mode 100644 doc/decisions/2026-04-12-separate-repo.md create mode 100644 doc/progress.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..8d4fd97 --- /dev/null +++ b/.github/copilot-instructions.md @@ -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 + +Коммитить и пушить после каждого завершённого этапа. diff --git a/doc/api/endpoints.md b/doc/api/endpoints.md new file mode 100644 index 0000000..ca5c07a --- /dev/null +++ b/doc/api/endpoints.md @@ -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) | diff --git a/doc/architecture/overview.md b/doc/architecture/overview.md new file mode 100644 index 0000000..74bde7b --- /dev/null +++ b/doc/architecture/overview.md @@ -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"]` diff --git a/doc/decisions/2026-04-12-separate-repo.md b/doc/decisions/2026-04-12-separate-repo.md new file mode 100644 index 0000000..90cbfaa --- /dev/null +++ b/doc/decisions/2026-04-12-separate-repo.md @@ -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 могут требовать обновления (новое имя образа) diff --git a/doc/progress.md b/doc/progress.md new file mode 100644 index 0000000..cb00af0 --- /dev/null +++ b/doc/progress.md @@ -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 (опционально, после подтверждения что всё работает)