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>
3.9 KiB
3.9 KiB
Итоги: GET /addresses/diff?since=
План: docs/plan-diff-endpoint.md.
Сделано
- Журнал изменений (
db.py): таблицыchangesиmeta, схема версии 2. Запись ведут SQL-триггеры наaddresses, поэтому охвачены сбор, TTL,purgeи снятие источника. Значение попадает в журнал, только если оно появилось или исчезло в итоговом наборе (адрес, который держит другой источник, не считается удалённым). Импорт из JSON в журнал не пишется. База версии 1 обновляется автоматически при первом открытии. get_changes(): в одной read-транзакции берёт первое действие по значению после точки отсчёта и сверяет с текущим состоянием, поэтому удалённое и возвращённое в тот же интервал в результат не попадает.- Срок хранения (
cidr_collector.py):changes_retention_days(по умолчанию 30,0- без очистки), очистка внутри транзакции обоих сборов; граница («горизонт») хранится вmeta. - API (
api_server.py):GET /addresses/diff?since=<курсор | время>&type=&ip_version=с ответом{since, now, cursor, added, removed};400при неверномsince,410при выходе за горизонт или чужом курсоре. В ответы/addressesдобавлен заголовокX-Changes-Cursor(курсор читается до данных) для первой синхронизации. - README: описание эндпоинта, ключа
changes_retention_days, журнала в разделе о хранении. - Тесты: 2 новых (журнал и очистка в БД; API), всего 18, в контейнере 18 passed.
Проверка
Вручную на копии базы версии 1: миграция сохранила данные, в журнале после неё пусто; при сборе истёкший по TTL префикс попал в removed, новый - в added; время с часовым поясом (+03:00) разобрано.
Отличия от плана
- Курсор рекомендован вместо времени: номер записи не зависит от часов и от длительности транзакции сборщика (иначе запись, зафиксированная позже, могла бы получить метку времени раньше выданного
nowи потеряться). - Добавлен заголовок
X-Changes-Cursor(в плане не было), чтобы клиент мог согласованно начать синхронизацию.
Замечания
- Diff считается отдельно по типам (
asn,fqdn); одинаковая строка в обоих типах приtype=allсообщалась бы дважды - на практике не встречается (CIDR и IP различаются записью). - Только JSON и без агрегации: агрегированный diff не аддитивен.
- Журнал растёт с числом изменений; при 30 днях хранения и редких изменениях RIPE объём мал. На реальных данных не проверялось (наблюдение 1 анализа остаётся в силе).
- Уведомления (webhook, Telegram) не сделаны: следующий шаг поверх журнала.