doc: detailed telemetry pipeline plan for Sonnet (Postgres + REST API + UI)
This commit is contained in:
@@ -737,3 +737,432 @@ IoT-хендлеры добавлять как методы того же Handle
|
|||||||
- Rules: REST API `POST /api/v5/rules`
|
- Rules: REST API `POST /api/v5/rules`
|
||||||
- Dashboard: порт 18083, default login admin/public
|
- Dashboard: порт 18083, default login admin/public
|
||||||
- **Sonnet должен зайти на https://www.emqx.io/docs/en/v5.5/ и проверить формат конфигурации**
|
- **Sonnet должен зайти на https://www.emqx.io/docs/en/v5.5/ и проверить формат конфигурации**
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# ПЛАН: Telemetry Pipeline — Postgres → REST API → UI
|
||||||
|
|
||||||
|
> **Автор плана**: GitHub Copilot (Claude Opus 4.6)
|
||||||
|
> **Дата**: 2026-04-05
|
||||||
|
> **Исполнитель**: Claude Sonnet
|
||||||
|
> **Ветка**: `iot-pg-telemetry`
|
||||||
|
> **Предусловия**: все компоненты до этого этапа РЕАЛИЗОВАНЫ и задеплоены (CRD, controller, EMQX, mqtt-bridge, IoT Console UI)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Полная цепочка: IoT устройство (или эмулятор в UI) → MQTT → INSERT в Postgres → REST API → отображение в таблице на вкладке «Телеметрия» в IoT Console.
|
||||||
|
|
||||||
|
**User story**: юзер входит токеном, регистрирует устройство, запускает эмулятор (рандомные temp/humidity), переходит на вкладку Телеметрия и видит таблицу с данными: время | устройство | payload.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что уже готово (НЕ ТРОГАТЬ без крайней необходимости)
|
||||||
|
|
||||||
|
| Компонент | Файл(ы) | Статус |
|
||||||
|
|-----------|---------|--------|
|
||||||
|
| IoTDevice CRD + types | `iot/api/v1alpha1/device_types.go` | ✅ |
|
||||||
|
| IoTDevice controller | `iot/controllers/iotdevice_controller.go` | ✅ |
|
||||||
|
| MQTT Auth + ACL | `internal/api/handler/iot_device_handler.go` | ✅ |
|
||||||
|
| IoT API CRUD | `internal/api/router.go` + handler | ✅ |
|
||||||
|
| EMQX deploy | `deployments/k8s/emqx.yaml` | ✅ |
|
||||||
|
| mqtt-bridge MQTT→RabbitMQ | `iot/cmd/mqtt-bridge/main.go` | ✅ |
|
||||||
|
| IoT Console UI | `internal/api/ui/iot-console.html` | ✅ |
|
||||||
|
| TLS (HTTPS + WSS) | `deployments/k8s/emqx-ws-ingress.yaml` | ✅ |
|
||||||
|
| Nubes branding | UI CSS | ✅ |
|
||||||
|
| Existing Postgres (invocations) | `deployments/k8s/postgres.yaml` | ✅ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Архитектурное решение (принято 2026-04-04)
|
||||||
|
|
||||||
|
Подробности: `doc/decisions/iot-telemetry-storage-2026-04-04.md`
|
||||||
|
|
||||||
|
- **Один Postgres инстанс** для IoT (отдельный от sless Postgres для invocations)
|
||||||
|
- **Отдельная DATABASE per tenant** (не одна таблица с tenant_id!)
|
||||||
|
- Tenant DB: `tenant_{namespace_hash}`, User: `tenant_{namespace_hash}`, Password: UUID в k8s Secret
|
||||||
|
- Таблица: `iot_telemetry(id BIGSERIAL, device_id TEXT, ts TIMESTAMPTZ, payload JSONB)`
|
||||||
|
- Клиент читает ТОЛЬКО через REST API, не через прямой доступ к Postgres
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаги реализации (порядок критичен!)
|
||||||
|
|
||||||
|
### ШАГ 1: Postgres Deployment для IoT (namespace: sless)
|
||||||
|
|
||||||
|
**Файл**: `deployments/k8s/iot-postgres.yaml`
|
||||||
|
|
||||||
|
**Почему отдельный от sless postgres**: разные данные, разная нагрузка. Sless postgres хранит invocations логи. IoT postgres хранит телеметрию — может быть значительно больше по объёму.
|
||||||
|
|
||||||
|
**Почему в namespace `sless`, а НЕ `iot`**: пока всё живёт в одном namespace `sless`. Отдельный namespace `iot` только усложнит без выигрыша. Отдельный Deployment с другим именем (`iot-postgres`) достаточно для изоляции.
|
||||||
|
|
||||||
|
**YAML манифест**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: iot-postgres-secret
|
||||||
|
namespace: sless
|
||||||
|
stringData:
|
||||||
|
POSTGRES_PASSWORD: "iot-pg-super-2026"
|
||||||
|
POSTGRES_USER: "iot_admin"
|
||||||
|
POSTGRES_DB: "iot_platform"
|
||||||
|
IOT_PG_DSN: "postgresql://iot_admin:iot-pg-super-2026@iot-postgres.sless.svc:5432/iot_platform?sslmode=disable"
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: iot-postgres
|
||||||
|
namespace: sless
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: iot-postgres
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: iot-postgres
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: postgres
|
||||||
|
image: postgres:16-alpine
|
||||||
|
ports:
|
||||||
|
- containerPort: 5432
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: iot-postgres-secret
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "128Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
limits:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "500m"
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: iot-postgres
|
||||||
|
namespace: sless
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: iot-postgres
|
||||||
|
ports:
|
||||||
|
- port: 5432
|
||||||
|
targetPort: 5432
|
||||||
|
```
|
||||||
|
|
||||||
|
**Действие**: `kubectl apply -f deployments/k8s/iot-postgres.yaml`
|
||||||
|
|
||||||
|
**Проверка**: `kubectl exec -n sless deploy/iot-postgres -- psql -U iot_admin -d iot_platform -c "SELECT 1"`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ШАГ 2: Go-пакет IoT Postgres storage
|
||||||
|
|
||||||
|
**Файл**: `internal/storage/iotpg/iot_telemetry_store.go`
|
||||||
|
|
||||||
|
**Назначение**: управление tenant databases + CRUD телеметрии. Один пакет, одна структура.
|
||||||
|
|
||||||
|
**Структура**:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package iotpg
|
||||||
|
|
||||||
|
type IoTPostgresStore struct {
|
||||||
|
adminDB *sql.DB // подключение к iot_platform (суперюзер)
|
||||||
|
tenants sync.Map // кэш *sql.DB per tenant namespace
|
||||||
|
log *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
type TelemetryRow struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
DeviceID string `json:"device_id"`
|
||||||
|
Ts time.Time `json:"ts"`
|
||||||
|
Payload json.RawMessage `json:"payload"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Методы (все обязательные)**:
|
||||||
|
|
||||||
|
1. `New(adminDSN string, log *slog.Logger) (*IoTPostgresStore, error)` — подключение к iot_platform DB. При старте создать таблицу tenant_credentials если не существует.
|
||||||
|
2. `EnsureTenantDB(ctx, namespace string) error` — создать DATABASE + USER + таблицу если не существуют:
|
||||||
|
- `SELECT 1 FROM pg_database WHERE datname = 'tenant_{ns}'`
|
||||||
|
- Если нет: `CREATE USER tenant_{ns} WITH PASSWORD '{uuid}'`
|
||||||
|
- `CREATE DATABASE tenant_{ns} OWNER tenant_{ns}`
|
||||||
|
- Подключиться к tenant_{ns} и: `CREATE TABLE IF NOT EXISTS iot_telemetry (...)`
|
||||||
|
- Индекс: `CREATE INDEX IF NOT EXISTS idx_iot_telemetry_device_ts ON iot_telemetry(device_id, ts DESC)`
|
||||||
|
- Записать пароль в tenant_credentials
|
||||||
|
3. `InsertTelemetry(ctx, namespace, deviceID string, payload json.RawMessage) error` — INSERT одной записи в tenant DB
|
||||||
|
4. `QueryTelemetry(ctx, namespace string, deviceID string, limit int) ([]TelemetryRow, error)` — SELECT из tenant DB
|
||||||
|
5. `Close() error`
|
||||||
|
|
||||||
|
**Таблица tenant_credentials** (в iot_platform DB):
|
||||||
|
```sql
|
||||||
|
CREATE TABLE IF NOT EXISTS tenant_credentials (
|
||||||
|
namespace TEXT PRIMARY KEY,
|
||||||
|
pg_password TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT now()
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Кэширование**: `sync.Map` для `*sql.DB` per tenant. Lazy init при первом обращении.
|
||||||
|
DSN для tenant: `postgresql://tenant_{ns}:{password}@iot-postgres.sless.svc:5432/tenant_{ns}?sslmode=disable`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ШАГ 3: Модифицировать mqtt-bridge — добавить INSERT в Postgres
|
||||||
|
|
||||||
|
**Файл**: `iot/cmd/mqtt-bridge/main.go`
|
||||||
|
|
||||||
|
**Текущее поведение**: MQTT message → envelope → RabbitMQ.
|
||||||
|
**Новое поведение**: MQTT message → INSERT в Postgres (tenant DB) + publish в RabbitMQ (как было).
|
||||||
|
|
||||||
|
**Изменения**:
|
||||||
|
|
||||||
|
1. Добавить env var: `IOT_PG_DSN` (admin DSN для iot-postgres)
|
||||||
|
2. При старте: если IOT_PG_DSN задан → подключиться к IoTPostgresStore
|
||||||
|
3. В `buildMQTTMessageHandler`:
|
||||||
|
- После парсинга namespace и deviceID из topic
|
||||||
|
- Если store != nil:
|
||||||
|
- `store.EnsureTenantDB(ctx, namespace)` — идемпотентно, кэшируется
|
||||||
|
- `store.InsertTelemetry(ctx, namespace, deviceID, payload)`
|
||||||
|
- При ошибке INSERT → логировать, НЕ останавливать publish в RabbitMQ
|
||||||
|
- RabbitMQ publish остаётся как было
|
||||||
|
|
||||||
|
**YAML обновление**: `deployments/k8s/iot-mqtt-bridge.yaml` — добавить env:
|
||||||
|
```yaml
|
||||||
|
- name: IOT_PG_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: iot-postgres-secret
|
||||||
|
key: IOT_PG_DSN
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ШАГ 4: REST API endpoint для чтения телеметрии
|
||||||
|
|
||||||
|
**Файл**: `internal/api/handler/iot_telemetry_handler.go` (НОВЫЙ)
|
||||||
|
|
||||||
|
**Endpoint**:
|
||||||
|
```
|
||||||
|
GET /v1/namespaces/{namespace}/iot/telemetry?device={deviceId}&limit={N}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Параметры**:
|
||||||
|
- `device` — фильтр по device_id (опционален: если нет — все устройства namespace)
|
||||||
|
- `limit` — максимум записей (default: 50, max: 1000)
|
||||||
|
- Авторизация: Bearer JWT → namespace validation (как все /v1/ маршруты)
|
||||||
|
|
||||||
|
**Response**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"device_id": "sensor-01",
|
||||||
|
"ts": "2026-04-05T08:15:30Z",
|
||||||
|
"payload": {"temperature": 22.5, "humidity": 65}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Порядок сортировки**: `ts DESC` (новые сверху).
|
||||||
|
|
||||||
|
**Реализация в handler**:
|
||||||
|
```go
|
||||||
|
func (h *Handler) ListIoTTelemetry(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ns := mux.Vars(r)["namespace"]
|
||||||
|
deviceID := r.URL.Query().Get("device")
|
||||||
|
limitStr := r.URL.Query().Get("limit")
|
||||||
|
// парсинг limit, default=50, max=1000
|
||||||
|
rows, err := h.IoTPG.QueryTelemetry(r.Context(), ns, deviceID, limit)
|
||||||
|
// writeJSON
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ШАГ 5: Инициализация IoTPostgresStore в main.go и handler
|
||||||
|
|
||||||
|
**Файл handler.go** — добавить поле:
|
||||||
|
```go
|
||||||
|
type Handler struct {
|
||||||
|
K8s client.Client
|
||||||
|
Scheme *runtime.Scheme
|
||||||
|
S3 *s3.Client
|
||||||
|
PG *postgres.Store
|
||||||
|
IoTPG *iotpg.IoTPostgresStore // ← НОВОЕ
|
||||||
|
Log *slog.Logger
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Файл main.go** — после создания Handler:
|
||||||
|
```go
|
||||||
|
var iotPGStore *iotpg.IoTPostgresStore
|
||||||
|
if iotDSN := os.Getenv("IOT_PG_DSN"); iotDSN != "" {
|
||||||
|
iotPGStore, err = iotpg.New(iotDSN, log)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to connect to IoT Postgres", "err", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer iotPGStore.Close()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Файл router.go** — добавить route:
|
||||||
|
```go
|
||||||
|
v1.HandleFunc("/namespaces/{namespace}/iot/telemetry", h.ListIoTTelemetry).Methods(http.MethodGet)
|
||||||
|
```
|
||||||
|
|
||||||
|
**YAML**: `deployments/k8s/operator.yaml` — добавить env IOT_PG_DSN из iot-postgres-secret
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ШАГ 6: Обновить IoT Console UI — вкладка «Телеметрия»
|
||||||
|
|
||||||
|
**Файл**: `internal/api/ui/iot-console.html`
|
||||||
|
|
||||||
|
**Заменить** заглушку `<div class="coming-soon">` на реальную таблицу.
|
||||||
|
|
||||||
|
**HTML**:
|
||||||
|
```html
|
||||||
|
<div id="tab-telemetry">
|
||||||
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:16px;">
|
||||||
|
<h3>Телеметрия</h3>
|
||||||
|
<div>
|
||||||
|
<select id="telemetry-device-filter">
|
||||||
|
<option value="">Все устройства</option>
|
||||||
|
</select>
|
||||||
|
<button onclick="loadTelemetry()">Обновить</button>
|
||||||
|
<label><input type="checkbox" id="telemetry-auto-refresh"> Авто (5с)</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table class="telemetry-table">
|
||||||
|
<thead><tr><th>Время</th><th>Устройство</th><th>Данные</th></tr></thead>
|
||||||
|
<tbody id="telemetry-body">
|
||||||
|
<tr><td colspan="3" style="text-align:center;">Нет данных</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
**CSS**: таблица в стиле Nubes — navy фон, бордеры #0b2d50, текст #e2ecf6.
|
||||||
|
|
||||||
|
**JavaScript**:
|
||||||
|
- `loadTelemetry()` — fetch GET `/v1/namespaces/{ns}/iot/telemetry?limit=100` → заполнить tbody
|
||||||
|
- Авто-обновление каждые 5с (чекбокс)
|
||||||
|
- Фильтр по устройству (select из списка devices)
|
||||||
|
- При переключении на вкладку → автоматический loadTelemetry()
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ШАГ 7: Улучшить эмулятор — рандомные temp/humidity
|
||||||
|
|
||||||
|
**Файл**: `internal/api/ui/iot-console.html` (секция эмулятора)
|
||||||
|
|
||||||
|
**Новое поведение**:
|
||||||
|
- Чекбокс: «Генерировать случайные данные (temp/humidity)» — по умолчанию ON
|
||||||
|
- Если ON: при каждой отправке payload = `{temperature: random(18-28), humidity: random(40-80), ts: ISO}`
|
||||||
|
- Если OFF: используется текстовое поле как сейчас
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function generateSensorPayload() {
|
||||||
|
return JSON.stringify({
|
||||||
|
temperature: +(18 + Math.random() * 10).toFixed(1),
|
||||||
|
humidity: +(40 + Math.random() * 40).toFixed(1),
|
||||||
|
ts: new Date().toISOString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Деплой и тестирование
|
||||||
|
|
||||||
|
### Сборка
|
||||||
|
```bash
|
||||||
|
cd ~/terra/sless
|
||||||
|
CGO_ENABLED=0 go build -o sless ./main.go
|
||||||
|
CGO_ENABLED=0 go build -o iot-mqtt-bridge ./iot/cmd/mqtt-bridge/
|
||||||
|
# УВЕЛИЧИТЬ ВЕРСИЮ!
|
||||||
|
docker build --no-cache -t pearlharbor.registryk8s.services.ngcloud.ru/naeel/sless-operator:v0.1.59 .
|
||||||
|
docker push pearlharbor.registryk8s.services.ngcloud.ru/naeel/sless-operator:v0.1.59
|
||||||
|
```
|
||||||
|
|
||||||
|
### Обновить версии в YAML
|
||||||
|
```bash
|
||||||
|
sed -i 's/v0\.1\.58/v0.1.59/g' deployments/k8s/operator.yaml
|
||||||
|
sed -i 's/v0\.1\.53/v0.1.59/g' deployments/k8s/iot-mqtt-bridge.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Деплой
|
||||||
|
```bash
|
||||||
|
kubectl apply -f deployments/k8s/iot-postgres.yaml
|
||||||
|
kubectl wait -n sless deploy/iot-postgres --for=condition=available --timeout=60s
|
||||||
|
kubectl apply -f deployments/k8s/operator.yaml
|
||||||
|
kubectl apply -f deployments/k8s/iot-mqtt-bridge.yaml
|
||||||
|
kubectl rollout restart -n sless deploy/sless-operator deploy/iot-mqtt-bridge
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проверка (E2E)
|
||||||
|
1. `kubectl exec -n sless deploy/iot-postgres -- psql -U iot_admin -d iot_platform -c "SELECT 1"`
|
||||||
|
2. `kubectl logs -n sless deploy/sless-operator | grep "IoT Postgres"`
|
||||||
|
3. Открыть `https://iot.kube5s.ru/console`
|
||||||
|
4. Ввести токен → вкладка Credentials → должно быть устройство
|
||||||
|
5. Вкладка Emulator → подключиться, включить «случайные данные», запустить авто-отправку
|
||||||
|
6. Вкладка Telemetry → должны появляться строки в таблице (avto-refresh 5с)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Файлы СОЗДАТЬ
|
||||||
|
|
||||||
|
| # | Файл | Описание |
|
||||||
|
|---|------|----------|
|
||||||
|
| 1 | `deployments/k8s/iot-postgres.yaml` | Deployment + Secret + Service |
|
||||||
|
| 2 | `internal/storage/iotpg/iot_telemetry_store.go` | Go: tenant DB management + telemetry CRUD |
|
||||||
|
| 3 | `internal/api/handler/iot_telemetry_handler.go` | REST handler GET telemetry |
|
||||||
|
|
||||||
|
## Файлы ИЗМЕНИТЬ
|
||||||
|
|
||||||
|
| # | Файл | Что менять |
|
||||||
|
|---|------|-----------|
|
||||||
|
| 1 | `internal/api/handler/handler.go` | Добавить поле `IoTPG *iotpg.IoTPostgresStore` |
|
||||||
|
| 2 | `internal/api/router.go` | Route `/namespaces/{ns}/iot/telemetry` |
|
||||||
|
| 3 | `main.go` | Init IoTPostgresStore + передача в Handler |
|
||||||
|
| 4 | `iot/cmd/mqtt-bridge/main.go` | INSERT в Postgres при MQTT message |
|
||||||
|
| 5 | `deployments/k8s/operator.yaml` | env IOT_PG_DSN + версия |
|
||||||
|
| 6 | `deployments/k8s/iot-mqtt-bridge.yaml` | env IOT_PG_DSN + версия |
|
||||||
|
| 7 | `internal/api/ui/iot-console.html` | Telemetry tab + emulator random data |
|
||||||
|
|
||||||
|
## Чего НЕ ДЕЛАТЬ
|
||||||
|
|
||||||
|
- НЕ трогать CRD / controller / EMQX / RabbitMQ — всё работает
|
||||||
|
- НЕ создавать namespace `iot` — пока всё в `sless`
|
||||||
|
- НЕ делать processing данных — пока RAW payload
|
||||||
|
- НЕ добавлять from/to фильтры по времени — хватит limit
|
||||||
|
- НЕ трогать Terraform provider — это следующий этап
|
||||||
|
- НЕ рефакторить существующие файлы
|
||||||
|
- НЕ запускать команды локально — только SSH
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Правила для Sonnet (напоминание)
|
||||||
|
|
||||||
|
1. Читай `.github/copilot-instructions.md` — правила проекта
|
||||||
|
2. Читай `doc/decisions/iot-telemetry-storage-2026-04-04.md` — принятое решение
|
||||||
|
3. Все команды — через SSH: `ssh -i /home/naeel/remote_dev/common/id_ed25519.txt naeel@5.172.178.213`
|
||||||
|
4. Файлы редактировать можно — sshfs mount видна на VM
|
||||||
|
5. ПЕРЕД go build — проверить .gitignore
|
||||||
|
6. Комментарии: дата + назначение + «почему»
|
||||||
|
7. Thinking log: `doc/thinking/2026-04-05.md`
|
||||||
|
8. progress.md: обновлять до и после каждого шага
|
||||||
|
9. Коммит + пуш после каждого завершённого шага
|
||||||
|
10. ВЕРСИЮ ПОДНИМАТЬ перед каждой сборкой!
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Thinking Log — 2026-04-05
|
||||||
|
## Агент: GitHub Copilot (Claude Opus 4.6)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Задача: написать подробный план реализации Telemetry Pipeline для Sonnet
|
||||||
|
|
||||||
|
### Контекст
|
||||||
|
Пользователь: "я не понял как этим пользоваться" — вкладка Телеметрия заглушка.
|
||||||
|
Нужна полная цепочка: устройство -> данные в таблице.
|
||||||
|
|
||||||
|
### Анализ — что уже есть
|
||||||
|
IoT-компоненты реализованы на 85%: CRD, controller, EMQX, MQTT auth/ACL, mqtt-bridge, IoT Console.
|
||||||
|
mqtt-bridge: MQTT -> RabbitMQ (для function triggers). Данные НЕ сохраняются.
|
||||||
|
Postgres для invocations есть, для IoT телеметрии — нет.
|
||||||
|
|
||||||
|
### Ключевые решения при написании плана
|
||||||
|
1. Postgres сразу (пользователь настоял)
|
||||||
|
2. Отдельный Postgres инстанс (iot-postgres), но в namespace sless
|
||||||
|
3. DATABASE per tenant (изоляция)
|
||||||
|
4. mqtt-bridge: INSERT параллельно с RabbitMQ publish
|
||||||
|
5. REST API: GET /v1/.../iot/telemetry (только limit, без from/to)
|
||||||
|
6. Эмулятор: рандом temp 18-28, humidity 40-80
|
||||||
|
|
||||||
|
### Результат
|
||||||
|
Записан подробный план в doc/iot-mvp-plan.md — 7 шагов с файлами, кодом и YAML.
|
||||||
Reference in New Issue
Block a user