feat: enable safe community auto publishing

This commit is contained in:
ik
2026-09-07 13:00:58 +07:00
parent c4d1c8b87b
commit 7217356594
10 changed files with 130 additions and 21 deletions
+1
View File
@@ -65,6 +65,7 @@
- [x] Сделать seed устойчивым к частично заполненной БД: справочники досеиваются независимо, демо-уловы идемпотентны и принудительно отключены в production; повторный/частичный запуск покрыт конфигурационными и интеграционными проверками.
- [x] Проверить списочные API по требованию раздела 12: все выдачи имеют ограниченные `limit`/`offset`, детерминированный tie-breaker и типизированные фильтры; фильтр категории рекордов перенесён до пагинации.
- [x] Добавить составные индексы PostgreSQL для activity, модерации, официальных рекордов, staging, импорта, аудита и очистки rate limit (миграция `0011`); бюджет и процедура проверки планов зафиксированы в `docs/query-performance.md`.
- [x] Включить все пять разрешённых community-источников и автоматическую публикацию полных наблюдений по ранее подтверждённым алиасам без межисточникового склеивания (миграция `0012`, 7 сентября 2026).
- [x] Провести security-проверку admin-аутентификации, CORS, headers, загрузок, контейнерных пользователей и секретов: двойная защита admin web/API, constant-time token, no-store, non-root API/web и отдельные MinIO root/app credentials; остаточные ограничения записаны в `docs/security-review.md`.
- [x] Проверить авторизацию повторной загрузки скриншота: используется отдельный одноразовый случайный токен, в БД хранится только SHA-256, UUID заявки недостаточно.
- [x] Определить сроки хранения ников, исходных payload, staging-наблюдений, moderation events и submission attempts; добавлены настраиваемая dry-run-first очистка, тест и `docs/data-retention.md`.
+5 -5
View File
@@ -64,14 +64,14 @@ Detail-страница дополнительно содержит ветер,
- тесты всех извлекаемых полей, locked-координат и отказа на постороннем HTML;
- живой контрольный прогон без сохранения персональных данных и изображений в репозиторий.
RF4MAP и RF4 Posts остаются исследовательскими: на живых HTML-снимках получены соответственно 30 индивидуальных наблюдений и 6 видов рыб одной точки. Оба результата укладываются в nullable-контракт, имеют отдельные пространства ID и не смешиваются с RF4DB/RF4-STAT. Разрешение и минимальный интервал 30 минут подтверждены; источники разрешены для staging, но остаются выключенными. RF4 Posts трактуется как агрегированная точка, а не индивидуальный улов.
На живых HTML-снимках RF4MAP и RF4 Posts получены соответственно 30 индивидуальных наблюдений и 6 видов рыб одной точки. Оба результата укладываются в nullable-контракт, имеют отдельные пространства ID и не смешиваются с RF4DB/RF4-STAT. Разрешение и минимальный интервал 30 минут подтверждены; все источники включены миграцией `0012`. RF4 Posts трактуется как агрегированная точка, а не индивидуальный улов.
## Перед продуктивным импортом
1. Сохранить подтверждение разрешения и согласованные лимиты запросов.
2. Расширять добавленные алиасы рыб и водоёмов по мере ручной проверки новых значений.
3. Согласовать начальные уровни доверия для RF4DB и двух каналов RF4-STAT (в staging записаны консервативные значения 70/65/60, но источники выключены).
4. Проверить правила публикации на небольшой вручную подтверждённой выборке.
3. При необходимости пересмотреть консервативные начальные уровни доверия 70/65/60/55/50.
4. Расширять контрольную выборку автопубликации по мере появления полных наблюдений.
5. Не хранить и не проксировать изображения без отдельного условия разрешения; на первом этапе достаточно исходной ссылки.
## Staging
@@ -80,6 +80,6 @@ RF4MAP и RF4 Posts остаются исследовательскими: на
Загрузка принимает только известные источники, HTTPS-ссылки соответствующего домена и значения в допустимых диапазонах. Все записи получают статус `staged`; таблица не связана с расчётом активности. Тест доказывает путь `HTML → ExternalCatch → JSON → external_observation` без потери provenance.
Контрольная загрузка в PostgreSQL создала 139 staging-записей: 23 `rf4db` с координатами, 100 `rf4stat-fishing` с весом и 16 `rf4stat-post` с весом. Немедленный повтор дал `created=0, updated=139`, подтвердив идемпотентность на реальной БД. Все три источника остались `enabled=false`.
Контрольная загрузка в PostgreSQL создала 139 staging-записей: 23 `rf4db` с координатами, 100 `rf4stat-fishing` с весом и 16 `rf4stat-post` с весом. Немедленный повтор дал `created=0, updated=139`, подтвердив идемпотентность на реальной БД. Миграция `0012` включает все пять разрешённых источников.
Миграция `0009` добавляет устойчивые алиасы внешних рыб и водоёмов, канонические ссылки и состояние ручной проверки. Страница `/admin/external-sources` позволяет сопоставить, отклонить или явно опубликовать наблюдение. Публикация создаёт одобренный `catch_report` с исходной ссылкой и полным provenance, но только если одновременно известны канонические сущности, координаты и вес. Поэтому текущие 139 неполных записей после миграции остались в staging.
Миграция `0009` добавляет устойчивые алиасы внешних рыб и водоёмов, канонические ссылки и состояние ручной проверки. Страница `/admin/external-sources` позволяет сопоставить, отклонить или явно опубликовать наблюдение. После миграции `0012` повторный импорт автоматически публикует запись только при одновременном наличии подтверждённых алиасов конкретного источника, координат и веса; остальные записи остаются в staging. Поэтому текущие 139 неполных записей не опубликованы.
+3 -3
View File
@@ -10,7 +10,7 @@
- Изображения: не скачиваются, не проксируются и не архивируются без отдельного явно зафиксированного условия.
- Атрибуция: опубликованный улов обязан сохранять `source_system`, внешний ID и прямой `source_url` в provenance.
- Объединение источников: автоматическое склеивание RF4DB и RF4-STAT запрещено, пока нет надёжного общего идентификатора.
- Публикация: только вручную, после канонического сопоставления; обязательны рыба, водоём, координаты и вес.
- Публикация: автоматически по ранее вручную подтверждённым алиасам источника либо вручную; обязательны рыба, водоём, координаты и вес.
## RF4MAP и RF4 Posts
@@ -19,7 +19,7 @@
- Частота: не чаще одного получения страницы каждого источника раз в 30 минут.
- Разрешённый контур: публичные HTML-страницы, нормализация в `ExternalCatch` и закрытый staging; запрещённые в `robots.txt` API не запрашиваются.
- Изображения: сохраняются только исходные URL доказательств; файлы не скачиваются и не проксируются.
- Публикация: только вручную и только при выполнении общего требования рыба + водоём + координаты + вес. Текущие записи RF4MAP/RF4 Posts не имеют веса и остаются в staging.
- Публикация: автоматически по подтверждённым алиасам либо вручную и только при выполнении общего требования рыба + водоём + координаты + вес. Текущие записи RF4MAP/RF4 Posts не имеют веса и остаются в staging.
- Семантика RF4 Posts: несколько видов одного поста остаются связанными с одним UUID точки и не считаются отдельными подтверждёнными взвешиваниями.
## Лимиты запросов
@@ -30,7 +30,7 @@
- RF4DB: один запрос списка за ручной запуск; detail-страницы запрашиваются только адресно, без массового обхода.
- RF4MAP: не чаще одного запроса раз в 30 минут.
- RF4 Posts: не чаще одного запроса раз в 30 минут.
- регулярные scheduler-задачи для обоих источников выключены;
- все пять разрешённых community-адаптеров включены в реестре staging; регулярные scheduler-задачи остаются выключены;
- при `429`, `403`, изменении DOM или неожиданном типе ответа сбор прекращается без попытки обхода ограничения.
## Что ещё требуется зафиксировать
+1 -1
View File
@@ -14,7 +14,7 @@ RF4 Spotter обрабатывает официальные публичные
- IP не хранится: rate limit использует HMAC-отпечаток с секретом сервера;
- из изображений удаляются EXIF и прочие метаданные;
- query string, authorization headers и пользовательские payload не попадают в HTTP-лог;
- community-наблюдения сначала поступают в изолированный staging и публикуются только после ручной проверки.
- community-наблюдения сначала поступают в изолированный staging; полные записи автоматически публикуются только по ранее вручную подтверждённым алиасам источника, остальные требуют ручной проверки.
## Источники и атрибуция