# RF4 Spotter RF4 Spotter — неофициальный сервис свежих точек и статистики клёва для Russian Fishing 4. Он объединяет публичные рекорды и подтверждённые пользовательские уловы, показывает свежесть источников и рассчитывает объяснимые индексы активности и уверенности. Проект не взаимодействует с игровым клиентом и не является официальным продуктом RF4. ## Статус разработки - этапы 0 и 1 завершены; - этап 2, официальный импорт, завершён технически; автоматический профиль остаётся выключенным до явного разрешения владельца источника; - этап 3 функционально завершён: форма, раздельные ошибки создания/скриншота с повторной загрузкой, MinIO, модерация, удаление с аудитом и постоянный rate limit готовы; - для полного пользовательского сценария добавлен E2E-тест `отправка → pending → модерация → публичная статистика`; - начат этап 4: формула индекса зафиксирована, детерминированные агрегаты и правила включения данных покрыты тестами; далее — состояния карточек и сквозная проверка фильтров. Подробный план и актуальные чекбоксы находятся в [`docs/ROADMAP.md`](docs/ROADMAP.md). Актуальная инвентаризация источников и правила подключения адаптеров находятся в [`docs/data-source-audit.md`](docs/data-source-audit.md). Разрешённый технический пилот RF4DB/RF4-STAT описан в [`docs/community-source-pilot.md`](docs/community-source-pilot.md); эти данные пока разбираются в общий промежуточный контракт, но не импортируются в рабочую БД и не влияют на индекс. Один ограниченный снимок публичных карточек можно получить исследовательским CLI: ```bash python -m rf4_research.community_cli rf4db --limit 25 python -m rf4_research.community_cli rf4stat-fishing --limit 100 python -m rf4_research.community_cli rf4stat-posts --limit 25 ``` Команды печатают нормализованный JSON в stdout и ничего не записывают в базу. Для регулярного получения необходимо соблюдать согласованные лимиты; для RF4-STAT — не менее пяти секунд между запросами разных страниц. ## Запуск через Docker Требуются Docker Engine и Docker Compose. Это основной и рекомендуемый сценарий: ```bash docker compose up --build ``` После успешного запуска: - сайт: ; - OpenAPI: ; - проверка API: ; - консоль MinIO: . Контейнер API сам выполняет `alembic upgrade head`, затем идемпотентный seed. PostgreSQL хранит данные в именованном volume `postgres_data`, а MinIO — в `minio_data`. Остановка: ```bash docker compose down ``` Удаление volume и повторное создание чистой базы — только когда данные больше не нужны: ```bash docker compose down --volumes docker compose up --build ``` Переменные и локальные значения по умолчанию перечислены в [.env.example](.env.example). Секретов в репозитории нет. ## Что реализовано - FastAPI и SQLAlchemy 2; - PostgreSQL 17 и миграции Alembic до `0007`; - идемпотентный seed с двумя точками и свежими демо-уловами; - `GET /api/v1/activity` с фильтрами периода, водоёма, рыбы, способа и сортировки; - `GET /api/v1/spots/{id}` и `/catches`; - справочники рыб, водоёмов и приманок; - Astro SSR-интерфейс с адаптивным дизайном из `design-reference` без переноса React/Vinext-стека; - объяснимые индексы активности и уверенности по формуле спецификации; - документированная формула и детерминированные тесты окон 6/12/24/72 часа; - состояния «нет данных» и «источник недоступен»; - идемпотентный импорт официальных записей с журналом запусков; - публичная страница `/records` с источником и временем последнего импорта; - форма `/report`, защищённые admin API и журнал модерации; - отдельные состояния ошибки создания заявки и загрузки скриншота; неудачный скриншот можно добавить повторно по ID уже сохранённой заявки; - honeypot и постоянный rate limit в PostgreSQL с HMAC-отпечатками вместо исходных IP; - скриншоты уловов в MinIO/S3 с проверкой MIME, расширения, размера и фактического содержимого, повторным кодированием и очисткой метаданных; - административная очередь `/admin/moderation` с одобрением, отклонением и обезличенным удалением записи с аудитом. Все пользовательские ники и уловы в seed демонстрационные. ## Проверка проекта Backend и исследовательский парсер: ```bash python3 -m venv .venv .venv/bin/pip install -r apps/api/requirements.txt .venv/bin/pip install -e . .venv/bin/pytest -q ``` Актуальное число тестов выводит команда `pytest`; набор включает backend, импорт, расчёт активности и исследовательский парсер. Frontend: ```bash cd apps/web npm install npm run build npm audit --omit=dev ``` E2E после запуска Compose: ```bash cd apps/web npx playwright install chromium npm run test:e2e ``` ## Импорт официальных рекордов Однократный контейнерный запуск после старта базы: ```bash docker compose --profile tools run --rm importer ``` Импорт делает до трёх ограниченных попыток, проверяет DOM-контракт и не удаляет ранее сохранённые данные при сбое. Повторный запуск обновляет совпавшие записи по SHA-256 ключу и не создаёт дубликаты. Расписание реализовано, но намеренно не включается обычным запуском: сначала требуется согласовать допустимость регулярного опроса официального сайта. Ручной административный запуск также доступен через `POST /api/v1/admin/imports/official-records`, журнал — через `GET /api/v1/admin/imports`. Импорт сохраняет HTTP-метаданные и использует `ETag`/`Last-Modified`, когда источник их предоставляет. Планировщик реализован отдельным opt-in профилем и по умолчанию опрашивает источник не чаще одного раза в час: ```bash docker compose --profile scheduler up -d scheduler ``` Обычный `docker compose up` его не запускает. Не включайте профиль во внешнем окружении, пока условия автоматического сбора не согласованы с владельцем источника; отсутствие `robots.txt` не является разрешением. ## Пользовательские уловы и модерация Новая запись из `/report` получает статус `pending` и не участвует в активности до одобрения. Административные методы требуют заголовок `Authorization: Bearer $ADMIN_TOKEN`: ```bash curl -H "Authorization: Bearer change-me-in-production" \ "http://localhost:8000/api/v1/admin/catch-reports?status=pending" ``` Перед внешним развёртыванием обязательно замените демонстрационные `ADMIN_TOKEN`, `RATE_LIMIT_SECRET`, `S3_ACCESS_KEY` и `S3_SECRET_KEY`. Форма принимает JPEG, PNG и WebP до 8 МБ; API сверяет MIME и расширение с фактическим форматом, повторно кодирует изображение и удаляет EXIF перед сохранением в MinIO. Модератор получает временную подписанную ссылку через admin API. Если создание записи прошло успешно, а загрузка скриншота завершилась ошибкой, форма сохраняет ID заявки и предлагает повторить только загрузку изображения. Повторно отправлять сам улов не требуется. Очередь модерации доступна по адресу . Администратор вводит `ADMIN_TOKEN`; интерфейс держит его только в памяти открытой страницы и не сохраняет в URL или браузерном хранилище. Администратор может одобрить, отклонить или удалить сообщение. Удаление очищает ник, комментарий, исходную ссылку и объект скриншота, исключает запись из статистики, но сохраняет обезличенный факт действия в журнале аудита. ## Исследовательский парсер официальных рекордов ```bash python -m rf4_research.records \ --url https://rf4game.de/records/region/RU/ \ --region RU \ --category records ``` Команда делает один HTTP-запрос и печатает типизированные записи в JSON. Это исследовательский инструмент этапа 0; продуктивный адаптер находится в `apps/api/app/importer.py` и добавляет ограниченные повторы, условные HTTP-запросы, транзакции, дедупликацию и журнал запусков. Подтверждённая структура источника и риски описаны в [docs/data-sources.md](docs/data-sources.md).