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>
13 KiB
Анализ проекта: сделано и осталось (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 | Снято | |
| 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 сами входят в граф, поэтому он частично отражает выводы анализа, а не независимую оценку. |
Новое |
Рекомендуемый порядок
- Гигиена и резервные копии:
git initи первый коммит (после этого можно поставить хук графа), закрепление версий, ежедневное задание демонаdb_backup(.backupс хранением нескольких копий) и автовосстановление из последней копии при порче. - Наблюдаемость: учёт ошибок по каждому источнику (
last_success, число ошибок) вstatus.jsonи/health, затем/metricsдля Prometheus и повторные попытки запросов к RIPE. - Развёртывание на реальных данных: запуск через compose с миграцией текущих файлов (наблюдение 1), оценка размера журнала (риск 9).
- TLS-прокси перед API, если появится внешний доступ.
- Уведомления (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.