Files
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

4.5 KiB

План: GET /addresses/diff?since= (изменения списка)

Цель

Клиент (роутер, файрвол, скрипт) запрашивает только изменения с момента прошлой синхронизации: что добавилось и что исчезло. Полный список каждый раз не скачивается.

Дизайн

  • GET /addresses/diff?since=<время | курсор>[&type=cidr|fqdn|all][&ip_version=all|4|6], чтение открыто (как у /addresses).
  • Ответ: {"since", "now", "cursor", "added": [...], "removed": [...]}. Форматы конфигураций и агрегация не поддерживаются (агрегированный diff не аддитивен).
  • Журнал изменений в SQLite: таблица changes(id, ts, kind, value, action add|del). Пишется триггерами на addresses, поэтому охватывает все пути удаления (TTL, снятый источник, purge) и добавления:
    • add: значение появилось, а у других источников его не было;
    • del: удалена последняя запись значения. Значение, которое есть у нескольких источников, в diff не попадает, пока хотя бы один источник его держит. Первичный импорт из JSON в журнал не пишется.
  • Итоговый эффект, а не история: по каждому значению берётся первое действие после точки since (add - значения не было, del - было) и сверяется с текущим состоянием. Удалено и возвращено в тот же интервал - в diff не попадает.
  • Точка отсчёта: since - время ISO 8601 (без часового пояса считается UTC) либо целый курсор из прошлого ответа. Рекомендуется курсор: порядковый номер записи журнала не зависит от часов и от длительности транзакции сборщика. Время округляется до миллисекунд, границы включительные (доставка «минимум один раз», повтор безвреден).
  • Срок хранения: changes_retention_days в config.json (по умолчанию 30, <= 0 - без очистки). Очистка выполняется в транзакции сбора. Границу («горизонт») хранит таблица meta. Если since старше горизонта или курсор не из этой базы (больше текущего) - 410 Gone: клиент забирает полный /addresses и продолжает с нового cursor.
  • Ошибки: 400 при неверном since; 503 при недоступной базе (общий обработчик).
  • Чтение diff выполняется в одной read-транзакции (согласованный снимок).

Изменения

  1. db.py: SCHEMA_VERSION = 2 (миграция 1 -> 2: changes, meta, триггеры; для v0 таблицы создаются после импорта JSON), prune_changes(), get_changes().
  2. cidr_collector.py: DEFAULT_CHANGES_RETENTION_DAYS, вызов очистки в обоих run_collection.
  3. api_server.py: эндпоинт /addresses/diff, разбор since; заголовок X-Changes-Cursor в ответах /addresses (курсор для первой синхронизации, читается до данных).
  4. README.md: описание эндпоинта, ключ конфигурации, раздел о хранении.
  5. Тесты (2): БД (триггеры: несколько источников, TTL, возврат в тот же интервал, очистка и горизонт); API (400, 410, курсор и время, фильтры).

Проверка

Тесты в контейнере; вручную на копии данных: миграция базы версии 1 -> 2 сохраняет данные, purge/TTL порождают removed, повторный запрос с cursor возвращает пустой diff.