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