# zemlya-web — обзор проекта

**Назначение:** веб-кабинет к боту `@ZemlyaBezRiskaCheck_bot` (анализ земельных участков по КН). FastAPI backend + Next.js frontend, общая SQLite-БД с ботом.

## Где живёт

- **Mac mini** (только prod, не под git): `/Users/cebers_mini/Services/zemlya-web/`
  - `backend/` — FastAPI (Python 3.12 venv) → `127.0.0.1:20140`
  - `frontend/` — Next.js production build → `127.0.0.1:20141`
  - `tests/` — e2e: nonce-login, dashboard, robokassa, admin_users, admin_payments_export, referrals_payout, nspd_smoke, **mobile_viewport** (Playwright), **full_audit** (Playwright, 18×2 страниц)
- **Бэкапы** перед каждой правкой: `~/.deploy-backups/<feature>-YYYYMMDD_HHMMSS/`
- **БД** (общая с ботом): `~/Services/zemlya-bot/data/db/zemlya_bot.db` (SQLite)

## Доступ

LAN: `ssh -i ~/.ssh/id_ed25519 cebers_mini@192.168.0.107`. SCP-туда-обратно для Read/Edit-инструментов.

## launchd-сервисы

- `local.zemlya-web-backend` — uvicorn :20140
- `local.zemlya-web-frontend` — npm start :20141 (требует `PATH=/opt/homebrew/bin:/opt/homebrew/opt/node@22/bin`)
- `local.zemlya-web-tunnel` — cloudflared в `zemlyaweb.duckdns.org`
- `local.zemlya-bot` — сам бот (Python 3.9 venv)

Перезапуск: `launchctl kickstart -k gui/$(id -u)/<label>`.

## Деплой-флоу

1. SCP файла на mac mini
2. `python -c "import ast; ast.parse(...)"` syntax check (для backend Python)
3. Импорт-чек (`backend/venv/bin/python -c "from app.main import app"` или модульный)
4. Для frontend: `cd frontend && npm run build` (нужен PATH с node@22)
5. `launchctl kickstart -k` соответствующего сервиса
6. Smoke: `curl 127.0.0.1:20140/api/...` с правильными headers (Mozilla UA + Sec-Fetch-Site + Referer, иначе antibot 429)

## JWT для тестов

```python
import sys; sys.path.insert(0, "/Users/cebers_mini/Services/zemlya-web/backend")
import dotenv; dotenv.load_dotenv(".env")
from app.auth.jwt import issue
token = issue(1005954706, 3)  # admin telegram_id, bot_user_id
# Использовать в Cookie: zw_session={token}
```

## Главные модули backend

- `app/main.py` — FastAPI factory + middleware (CORS, antibot, security headers)
- `app/auth/` — JWT (`jwt.py`), deps (`deps.py: current_session`), nonce-flow с ботом
- `app/db.py` — `engine()` SQLAlchemy connection к общей БД бота
- `app/security.py` — slowapi limiter, antibot scoring middleware
- `app/routes/*.py` — auth, reports, payments, **admin** (~1240 lines, 7 разделов), referrals, leads, promo, feedback, pricing, health
- `app/services/` — `nspd.py` (валидация КН), `referral_crypto.py` (Fernet PD), `ref_breakdown.py` (детализация рефералов)

## Главные модули frontend

- `app/` — Next 14 App Router, server components где можно
- `app/layout.tsx` — **обязательно `viewport: { width: "device-width", initialScale: 1, maximumScale: 5 }`** (иначе мобила рендерится как desktop)
- `app/globals.css` — `html,body { overflow-x: hidden; max-width: 100vw }` + `word-break: break-word` на `<sm:` (safety-net против горизонтального скролла)
- `lib/api.ts` — единый API-клиент с типами + `_fetch` с credentials
- `components/MobileMenu.tsx` — бургер на `<md:`, во всех 18 страницах
- `components/{Logo,ReportDataView,ReportCard,BalanceCard,HubButton,OnboardingModal,UnderConstruction}.tsx`
- Стили — Tailwind (`primary-*`, `accent-*`, `ink-*`, `line`, `line-soft`, `danger`)

## Sprint-история (что готово на 2026-05-18)

- Sprint 1-6: основной функционал (login TG, reports, payments Robokassa, leads/scenarios, promo)
- **NSPD-снимок**: ESRI спутник + контур из NSPD API → `data/nspd_snapshots/{id}.png`. Endpoint `GET /api/reports/{id}/nspd-image`. В UI на `/reports/[id]` сверху. В TG-боте бот шлёт фото + анализ при free/pro проверке.
- **Sprint 7 — Рефералы**: `/referrals` (PII Fernet-шифрование, payout request ≥5000₽ ≤1/мес, история). Совместимо с ботом через `REFERRAL_PD_KEY`.
- **Полная админка** (фаза А): 7 разделов + хаб с метриками
- **/help контент**: 5 секций включая сравнительную таблицу Free/Pro/AI из `bot/assets/пример.pdf`
- **/dashboard виджеты**: quick КН, последние 3 отчёта, мини-реф-виджет
- **Mobile responsive**: бургер на всех страницах, viewport meta фикс, лого не overflow, контент стэкается вертикально, 0 горизонтальных скроллов
- **Контент главной**: сравнительная таблица Free/Pro/AI из `bot/assets/пример.pdf`, пакеты с правильными ценами, 10 разделов AI-отчёта

## Env-переменные (web/.env)

```
REFERRAL_PD_KEY=<тот же что у бота, для Fernet PII>
BOT_TOKEN=<TG bot token, для уведомлений о выплатах>
BOT_USERNAME=ZBRCheck
REFERRAL_PAYOUT_ADMIN_CHAT_ID=<chat для алертов о выплатах>
NSPD_SNAPSHOT_DIR=~/Services/zemlya-bot/data/nspd_snapshots  (default)
AUTH_BOT_HMAC=<HMAC-секрет для bot-nonce auth>
```

## Существующие роуты (важно для не-404)

```
/  /help  /login  /dashboard  /dashboard/new  /reports  /reports/[id]
/buy  /referrals  /promo  /feedback
/admin  /admin/users  /admin/users/[id]  /admin/payments  /admin/reports
/admin/leads  /admin/feedback  /admin/promo  /admin/referrals
/payment/success  /payment/fail
```

**Не существуют (не плодить ссылки):** `/pricing`, `/about`, `/privacy`, `/oferta`, `/contacts`.

## Контрольная метрика здоровья сайта

`/usr/bin/python3 /Users/cebers_mini/Services/zemlya-web/tests/full_audit.py` обходит 18 страниц × 2 viewport (mobile 390×844 + desktop 1440×900), проверяет:
- `scrollWidth ≤ clientWidth` (нет горизонтального скролла)
- Все `<a href="/...">` ссылки → не 404
- На мобиле — есть бургер на каждой странице

Текущее состояние: **0 failures, 0 warnings**.
