# Gardie / sadprosto.ru — Полный контекст проекта

## Что это
Платформа для садоводов и ландшафтных специалистов. Маркетплейс услуг, сообщество, форум, стриминг (радио + видео), академия. Бренд для пользователей — **sadprosto**.

## Домен и URL
- Прод: `https://sadprosto.ru`
- Docker registry: `protected-0d684db22a0abe690.sadprosto.ru` (порт 5000)

---

## Сервер
- **IP**: `65.87.7.162`
- **SSH**: `root@65.87.7.162` (ключ ed25519)
- **OS**: Linux, Docker-based
- **Node**: v22.22.2 (nvm)
- **Диск**: 96GB `/dev/sda1` — СЛЕДИТЬ ЗА МЕСТОМ, Docker образы жрут много. Периодически `docker image prune -f`

---

## GitHub
- **Org**: `Maxton-studio`
- **SSH account**: `heroin-abuser`
- **Repos**:
  - `gardie-dbschema` — Prisma схема (MongoDB)
  - `gardie-service-auth` — основной бэкенд (NestJS)
  - `gardie-frontend-new` — фронтенд (Next.js)
  - + другие сервисы (content, forum, telegram, task, admin, services, elastic)

### Локальные пути (dev-сервер 93.113.25.62)
- `/root/gardie/gardie-dbschema/`
- `/root/gardie/gardie-service-auth/`
- `/root/gardie/gardie-frontend-new/`

---

## Стек

### Backend
- **NestJS** + TypeScript
- **Prisma** (provider: MongoDB)
- **MongoDB 7.0** — 3-node replica set в Docker
- **Redis 7** — alpine, `127.0.0.1:6379`
- **Elasticsearch 8.15.3** — поиск
- `app.setGlobalPrefix("api")` — все HTTP эндпоинты через `/api/`
- Socket.IO namespace `/chat` — НЕ подчиняется globalPrefix, идёт через `/socket.io/`

### Frontend
- **Next.js 15.3.8** + React 19 + TypeScript
- **TailwindCSS**
- **Framer Motion** — анимации
- **hls.js** — видеостриминг
- **Socket.IO client** — чат
- **Zustand** — state management (`useUserStore`, `useCityStore`, `useServiceStore`)

### Стриминг (отдельный сервер 2.56.246.30)
- **Icecast 2.4.4** — только аудио стримы
- **nginx-rtmp** — видео, HLS
- **RTMP endpoint**: `rtmp://2.56.246.30:1935/s3cure_ingest`
- **HLS URL**: `https://radio.sadprosto.ru/hls/{mountpoint}.m3u8`
- **HLS path**: `/var/www/hls/`
- OBS → RTMP → nginx-rtmp → HLS чанки → hls.js на фронте
- Конфиг: `hls_fragment 2s`, `hls_playlist_length 10s`, `hls_cleanup on`

---

## Docker контейнеры (65.87.7.162)

| Контейнер | Порт | Образ |
|-----------|------|-------|
| gardie-frontend-new | 3000 | gardie-frontend-new:latest |
| gardie-service-auth | 4000 | gardie-service-auth:latest |
| gardie-service-content | 4002 | gardie-service-content:latest |
| gardie-service-admin | 4004 | gardie-service-admin:latest |
| gardie-service-services | 4006 | gardie-service-services:latest |
| gardie-service-telegram | 4010 | gardie-service-telegram:latest |
| gardie-service-task | 4012 | gardie-service-task:latest |
| gardie-service-elastic | 4014 | gardie-service-elastic:latest |
| gardie-service-forum | 4016 | gardie-service-forum:latest |
| gardie-bot | — | gardie-bot:latest |
| mongo-1-container | 127.0.10.1:27017 | mongo:7.0.0 |
| mongo-2-container | 127.0.10.2:27017 | mongo:7.0.0 |
| mongo-3-container | 127.0.10.3:27017 | mongo:7.0.0 |
| redis | 127.0.0.1:6379 | redis:7-alpine |
| elasticsearch | 9200 | elasticsearch:8.15.3 |
| docker-registry | 5000 | registry:2 |

Все образы тегированы `protected-0d684db22a0abe690.sadprosto.ru/<name>:latest` и пушатся в локальный Docker registry.

---

## Nginx (65.87.7.162)

```
server_name sadprosto.ru;
location /           → :3000 (frontend)
location /api        → :4000 (auth)
location /api/forum  → :4016
location /api/telegram → :4010
location /api/task   → :4012
location /api/content → :4002
location /api/admin  → :4004
location /api/services → :4006
location /socket.io/ → :4000 (websocket upgrade)
```

---

## MongoDB
- **3 реплики**: mongo-1 (127.0.10.1), mongo-2 (127.0.10.2), mongo-3 (127.0.10.3)
- **БД**: `gardiedev`
- **User**: `gardie-admin`
- **Auth DB**: `admin`
- **Индекс telegramId**: partial unique (null допускается для email-юзеров)
  ```
  { telegramId: 1 }, unique: true, partialFilterExpression: { telegramId: { $type: "string" } }
  ```

---

## Аутентификация

### Два метода входа:
1. **Telegram** — через initdata в заголовке, `POST /api/auth/login`
2. **Email** — регистрация + верификация кодом + JWT

### Email auth эндпоинты (POST /api/auth/...):
- `register-email` — регистрация (email, password, firstName, cityId?, consents)
- `verify-email` — подтверждение кода (6 цифр, TTL 15 мин)
- `login-email` — вход по email+пароль
- `forgot-password` — отправка токена сброса (TTL 1 час)
- `reset-password` — сброс пароля по токену
- `resend-verification` — повторная отправка кода
- `refresh` — обновление JWT токенов

