15 KiB
15 KiB
Полный аудит проекта RF4 Spotter — 10 сентября 2026
База: 13e04e6. Проверены: архитектура, backend, frontend, security, performance, testing, deployment, documentation.
1. Архитектура и высокоуровневый обзор
Стек
| Слой | Технология | Статус |
|---|---|---|
| Proxy | Caddy 2.10 | Production-ready, TLS завершение, Basic Auth на admin |
| Frontend | Astro 7 SSR (Node) | Сборка 0 errors, адаптивный дизайн |
| Backend | FastAPI + SQLAlchemy 2 + PostgreSQL 17 | 130 тестов, 1 skipped |
| Хранилище | PostgreSQL 17 + MinIO/S3 | Volumes, backup/restore |
| CI | Gitea Actions | Python, Astro, Compose E2E, pip-audit |
| Scheduler | Official import + Community scheduler | Опциональный профиль |
Оценка архитектуры: ✅ Хорошо (8/10)
- Чёткое разделение: Caddy → Astro SSR → FastAPI → Postgres/MinIO
- Нет точки отказа в виде единого контейнера
- Profiles в docker-compose для scheduler/importer
- Production compose отдельно от dev
2. Backend (FastAPI) — детальный разбор
2.1 main.py (~1100 строк) — 🔴 КРИТИЧЕСКАЯ ПРОБЛЕМА
Проблемы:
- Все endpoints в одном файле — нарушение SRP
create_catch_report— 50+ строк бизнес-логики inline_check_rate_limitи_is_trusted_proxy— утилиты должны быть в отдельном модуле_admin,_spot_or_404,_report_source— общие функции смешаны с endpoints- Нет APIRouter — всё на
app.get/post
Рекомендация: Разделить на routes/ с APIRouter:
apps/api/app/routes/
├── activity.py # /api/v1/activity, /spots/*
├── submissions.py # catch-reports, screenshots
├── admin.py # /admin/*
├── community.py # observations, source-status
├── records.py # /records, /imports
└── catalog.py # fishes, waterbodies, baits
2.2 activity.py — ⚠️ Есть риски при scale
Плюсы:
- Детерминированная формула, покрыта тестами
- Cap confidence для 1-2 игроков (A07)
- Exponential decay freshness (18h half-life)
Риски:
- Нет индекса на
(spot_id, fish_id)—groupsагрегирует в памяти - При 10k+ approved reports за 72h — полный load + sort в памяти
total = len(rows)— нетCOUNTзапроса, полная выборка для pagination
Рекомендация:
- Добавить composite index:
ix_catch_report_spot_fish_approved - Для activity endpoint — materialized view или pre-aggregation table
- Для total — отдельный
COUNTquery вместоlen(rows)
2.3 models.py — ✅ Хорошо структурирован
Плюсы:
- Чёткие enum для SourceType, ModerationStatus, ImportStatus
- Unique constraints на slug, external_id
- JSON поля для raw_payload, changes, provenance
- Foreign keys с relationships
Проблемы:
source_external_idunique=True — конфликт при повторном импорте разных sources с одинаковым IDSubmissionAttempt— нет cleanup cron, растёт бесконечно (хотя есть cleanup в_check_rate_limit)ImportRecordEvent— нет индекса на(catch_report_id, created_at)для history query
2.4 importer.py — ✅ Хорошо, но есть edge cases
Плюсы:
- Advisory lock для cross-process mutex
- ETag/Last-Modified conditional requests
- Idempotent по external_id SHA-256
- A12: meaningful changes/provenance в ImportRecordEvent
Риски:
html: str | None = None— тестовый параметр в production signature_unique_slug— N+1 query loop при коллизиях- Нет retry policy для HTTP 5xx с exponential backoff
2.5 retention.py — ✅ Хорошо
Плюсы:
- Dry-run mode по умолчанию
- Configurable policy через RetentionPolicy
- Handles screenshots, payloads, moderation events
- Автоматический rejection pending reports по TTL
Проблемы:
delete_objectcallback — должен быть injected, не passed per-call- Нет bulk delete — loop по reports
2.6 scheduler.py vs community_scheduler.py — ⚠️ Дублирование
Оба используют pattern:
while True:
try:
run_due_import()
except Exception:
logger.exception(...)
time.sleep(interval)
Рекомендация: Общий base class или use APScheduler/celery-beat
3. Frontend (Astro) — детальный разбор
3.1 index.astro — ⚠️ Сложная логика в template
Проблемы:
- 50+ строк бизнес-логики в
.astroфайле - Нет error boundary — catch all делает 503
offset + items.length < totalItems— работает, но fragile при partial last page- Нет optimistic UI / loading state
Рекомендация:
- Вынести fetch logic в
src/lib/fetchers.ts - Добавить
isLoadingstate и skeleton UI - Error boundary per-section (filters, activity, signals)
3.2 Layout.astro — ✅ Хорошо
Плюсы:
- A08: errorPage prop для skip structuredData
- Canonical, OG, Twitter Card, JSON-LD
- robots/noindex для admin/error pages
- Skip link для accessibility
Минусы:
- 12 CSS imports — можно aggregate в
global.css replaceAll("<", "\\u003c")— hack для JSON escaping
3.3 global.css (~1200 строк) — ⚠️ Все стили вместе
Проблемы:
- Все компоненты в одном файле — сложно navigate
- Нет CSS modules или scoped styles
Рекомендация:
- Разделить на
components/,pages/,utilities/ - Astro scoped styles для компонентов
4. Security — ✅ В целом хорошо, есть улучшения
✅ Реализовано
| Мера | Статус |
|---|---|
| Rate limiting (PostgreSQL advisory lock) | ✅ |
| Idempotency-Key header | ✅ (A05) |
| HMAC client_hash вместо IP | ✅ |
| Screenshot validation (MIME, size, EXIF removal) | ✅ |
| Admin Bearer token + Caddy Basic Auth | ✅ |
| Security headers (HSTS, X-Frame-Deny, etc.) | ✅ |
| Production settings validation | ✅ |
| pip-audit в CI | ✅ |
⚠️ Улучшения
| Проблема | Приоритет |
|---|---|
CORS origins — dev defaults localhost:4321, production требует HTTPS |
Medium |
ADMIN_TOKEN — default change-me-in-production в compose.yaml |
High (docs) |
| X-Content-Type-Options — только на admin/catch-reports, не на всех | Low |
| Content-Security-Policy — отсутствует | Medium |
| Rate limit — 5 requests per 10 min, нет per-endpoint limits | Low |
| Screenshot upload — нет CSRF protection (POST без token) | Medium |
5. Performance — ⚠️ Есть узкие места
5.1 Database
Проблемы:
activityendpoint: full table scan + in-memory sortspot_detail:count_since— Python loop по reportspublic_spot_pages: 3-way JOIN + DISTINCT + offset pagination
Рекомендации — SQL индексы:
-- Добавить индексы
CREATE INDEX ix_catch_report_spot_fish_approved
ON catch_report(spot_id, fish_id, moderation_status, reported_at);
CREATE INDEX ix_catch_report_waterbody_approved_reported
ON catch_report(waterbody_id, moderation_status, reported_at);
CREATE INDEX ix_catch_report_official_records
ON catch_report(source_type, caught_at, weight_g)
WHERE source_type = 'official_record';
5.2 Caching
Текущее:
- In-memory cache: 20s TTL, 128 keys, process-local
public_cache.invalidate()на moderation/publish
Проблемы:
- Multi-process: каждый API worker имеет свой cache
- Нет cache warming на restart
- Community observations не кэшируются
Рекомендация: Redis или shared memory (SHM) для multi-process
5.3 Frontend
Плюсы:
- Astro SSR — no client JS for initial render
fetchpriority="high"для hero image- Reduced motion support
Улучшения:
- Нет lazy loading для нижеfold images
- Нет prefetch для
/spots/[id] - SignalFeed — нет intersection observer для "load more"
6. Testing — ✅ 130 passed, 1 skipped
✅ Хорошее покрытие
- API endpoints (test_api.py)
- Import idempotency (test_importer.py)
- Community CLI state/validation (test_community_cli.py)
- Readiness aggregation (test_readiness.py)
- Rate limit with Docker chain (test_rate_limit.py)
⚠️ Пропущенные сценарии
| Тест | Приоритет |
|---|---|
| Retention cleanup — нет интеграционного теста | High |
| Screenshot validation — edge cases (PNG vs JPEG magic bytes) | Medium |
| Community scheduler retry backoff — 30min → 24h | Medium |
| Activity calculation — edge cases (0 players, all same player) | Low |
| Bootstrap E2E — test-production-bootstrap.sh не в CI | Medium |
| Astro accessibility — audit:axe есть, но нет automated checks | Low |
7. Deployment & Operations
✅ Хорошее
- Compose production с healthchecks
- Backup/restore scripts
- Monitoring docs (disk, TLS, readiness)
- Data retention policy
- Preflight checks before deploy
⚠️ Улучшения
| Проблема | Решение |
|---|---|
| Нет blue-green/canary | Добавить rolling update в deploy script |
| Database migration rollback | Нет test-rollback в bootstrap |
| Log rotation — Docker json-file, 10MB × 5 | OK, но нет centralized logging |
| No distributed tracing | Добавить OpenTelemetry для request_id propagation |
| No metrics export | Нет Prometheus metrics (request count, latency, error rate) |
8. Documentation
✅ Хорошее
- README.md — comprehensive (150+ строк)
- docs/ — recovery plans, audit reports, roadmap
- deploy/README.md — production steps
- data-policy.md, retention, monitoring
⚠️ Улучшения
| Проблема | Решение |
|---|---|
| API documentation — FastAPI auto-docs, но нет OpenAPI spec file | Добавить openapi.json в repo |
| Data model ER diagram | Добавить визуальную схему |
| Architecture decision records (ADR) | Для key decisions (Astro vs Next, in-memory cache, etc.) |
| Runbook for incidents | Что делать при Postgres full, MinIO down, etc. |
9. Код-стиль и maintainability
✅ Хорошее
- Type hints everywhere
from __future__ import annotations- Pydantic v2 models
- Alembic migrations
⚠️ Улучшения
| Проблема | Пример |
|---|---|
| Long lines | create_catch_report — 70+ char lines |
| Magic numbers | 55 * min(1, weighted / 12) — вынести константы |
| Inline SQL | text("SELECT pg_try_advisory_lock(:key)") — вынести в constants |
| Duplicate URL patterns | "/api/v1/admin/" repeated in middleware and endpoints |
10. Приоритизированный список улучшений
🔴 High Priority (блокирующие/рисковые)
- Разделить main.py на routers — SRP, testability, reviewability
- Добавить database indexes —
spot_id + fish_id + moderation_status,source_type + official_records - Retention integration test — verify cleanup works end-to-end
- Screenshot CSRF protection — form submission без CSRF token
- Bootstrap E2E в CI —
test-production-bootstrap.shдолжен run на push
🟡 Medium Priority (улучшения)
- Shared cache (Redis) — вместо in-memory для multi-process
- Content-Security-Policy header — добавить base-uri, script-src
- Error boundaries в Astro — per-section error handling
- Materialized view для activity — pre-aggregation вместо in-memory sort
- OpenAPI spec export —
openapi.jsonв repo + swagger UI - Metrics export (Prometheus) — request latency, error rate, queue depth
- Screenshot validation test — magic bytes, MIME mismatch
🟢 Low Priority (косметика/технический долг)
- CSS modularization — split global.css into components
- Extract constants — 55, 25, 20, 12, 6, 3 → named constants
- Scheduler base class — DRY для official + community scheduler
- Prefetch links —
/spots/[id]prefetch на hover - ADR documentation — record architecture decisions
- Incident runbook — playbooks для common failures
Итоговая оценка
| Категория | Оценка | Комментарий |
|---|---|---|
| Архитектура | 8/10 | Чистое разделение, но main.py — god object |
| Безопасность | 7.5/10 | Хорошие основы, нужно CSP + CSRF |
| Производительность | 6.5/10 | Indexes + caching critical для scale |
| Тестирование | 8/10 | 130 tests, но пропущены retention + bootstrap |
| Deploy/Ops | 7.5/10 | Good compose, no metrics/tracing |
| Документация | 8.5/10 | Comprehensive README, missing runbook |
| Code Quality | 7.5/10 | Type hints, но long files + magic numbers |
Общий балл: 7.6/10 — крепкий проект с хорошими основами, требует refactor main.py и database indexes для production scale.
История ревизий
| Дата | Автор | Изменения |
|---|---|---|
| 2026-09-10 | AI | Полный аудит: архитектура, backend, frontend, security, performance, testing, deploy, docs |
| 2026-09-08 | (предыдущий) | Базовый аудит UI/UX, T01/T02 fixes |
Что делать дальше (рекомендации)
Немедленно (до продакшена)
- Разделить main.py на routers (минимум 3 файла)
- Создать и применить SQL индексы
- Добавить retention integration test
- Добавить CSRF token для screenshot upload
- Добавить
test-production-bootstrap.shв CI workflow
В течение спринта
- Внедрить Redis для shared caching
- Добавить CSP headers
- Вынести fetch logic из index.astro в fetchers.ts
- Добавить OpenAPI spec export
- Написать incident runbook
По возможности
- Materialized view для activity
- Prometheus metrics
- CSS modularization
- ADR documentation
- Scheduler base class