Collector daemon, FastAPI server (addresses, diff, collect, sources), SQLite storage with change journal, Docker Compose deployment, tests, documentation and project rules. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
90 lines
13 KiB
Markdown
90 lines
13 KiB
Markdown
# Анализ проекта: сделано и осталось (2026-09-21, сверка с графом)
|
||
|
||
Состояние после восьми доработок: надёжность и безопасность, тесты в контейнере, форматы вывода, разделение процессов, управление ASN/FQDN через API, контейнер приложения, SQLite, `POST /collect`, `GET /addresses/diff`. Предыдущий анализ: `analysis-2026-09-20.md`. Планы и итоги лежат рядом в `docs/`.
|
||
|
||
Проект: 7 модулей Python (API, сборщик, демон, БД, форматы, хранилище, healthcheck), 1235 строк, 18 тестов (15 функций с параметризацией) в 5 файлах, 20 документов в `docs/`.
|
||
|
||
**Граф знаний** (`graphify-out/`, построен по коду и `docs/`): 315 узлов, 643 связи, 13 сообществ. Использован для сверки: узлы-«хабы», связи между модулями и документами, изолированные узлы. Все выводы из графа ниже проверены по исходникам.
|
||
|
||
## Что изменилось с предыдущей версии этого файла
|
||
|
||
- **Выполнено:** `GET /addresses/diff` (журнал изменений на триггерах SQLite, курсор, `410` за горизонтом, заголовок `X-Changes-Cursor`). Из «Не сделано» убран diff; уведомления остались.
|
||
- **Исправлено по сверке:**
|
||
- Безымянных образов Docker не 65, а 3 (всего образов 31). Наблюдение 3 снято.
|
||
- Предположение, что в демоне не ловится `sqlite3.Error`, не подтвердилось: `run_job` перехватывает любое исключение сбора и пишет его в `last_error`. Пробела нет.
|
||
- Метрики: строк кода 1235 (было 1102), тестов 18 (было 16).
|
||
- **Новое:** `graphify-out/` (1,2 МБ) не был исключён из `.gitignore` и `.dockerignore`, то есть попадал бы в образ. Исправлено, в README добавлен пункт о графе.
|
||
|
||
## Сделано
|
||
|
||
| Направление | Результат |
|
||
|---|---|
|
||
| Токен на изменяющие запросы | `X-API-Key` на `POST`/`DELETE`, без токена запись отключена (503). Чтение открыто намеренно. |
|
||
| TTL, атомарная запись, защита от порчи | TTL (`ttl_days`) на SQLite. Битые JSON и база уходят в `*.corrupt-*`, API отвечает 503. |
|
||
| Форматы вывода, агрегация CIDR, `ip_version` | nftables, mikrotik, bird, frr. |
|
||
| Разделение сборщика и API | Отдельный демон, singleton, heartbeat, `/health` по `status.json`. |
|
||
| Управление ASN и FQDN через API | Добавление, удаление, `purge`, старение данных удалённых источников. |
|
||
| Контейнер приложения | `Dockerfile` и `docker-compose.yml`: два сервиса, том, non-root, read-only, healthcheck. |
|
||
| SQLite | Таблица `addresses`, WAL, автоматическая миграция из JSON (оригиналы сохраняются), транзакции, чтение без блокировок. |
|
||
| `POST /collect` | Токен, ответ 202, запрос демону файлом `collect_request.json` (опрос каждые 5 с), объединение запросов, пропуск наложения, `503` без живого демона. |
|
||
| `GET /addresses/diff` | Журнал `changes` (триггеры, схема версии 2), итоговый эффект вместо истории, срок хранения `changes_retention_days` (30 дней), курсор или время, `410` за горизонтом. Проверено тестами и на копии базы версии 1. |
|
||
| Граф знаний | `graphify-out/`: интерактивный граф, отчёт, JSON; исключён из git и образа. |
|
||
| Тесты | 18 проверок, запуск в контейнере. |
|
||
|
||
## Не сделано
|
||
|
||
| Направление | Статус |
|
||
|---|---|
|
||
| Автоматическая резервная копия базы | Есть только команда в README (`sqlite3 ".backup"`), заданий в демоне нет |
|
||
| Метрики `/metrics`, алерты | Не начато |
|
||
| Уведомления о изменениях (webhook, Telegram) | Не начато; основа (журнал) есть |
|
||
| Повторные попытки запросов к RIPE | Не начато |
|
||
| TLS перед API | Не начато (решение пользователя: порт наружу без TLS) |
|
||
|
||
## Остаточные риски
|
||
|
||
| # | Риск | Статус |
|
||
|---|---|---|
|
||
| 1 | Нет репозитория git, истории изменений нет. | Не закрыт |
|
||
| 2 | Версии в `requirements.txt` не закреплены, это влияет и на образ. | Не закрыт |
|
||
| 3 | После порчи базы API отдаёт пустой список, пока сборщик не наполнит новую базу; автовосстановления из копии нет. Курсоры diff после пересоздания базы недействительны (`410`), клиент делает полную выгрузку. | Не закрыт |
|
||
| 4 | MikroTik и FRR не проверены на реальном ПО. | Не закрыт |
|
||
| 5 | Токен идёт по HTTP открытым текстом. | Осознанный выбор |
|
||
| 6 | Один общий токен, без ротации и аудита. | Не закрыт |
|
||
| 7 | Тестов минимум, что соответствует правилам проекта. | Осознанно |
|
||
| 8 | `POST /collect` без ограничения частоты: повторные запросы во время идущего сбора пропускаются, но защиты от нагрузки на RIPE нет. | Не закрыт |
|
||
| 9 | Журнал diff проверен только на копиях и тестах: размер и нагрузка на реальных данных неизвестны. | Не закрыт (новое) |
|
||
|
||
## Наблюдения
|
||
|
||
| # | Наблюдение | Состояние |
|
||
|---|---|---|
|
||
| 1 | **Изменения не применены к реальным данным.** В каталоге проекта нет `ripe.db`, `data.json` и `fqdn_data.json` остались в старом формате; всё проверено только на копиях. Первый запуск на боевых данных выполнит миграцию до схемы версии 2: `last_seen` старых записей станет равным времени миграции, журнал diff начнётся пустым (импорт в него не пишется), клиентам стартовать с `X-Changes-Cursor`. | Без изменений |
|
||
| 2 | **`/health` не видит сбоев источников.** Недоступный RIPE или DNS пишется в лог, задание считается успешным (`last_error` отражает только исключение всего сбора). | Без изменений |
|
||
| 3 | ~~Мусор от сборок: 65 безымянных образов.~~ Сейчас 3 безымянных образа. | Снято |
|
||
| 4 | **Повреждённый `config.json`** переименовывается при чтении; первый `POST /asns` после этого создаст файл без остальных источников, расписания и `ttl_days`. | Без изменений |
|
||
| 5 | **Логи:** сообщение «data saved» пишется после каждой транзакции, даже без изменений (`cidr_collector.py:152,206`). | Без изменений |
|
||
| 6 | **Структура README:** разделы 8 (Docker) и 9 (SQLite) дописаны в конец, разделы 1-4 описывают ручную установку. Стоит перестроить: Docker в начало, ручная установка ниже. | Без изменений |
|
||
| 7 | **`google.com`** в данных не входит в конфигурацию, его адрес удалится через 90 дней после миграции. | Без изменений |
|
||
| 8 | **Ручной сбор стартует не мгновенно**, а в пределах 5 секунд (интервал опроса демона). | Без изменений |
|
||
| 9 | **Логи APScheduler о плановых запусках скрыты** (чтобы опрос запросов каждые 5 с не засорял лог); остаются сообщения самого приложения. | Без изменений |
|
||
| 10 | **Общая точка отказа хранилища (по графу).** Главные узлы: `load_json()` (19 связей), `session()` (17), `StorageError` (16). Это осознанная развязка: одно исключение скрывает JSON и SQLite от API, демона и CLI (проверено по исходникам: `api_server.py:94,192`, `collector_daemon.py:79,102,147`, `cidr_collector.py:265`). Но любое изменение `storage.py`/`db.py` затрагивает все три процесса, покрытие тестами здесь важнее всего. | Новое |
|
||
| 11 | **`api_server.py` растёт (320 строк, связность сообщества 0,06 по графу):** схемы, проверки, все эндпоинты в одном модуле; после diff стал больше. Разделять пока не нужно, но при следующем эндпоинте стоит вынести схемы и разбор параметров. | Новое |
|
||
| 12 | **Граф не заменяет проверку.** 19 изолированных узлов (например, описания эндпоинтов в README) не связаны с обработчиками в коде: это ограничение семантической выгрузки, а не обязательно пробел в документации. Расход токенов на построение в `cost.json` не записан (нули). Документы `analysis-*.md` сами входят в граф, поэтому он частично отражает выводы анализа, а не независимую оценку. | Новое |
|
||
|
||
## Рекомендуемый порядок
|
||
|
||
1. **Гигиена и резервные копии:** `git init` и первый коммит (после этого можно поставить хук графа), закрепление версий, ежедневное задание демона `db_backup` (`.backup` с хранением нескольких копий) и автовосстановление из последней копии при порче.
|
||
2. **Наблюдаемость:** учёт ошибок по каждому источнику (`last_success`, число ошибок) в `status.json` и `/health`, затем `/metrics` для Prometheus и повторные попытки запросов к RIPE.
|
||
3. **Развёртывание на реальных данных:** запуск через compose с миграцией текущих файлов (наблюдение 1), оценка размера журнала (риск 9).
|
||
4. **TLS-прокси** перед API, если появится внешний доступ.
|
||
5. **Уведомления** (webhook, Telegram): демон после сбора считает diff по журналу и отправляет непустой результат.
|
||
|
||
Обновлять граф после доработок: `/graphify . --update`.
|
||
|
||
## Связанные документы
|
||
|
||
- `plan-*.md` / `summary-*.md`: reliability-security, tests-in-container, output-formats, process-split, asn-fqdn-api, app-container, sqlite-storage, collect-endpoint, diff-endpoint.
|
||
- Предыдущий анализ: `analysis-2026-09-20.md`.
|
||
- Граф: `graphify-out/GRAPH_REPORT.md`, `graphify-out/graph.html`.
|