Files
ripe-cidr-collector/docs/analysis-2026-09-21.md
T
ayurishchevandClaude Sonnet 5 bcf8156085 Initial commit: RIPE CIDR/FQDN collector
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>
2026-09-21 07:29:38 +03:00

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 Мусор от сборок: 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.