docs(history): раскладка HISTORY по тематическим папкам (75 файлов)

Было: 41 файл в корне HISTORY/ + авторские папки OPUS/ и SONNET/ (34 файла).
Стало — тематическая нумерация в стиле NOTES/ (10_, 20_, …):

  10_reviews/    ревью кода и разборы от LLM (2)
  20_releases/   заливки версий в реестр, чистки реестра, нумерация версий (8)
  30_provider/   ядро провайдера: архитектура, модификаторы, UUID, nested (6)
  40_generator/  генератор YAML/спеки, формат MAN (3)
  50_docs/       пайплайн документации, навигация, публикация, хостинг S3 (9)
  60_stands/     стенды и примеры: CRUD, FullPipe, Штурвал, TEST_STAND (7)
  70_infra/      реестр, API Gateway, DDoS-Guard, VPN/213, зеркала (4)
  90_llm/        диалоги и промпты с LLM вне тематики: OPUS/, SONNET/, gemini/ (34)

OPUS/ и SONNET/ перенесены как есть в 90_llm/ — чтобы не рвать пары
«бриф → ответ» внутри диалогов. Все переносы — через git mv (история сохранена).
Перед правкой: TMP/backup_2026-10-02/HISTORY_before_restructure.tar.gz.
Перекрёстные ссылки обновляются следующим коммитом.
This commit is contained in:
Repinoid
2026-10-02 07:35:32 +03:00
parent 9cc7b3f260
commit 2c196e8cc8
75 changed files with 0 additions and 0 deletions
@@ -0,0 +1,99 @@
# 10 — Code Review (Sonnet) + Fix
**Дата:** 2026-07-22
**Источник:** Анализ Sonnet (Claude) через VS Code Copilot Chat
---
## Структура проекта
### apps — исходный код приложений
| Папка | Роль | Зависимости |
|---|---|---|
| `iot-producer` | Генерирует события → RabbitMQ | `amqplib`, `express` |
| `iot-consumer` | RabbitMQ → Redis + MongoDB | `amqplib`, `redis`, `mongodb`, `express` |
| `iot-dashboard` | Redis → UI (Chart.js) | `express`, `redis` |
| `iot-demo` | Только HISTORY — не приложение | — |
### IOT_RMQ_DEMO — Terraform-конфиг стенда
6 ресурсов в 5 файлах: infra.tf (RabbitMQ + Redis + MongoDB), producer.tf, consumer.tf, dashboard.tf, locals.tf (все параметры вынесены сюда).
---
## Что хорошо
**Архитектура:**
- Чёткое разделение ответственности: каждое приложение делает одно дело
- Dashboard читает **только Redis** — правильно, никакой нагрузки на MongoDB
- `ch.prefetch(10)` у consumer — нормальное управление backpressure
- TTL-индекс в MongoDB (`expireAfterSeconds: 604800`, 7 дней) — события автоматически удалятся
- `zRemRangeByRank("iot:recent", 0, -1001)` — лента событий ограничена 1000 записями
**Terraform:**
- `adopt_existing_on_create = true` — удобно при повторных `apply` без пересоздания
- `depends_on` расставлены корректно: consumer ждёт все три сервиса, dashboard — только Redis
- Весь хардкод вынесен в locals.tf, в ресурсах чисто
- `sensitive = true` на `api_token`
---
## Проблемы (на момент анализа)
### 🔴 Баг: `MONGO_URI` без схемы `mongodb://` — **ИСПРАВЛЕНО 2026-07-22**
В consumer.tf строка формировалась так:
```hcl
MONGO_URI = "${local.cons_mgo_user}:${local.cons_mgo_pass}@${local.cons_mgo_host}:27017/iot?authSource=admin"
```
Результат: `admin:@hostname:27017/iot?authSource=admin`
В consumer.js `MongoClient` получал этот URI и падал — схема `mongodb://` отсутствовала. Дефолтный fallback `mongodb://localhost:27017/iot` не срабатывал, потому что переменная окружения была задана (просто невалидна).
**Фикс (2026-07-22):**
```hcl
MONGO_URI = "mongodb://${local.cons_mgo_user}:${local.cons_mgo_pass}@${local.cons_mgo_host}:27017/iot?authSource=admin"
```
Результат: `mongodb://admin:@hostname:27017/iot?authSource=admin`
**Верификация:** consumer `errors=0` после фикса.
### 🟡 nack с requeue=true — потенциальный infinite loop
В consumer.js:
```js
ch.nack(msg, false, true); // requeue = true
```
При систематической ошибке (например MongoDB недоступна) сообщение будет бесконечно возвращаться в очередь и перечитываться.
**Рекомендация:** `requeue=false` + логировать потерянное сообщение.
### 🟡 `s3_name` — объявлена, но не используется
В main.tf есть переменная `s3_name`, которая нигде в TF-файлах стенда не применяется. Legacy от шаблона.
### 🟡 `.trigger` — пустой файл в `iot-producer`
Файл .trigger пустой. Если нужен для force-redeploy — добавить комментарий.
### 🟡 `requirements.txt` в Node.js-папках
Файлы `requirements.txt` остались от Flask-экспериментов в iot-producer и iot-consumer. Мусор.
### 🟡 MongoDB без пароля
`cons_mgo_pass = ""` в locals.tf. Для демо-стенда приемлемо, но зафиксировано как известное ограничение.
---
## Статус на 2026-07-22
| Проблема | Статус |
|---|---|
| MONGO_URI без mongodb:// | ✅ Исправлено |
| nack + requeue=true | 🟡 Не исправлено (низкий приоритет) |
| s3_name не используется | 🟡 Не исправлено |
| .trigger пустой | 🟡 Не исправлено |
| requirements.txt мусор | 🟡 Не исправлено |
| MongoDB без пароля | ℹ️ Приемлемо для демо |
@@ -0,0 +1,173 @@
# Code Review провайдера — Opus — 2026-08-31
**Источник:** анализ и код-ревью через VS Code Copilot Chat
**Статус:** анализ завершён; часть исправлений внесена 2026-08-31
## Область анализа
Проверены:
- рукописное ядро провайдера в `provider/internal/core` и `provider/internal/resources_core`;
- CRUD, state management и валидация;
- HTTP-слой и `client.go`;
- регистрация провайдера и TLS-настройки;
- генераторы Go-ресурсов, YAML и build-пайплайн;
- Python- и shell-скрипты;
- gateway.
## Критичные находки
### 1. Отладочный лог с данными инстансов пишется в `/tmp` безусловно
В `provider/internal/core/client.go:629-637` замыкание `debug()` в `FindInstanceByDisplayName` всегда пишет в `/tmp/nubes_find_debug.log` с правами `0644`. В лог попадают `instanceUid`, `displayName` и `serviceId`.
Файл не защищён условием `NUBES_DEBUG_HTTP`, не ротируется и не очищается. Это создаёт риск раскрытия данных и неконтролируемого роста файла.
**Рекомендация:** убрать постоянную запись либо включать её только через явный debug-флаг; использовать безопасный путь и контролируемую ротацию.
### 2. Bearer-токен попадает в stderr при HTTP-отладке
В `provider/internal/core/client.go:1100-1101` вызов `httputil.DumpRequestOut(req, ...)` выводит полный исходящий запрос вместе с заголовком `Authorization: Bearer <token>` при `NUBES_DEBUG_HTTP=1`.
Токен может попасть в логи CI/CD или окружения выполнения.
**Рекомендация:** перед дампом удалять или маскировать `Authorization`; не выводить секреты ни в одном режиме.
### 3. В Python-скрипте сетевые вызовы выполняются без таймаутов
В `scripts/check_cloud_instances.py:87-88` вызовы `self.session.get(...)` не передают `timeout=`. При зависании API процесс может ожидать ответ бесконечно.
**Рекомендация:** добавить явные таймауты ко всем HTTP-вызовам и определить единое значение или конфигурационный параметр.
## Существенные находки
### 4. Retry сетевых ошибок применяется к POST-запросам
В `provider/internal/core/client.go:1113-1120` при сетевой ошибке повторяется любой HTTP-метод, включая POST к `/instances` и `/instanceOperations`.
Если сервер принял запрос, но ответ потерян, повтор может создать дубликат инстанса или операции. Идемпотентность POST не гарантирована.
**Рекомендация:** ограничить retry идемпотентными методами либо использовать идемпотency key и явную серверную поддержку повторов.
### 5. Ответ `401 Unauthorized` включён в retryable
В `provider/internal/core/client.go:1150-1156` статус `401` считается повторяемым. Протухший или неверный токен приводит к трём попыткам с задержкой, маскируя исходную ошибку авторизации и увеличивая время отказа.
**Рекомендация:** исключить `401` из retryable; возвращать ошибку авторизации сразу.
### 6. Gateway раскрывает внутренние upstream-адреса
В `gateway/server.js:60-71` корневой endpoint `/` и обработчик 404 возвращают наружу адреса `upstream` для маршрутов.
Публичный ответ раскрывает внутреннюю топологию сервисов.
**Рекомендация:** убрать `upstream` из публичных ответов; внутренние адреса оставлять только в серверных логах с необходимой санацией.
### 7. Некорректное определение неуспешной операции в Python
В `scripts/check_cloud_instances.py:187-189` используется сравнение `last_op.get("isSuccessful") == False`. При отсутствии поля возвращается `None`, поэтому состояние `OPERATION_FAILED` не определяется.
**Рекомендация:** использовать проверку `is False` либо явно обрабатывать отсутствие ключа согласно контракту API.
## Умеренные находки
### 8. Retry-логика дублируется в трёх местах
В `provider/internal/core/client.go:777-905` похожие циклы retry присутствуют в `doRequest`, `GetInstanceState` и `GetInstanceStateRaw`.
Дублирование увеличивает риск расхождения поведения и повторного появления ошибок безопасности.
**Рекомендация:** вынести общую retry-логику в единый внутренний helper с параметрами метода, таймаутов и политики повторов.
### 9. Пагинация имеет тихий предел 10 000 инстансов
В fallback-ветке `FindInstanceByDisplayName` (`provider/internal/core/client.go:747-749`) поиск прекращается после `page > 100` при размере страницы `100`.
При большем количестве инстансов совпадение может не быть найдено без предупреждения.
**Рекомендация:** убрать произвольный предел либо возвращать диагностируемую ошибку/предупреждение при достижении лимита.
### 10. Ошибка `gofmt` не останавливает генерацию
`FormatSourceOrWarn` в `TOOLS/resource-generator/writers.go:61` при ошибке форматирования только выводит предупреждение и записывает исходник.
В результате pipeline может сохранить неформатированный или потенциально некомпилируемый Go-код.
**Рекомендация:** считать ошибку форматирования фатальной для генерации либо выполнять последующую обязательную компиляционную проверку.
### 11. Секрет передаётся в командной строке shell-скрипта
В `TOOLS/s3_notification_example.sh:74` значение `SECRET_KEY` передаётся аргументом в `mc alias set`.
Секрет может быть виден через `ps` или аналогичный список процессов.
**Рекомендация:** использовать механизм передачи секрета через stdin, переменную окружения, конфигурационный файл с безопасными правами или другой поддерживаемый секретный канал.
## Дополнительные замечания
- В `provider/internal/core/client.go` ссылка на `tools/gen_v2/generate_resources_v2.go` обновлена на актуальный путь `TOOLS/resource-generator/internal/templates/instance.go`.
- В исходниках генератора (`TOOLS/resource-generator/internal/templates/*`, `TOOLS/resource-generator/internal/writers/writers.go`) метка `Code generated by tools/gen_v2` обновлена на `Code generated by TOOLS/resource-generator`.
- Текущий `provider/internal/resources_gen/registry.go` обновлён на новую метку генератора.
- `TOOLS/resource-generator/main.go` переведён на `run()` с корректным `exit code=1` и агрегированным отчётом по ошибкам записи ресурсов (instance/subresource/action).
- Пути debug-логов в `provider/internal/core/client.go` переведены на `os.TempDir()` с override через `NUBES_DEBUG_DIR` (без хардкода `/tmp`).
## Что выглядит хорошо
- Сериализация операций на инстансе через `instanceMutexes` в `client.go` защищает от параллельных операций API.
- TLS настроен с `MinVersion: TLS 1.2`; `InsecureSkipVerify` по умолчанию равен `false`.
- `api_token` отмечен как `Sensitive: true` в схеме провайдера.
- Канонизация JSON для сравнения state устраняет ложные различия из-за порядка ключей.
## Итоговый статус
| Находка | Статус |
|---|---|
| Безусловная запись данных инстансов в `/tmp` | Исправлено: debug gated + права `0600` |
| Bearer-токен в HTTP debug dump | Исправлено: `Authorization` маскируется |
| Python HTTP-вызовы без таймаутов | Исправлено: добавлен `REQUEST_TIMEOUT` |
| Retry POST-запросов | Исправлено: retry сетевых ошибок только для GET |
| `401` в retryable | Исправлено: исключён из retryable |
| Раскрытие upstream в gateway | Исправлено: `upstream` удалён из root-ответа |
| Ошибка определения `OPERATION_FAILED` | Исправлено: сравнение через `is False` |
| Дублирование retry-логики | Исправлено: общий helper для чтения состояния |
| Тихий предел пагинации | Частично исправлено: добавлена явная ошибка при достижении лимита |
| Некритичная ошибка `gofmt` в генераторе | Исправлено: fail-fast при ошибке форматирования |
| Секрет в аргументах shell-команды | Исправлено: исключена передача в argv |
## Выполненные изменения (2026-08-31)
- `provider/internal/core/client.go`:
- debug-лог `FindInstanceByDisplayName` теперь пишется только при `NUBES_DEBUG_HTTP=1`;
- права debug-логов снижены до `0600`;
- в stderr-дампе HTTP-запроса маскируется заголовок `Authorization`;
- retry сетевых ошибок ограничен методом `GET`;
- `401 Unauthorized` удалён из `isRetryable`;
- при достижении лимита fallback-пагинации возвращается явная ошибка.
- `GetInstanceState` и `GetInstanceStateRaw` переведены на общий helper `getInstanceStateWithRetry` с единым retry/HTTP-поведением.
- `scripts/check_cloud_instances.py`:
- добавлен `REQUEST_TIMEOUT = 30` и применён ко всем `session.get(...)`;
- проверка failed-операции изменена на `is False`.
- `gateway/server.js`:
- удалено поле `upstream` из публичного ответа `GET /`.
- `TOOLS/resource-generator/internal/helpers/helpers.go`:
- `FormatSourceOrWarn` переведён на fail-fast: возвращает ошибку при сбое `gofmt`.
- `TOOLS/resource-generator/internal/writers/writers.go`:
- все вызовы форматирования обрабатывают ошибку и прерывают генерацию.
- `TOOLS/resource-generator/main.go`:
- убраны `panic` на первом сбое записи ресурса;
- добавлена агрегация ошибок генерации с отчётом по каждому ресурсу;
- завершение с `exit code=1` и человекочитаемым сообщением в stderr.
- `TOOLS/resource-generator/internal/templates/instance.go`:
- обновлён marker генерации на `Code generated by TOOLS/resource-generator`.
- `TOOLS/resource-generator/internal/templates/subresource.go`:
- обновлён marker генерации на `Code generated by TOOLS/resource-generator`.
- `TOOLS/resource-generator/internal/templates/action.go`:
- обновлён marker генерации на `Code generated by TOOLS/resource-generator`.
- `provider/internal/resources_gen/registry.go`:
- обновлён marker генерации на `Code generated by TOOLS/resource-generator`.
- `scripts/s3_notification_example.sh`:
- убрана передача секрета в аргументах процесса;
- для `mc` используется временный `--config-dir` и переменная `MC_HOST_<alias>`.
- `provider/internal/core/client.go`:
- debug log path переведён на `os.TempDir()`;
- добавлен override директории через `NUBES_DEBUG_DIR`.