### Rate limiting
- 5 попыток / 15 минут — атомарный `INCR` + `EXPIRE` через Redis

### Юридические согласия (обязательные при регистрации):
- `personalDataConsent` (ФЗ-152)
- `termsConsent`
- `privacyPolicyConsent`
- Валидация: `@Equals(true)` в DTO

### Пароль
- Минимум 8 символов, хотя бы 1 цифра, хотя бы 1 буква
- Хеширование: bcrypt, 12 rounds

### JWT
- Access + Refresh токены
- `GET /api/users/profile` — получение профиля по JWT (`JwtAccessGuard`)
- apiClient (фронт) автоматически прикладывает Bearer token

### TelegramProvider (фронт)
- `showWidget = !webApp && !user && !isRegister`
- В обычном браузере показывает TG виджет + кнопки "Вход по email" / "Регистрация"
- `getUserData()` — сначала пробует TG auth (`POST /auth/login`), fallback на JWT (`GET /users/profile`)

---

## SMTP
- **Host**: `mail.sadprosto.ru`
- **Port**: 465 (TLS)
- **User**: `no-reply@sadprosto.ru`
- Используется для: верификация email, сброс пароля

---

## Фронтенд — структура страниц

### Registration wizard (`/registration`)
Multi-step с framer-motion анимациями:
- **Режим TG**: Telegram Login виджет
- **Режим email регистрации**: Step 0 (данные + пароль + галочки) → Step 1 (город) → Step 2 (код верификации)
- **Режим email входа**: email + пароль
- **Восстановление пароля**: email → токен + новый пароль

### App pages (`/(apppages)/`):
- `/` — главная
- `/market`, `/market/[id]` — маркетплейс услуг
- `/profile`, `/profile/[id]` — профиль
- `/profile/edit`, `/profile/new/service`, `/profile/new/case` — редактирование
- `/forum`, `/forum/[id]`, `/forum/new` — форум
- `/radio`, `/radio/[id]` — аудио стримы (Icecast)
- `/video`, `/video/[id]` — видео стримы (HLS)
- `/community` — сообщество
- `/academy` — академия
- `/garder-center` — гардер-центр
- `/bonuses` — бонусная система

### Стиль (дизайн-система):
- Основной зелёный: `#46703E` (кнопки, акценты)
- Тёмно-зелёный: `#215217` (фоны)
- Инпуты: `bg-[#F7F7F7] rounded-2xl py-5 text-center`
- Кнопки: `py-5 bg-[#46703E] rounded-2xl text-white`
- Вторичные: `border-[2px] border-[#46703E] text-[#46703E]`
- **НЕ ДЕЛАТЬ СВОЙ СТИЛЬ** — использовать существующие классы

---

## Deploy flow (ручной)

Нет CI/CD. Деплой руками:

```bash
# На dev-сервере (93.113.25.62) или локально
cd /root/gardie/<repo>
# ... code changes ...
git add -A && git commit -m "..." && git push

# На prod-сервере (65.87.7.162)
cd /root/gardie/<repo>  # или git clone
git pull
docker build --no-cache -t protected-0d684db22a0abe690.sadprosto.ru/<name>:latest .
docker stop <container> && docker rm <container>
docker run -d --name <container> -p <port>:<port> [env flags] protected-0d684db22a0abe690.sadprosto.ru/<name>:latest
```

**ВАЖНО**: `--no-cache` при билде! Docker кеширует `npm install` и не обновляет git-зависимости (gardie-dbschema).

---

## gardie-dbschema

Shared Prisma-схема, подключается как git-зависимость:
```json
"gardie-dbschema": "git+https://github.com/Maxton-studio/gardie-dbschema.git#<commit>"
```

При изменении схемы:
1. Пушишь в gardie-dbschema
2. Обновляешь хеш коммита в package.json зависимого сервиса
3. `npm install` + `npx prisma generate`
4. Docker build **с --no-cache**

---

## Известные грабли

1. **Диск 100%** — Docker images жрут место. Регулярно `docker image prune -f`
2. **gardie-dbschema кеш** — Docker кеширует git-зависимости. Всегда `--no-cache`
3. **telegramId unique** — partial index, иначе второй email-юзер не создастся (null конфликт)
4. **showWidget логика** — в обычном браузере без TG нужны emailMode/loginMode/forgotMode стейты
5. **getUserData()** — fallback: TG auth → JWT profile. Без этого email-юзеры залипают на экране входа
6. **user.city может быть null** — email-юзеры без города. Всегда проверять `user.city &&` перед `.name`
7. **Socket.IO** — namespace `/chat` не подчиняется globalPrefix `/api`
8. **axios supply chain** — версия 1.14.1 заражена. Запинена на 1.14.0 в service-auth
9. **Dockerfile frontend** — использует `node:18-alpine`, но nvm на сервере — 22. Расхождение ок, Docker изолирован

---

## Yandex S3
- **Bucket**: `gardie`
- **Endpoint**: `https://storage.yandexcloud.net`
- Используется для: баннеры стримов, медиа

---

## Правила работы

- **НЕ ПУШИТЬ БЕЗ КОМАНДЫ СТАСА** — он повторял это трижды
- **НЕ делать свою ИИшную вёрстку** — использовать существующий дизайн
- **НЕ создавать новые страницы/роуты** без согласования — интегрировать в существующие
- `trash` > `rm`
- Коммиты на русском или английском, осмысленные
- Тестировать билд локально перед пушем (`nest build` / `npm run build`)
