# Marketplace — Админ-панель, Модерация, Монетизация

## Контекст

MVP маркетплейса полностью рабочий и задеплоен (buyer/seller flow, чат, офферы, нотификации, избранное, настройки). Но отсутствует вся бизнес-логика уровня продакшена: нет ролей, нет админки, нет модерации, нет системы жалоб, нет монетизации. Сейчас любой листинг публикуется мгновенно без проверки, нет возможности заблокировать пользователя со стороны платформы, и нет инструментов для управления маркетплейсом.

**Сервер**: 173.212.212.157 | **Домен**: marketplace.173.212.212.157.sslip.io

---

## Фаза 1: Схема БД + RBAC (Роли и права)

### Prisma Schema — `server/prisma/schema.prisma`

**Новые enum'ы:**
- `UserRole`: USER, MODERATOR, ADMIN, SUPER_ADMIN
- `ReportReason`: SPAM, INAPPROPRIATE, SCAM, COUNTERFEIT, PROHIBITED_ITEM, HARASSMENT, OTHER
- `ReportStatus`: OPEN, REVIEWING, RESOLVED, DISMISSED
- `ReportTargetType`: LISTING, USER
- `SubscriptionTier`: BASIC, PRO, BUSINESS
- `SubscriptionStatus`: ACTIVE, CANCELLED, EXPIRED, PAST_DUE
- `PaymentType`: LISTING_FEE, COMMISSION, PROMOTION, SUBSCRIPTION
- `ModerationAction`: APPROVED, REJECTED, WARNING, BAN, UNBAN, LISTING_DELETED, LISTING_FEATURED

**Изменения существующих enum'ов:**
- `ListingStatus`: добавлен `PENDING_REVIEW` между DRAFT и ACTIVE
- `NotificationType`: добавлены `LISTING_APPROVED, LISTING_REJECTED, MODERATION_WARNING, ACCOUNT_BANNED, ACCOUNT_UNBANNED, REPORT_RESOLVED`

**Изменения существующих моделей:**
- `User`: + `role UserRole @default(USER)`, `isBanned Boolean @default(false)`, `banReason String?`, `bannedAt DateTime?`, `bannedBy String?`
- `Listing`: + `isFeatured Boolean @default(false)`, `rejectionReason String?`, `reviewedBy String?`, `reviewedAt DateTime?`
- `Payment`: + `type PaymentType @default(LISTING_FEE)`, `description String?`

**Новые модели (6 штук):**

| Модель | Назначение | Ключевые поля |
|---|---|---|
| `Report` | Жалобы пользователей | reporterId, targetType, targetId, reason, status, resolvedBy, resolution |
| `PlatformConfig` | Настройки платформы (1 строка) | listingFee, commissionPercent, autoApprove, maxImagesPerListing, maxListingsFreeTier, proPrice, businessPrice, promotionDayPrice, blockedKeywords[] |
| `Subscription` | Подписки продавцов | userId (unique), tier, status, stripeSubscriptionId, currentPeriodEnd |
| `PromotedListing` | Продвинутые объявления | listingId (unique), userId, startDate, endDate, amountPaid, isActive |
| `ModerationLog` | Аудит-лог действий модераторов | moderatorId, targetUserId?, targetListingId?, action, reason, details(Json) |

### Middleware — новые файлы

| Файл | Назначение |
|---|---|
| `server/src/middleware/requireRole.ts` | Фабрика middleware: `requireRole('ADMIN', 'SUPER_ADMIN')`. Проверяет роль из БД (не из JWT — изменения роли применяются мгновенно). Экспорт: `requireModerator`, `requireAdmin`, `requireSuperAdmin` |
| `server/src/middleware/checkBanned.ts` | Проверяет `isBanned` и возвращает 403 если забанен |
| `server/src/utils/moderation.ts` | Проверка текста на запрещённые слова из PlatformConfig. Кэш конфига на 60 сек |

### Изменения существующих файлов

| Файл | Изменения |
|---|---|
| `server/src/middleware/auth.ts` | Расширен `Request` типом `userRole?: string` |
| `server/src/routes/auth.ts` | Добавлен `role` в select; проверка `isBanned` при логине (403 с причиной) |
| `client/src/types/index.ts` | Добавлен `role: UserRole` в User, новые типы Report, PlatformConfig, Subscription и т.д. |
| `client/src/context/AuthContext.tsx` | Добавлены `isAdmin`, `isModerator`, `isSuperAdmin` (computed из `user.role`) |

### Seed — `server/prisma/seed.ts`
- Создана строка PlatformConfig с дефолтами
- alice → SUPER_ADMIN, bob → ADMIN, carol → MODERATOR

---

## Фаза 2: Админ-панель — Layout + Dashboard

### Новые компоненты

| Файл | Назначение |
|---|---|
| `client/src/components/layout/RequireRole.tsx` | Route guard: проверяет `user.role`, редирект на `/` если нет доступа |
| `client/src/components/layout/AdminLayout.tsx` | Sidebar с навигацией (Dashboard, Users, Listings, Reports, Moderation, Payments, Settings) + `<Outlet />` |
| `client/src/components/ui/StatCard.tsx` | Карточка метрики: иконка, число, подпись, тренд |
| `client/src/components/ui/DataTable.tsx` | Переиспользуемая таблица: колонки, пагинация, поиск, сортировка |

### Backend — `server/src/routes/admin/stats.ts`

| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/stats` | MODERATOR+ | Общая статистика: юзеры, листинги, офферы, выручка, активные сегодня |
| `GET /api/admin/stats/revenue` | ADMIN+ | Выручка по дням/неделям/месяцам |
| `GET /api/admin/stats/users` | ADMIN+ | Рост пользователей по времени |
| `GET /api/admin/stats/listings` | MODERATOR+ | Активность листингов по времени |

### Frontend — `client/src/pages/admin/AdminDashboardPage.tsx`
- 5 StatCard'ов: юзеры, листинги, офферы, выручка, активные
- Quick Stats и Platform Health панели

### Router — `client/src/router.tsx`
```
/admin → RequireRole(['MODERATOR','ADMIN','SUPER_ADMIN']) → AdminLayout
  /admin (index)       → AdminDashboardPage
  /admin/users         → AdminUsersPage
  /admin/users/:id     → AdminUserDetailPage
  /admin/listings      → AdminListingsPage
  /admin/reports       → AdminReportsPage
  /admin/moderation    → AdminModerationPage
  /admin/payments      → AdminPaymentsPage
  /admin/settings      → AdminSettingsPage
```

### Header — `client/src/components/layout/Header.tsx`
- Добавлена ссылка "Admin" в навигацию (если `isModerator || isAdmin`)

---

## Фаза 3: Управление пользователями

### Backend — `server/src/routes/admin/users.ts`

| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/users` | MODERATOR+ | Список юзеров с поиском, фильтром по роли/статусу, пагинацией |
| `GET /api/admin/users/:id` | MODERATOR+ | Полный профиль + кол-во листингов/офферов/жалоб + лог модерации |
| `PATCH /api/admin/users/:id/role` | SUPER_ADMIN | Сменить роль (нельзя себя понизить) |
| `POST /api/admin/users/:id/ban` | ADMIN+ | Забанить (body: `{ reason }`). Создаёт ModerationLog, отправляет нотификацию ACCOUNT_BANNED |
| `POST /api/admin/users/:id/unban` | ADMIN+ | Разбанить. ModerationLog + нотификация ACCOUNT_UNBANNED |

### Frontend
- `AdminUsersPage.tsx` — DataTable: аватар+имя, email, роль (Badge), статус, дата регистрации, действия
- `AdminUserDetailPage.tsx` — профиль + статистика + история модерации + кнопки бан/разбан/роль

---

## Фаза 4: Модерация объявлений

### Backend

**`server/src/routes/admin/listings.ts`:**

| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/listings` | MODERATOR+ | Все листинги, фильтр по статусу/категории, поиск |
| `POST /api/admin/listings/:id/approve` | MODERATOR+ | Статус → ACTIVE, ModerationLog, нотификация LISTING_APPROVED |
| `POST /api/admin/listings/:id/reject` | MODERATOR+ | Статус → DELETED, сохранить rejectionReason, нотификация LISTING_REJECTED |
| `DELETE /api/admin/listings/:id` | ADMIN+ | Принудительное удаление, ModerationLog |
| `POST /api/admin/listings/:id/feature` | ADMIN+ | Переключить isFeatured |

**`server/src/routes/admin/moderation.ts`:**

| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/moderation/queue` | MODERATOR+ | Листинги со статусом PENDING_REVIEW, пагинация |
| `GET /api/admin/moderation/logs` | ADMIN+ | Полная история действий модерации |

**Изменения `server/src/routes/listing.ts`:**
- `POST /:id/activate`: если `PlatformConfig.autoApprove === false` → статус `PENDING_REVIEW` вместо `ACTIVE`
- `POST /` (создание): проверка текста на `blockedKeywords` → если найдено, автоматически `PENDING_REVIEW`
- Добавлен `isFeatured` в listingSelect

### Frontend
- `AdminListingsPage.tsx` — DataTable с табами по статусу (ALL, PENDING_REVIEW, ACTIVE, SOLD, DELETED)
- `AdminModerationPage.tsx` — Карточки листингов на ревью: фото, заголовок, описание, продавец, кнопки Approve/Reject

---

## Фаза 5: Система жалоб (Reports)

### Backend

**`server/src/routes/report.ts`** (пользовательский):

| Endpoint | Доступ | Что делает |
|---|---|---|
| `POST /api/reports` | Авторизованный | Создать жалобу: `{ targetType, targetId, reason, description }` |

**`server/src/routes/admin/reports.ts`:**

| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/reports` | MODERATOR+ | Список жалоб, фильтр по статусу/типу, пагинация |
| `GET /api/admin/reports/:id` | MODERATOR+ | Детали жалобы с информацией о цели |
| `PATCH /api/admin/reports/:id` | MODERATOR+ | Разрешить/отклонить: `{ status, resolution }` |

### Frontend
- `ReportModal.tsx` — Модалка с радиокнопками причин + описание
- `ProductDetailPage.tsx` — кнопка "Report" теперь открывает ReportModal (вместо `alert()`)
- `AdminReportsPage.tsx` — DataTable с табами по статусу

---

## Фаза 6: Настройки платформы + Админ платежей

### Backend

**`server/src/routes/admin/settings.ts`:**

| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/settings` | ADMIN+ | Текущий PlatformConfig |
| `PATCH /api/admin/settings` | SUPER_ADMIN | Обновить любые поля PlatformConfig |

**`server/src/routes/admin/payments.ts`:**

| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/admin/payments` | ADMIN+ | Все платежи, фильтр по типу/статусу, пагинация |
| `GET /api/admin/payments/revenue` | ADMIN+ | Разбивка: listing fees, commissions, promotions, subscriptions |

**Изменения `server/src/routes/payment.ts`:**
- `POST /create-intent`: читает `listingFee` из PlatformConfig вместо хардкода `500`
- Записывает `type: 'LISTING_FEE'` в Payment

### Frontend
- `AdminSettingsPage.tsx` — Форма: listing fee, commission %, auto-approve toggle, max images, free tier limit, pro/business цены, promotion day price, blocked keywords
- `AdminPaymentsPage.tsx` — 4 карточки выручки по типам + таблица транзакций

---

## Фаза 7: Монетизация — Подписки + Продвижение

### Backend

**`server/src/routes/subscription.ts`:**

| Endpoint | Доступ | Что делает |
|---|---|---|
| `GET /api/subscriptions/current` | Авторизованный | Текущая подписка (или BASIC по умолчанию) |
| `POST /api/subscriptions/create` | Авторизованный | Создать подписку PRO/BUSINESS |
| `POST /api/subscriptions/cancel` | Авторизованный | Отменить подписку |

**`server/src/routes/promotion.ts`:**

| Endpoint | Доступ | Что делает |
|---|---|---|
| `POST /api/listings/:id/promote` | Авторизованный | Оплатить продвижение (body: `{ days }`) |
| `GET /api/listings/:id/promotion` | Авторизованный | Статус продвижения |

**Изменения `server/src/routes/offer.ts`:**
- При ACCEPTED: создаётся Payment с `type: 'COMMISSION'` и суммой = `commissionPercent` от amount

**Тарифные планы:**
- **Basic** (бесплатно): до 5 листингов/мес
- **Pro** ($9.99/мес): безлимит + аналитика продавца
- **Business** ($29.99/мес): bulk-операции + приоритетная поддержка

---

## Деплой

```bash
# 1. Миграция
cd server && npx prisma migrate dev --name admin_moderation_monetization

# 2. Обновить seed
npx tsx prisma/seed.ts

# 3. Билд и деплой
npm run build --workspace=client
scp -r client/dist/* root@173.212.212.157:/var/www/marketplace/
ssh root@173.212.212.157 'cd /var/www/marketplace-app && git pull && cd server && npm install && npx prisma db push && pm2 restart marketplace-api'

# 4. Seed на проде
ssh root@173.212.212.157 'cd /var/www/marketplace-app/server && npx tsx prisma/seed.ts'
```

---

## Верификация

1. Логин как alice (SUPER_ADMIN) → в хедере видна ссылка "Admin"
2. `/admin` → дашборд с метриками
3. `/admin/users` → таблица юзеров, можно забанить david
4. Логин как david → получить 403 "Account suspended"
5. `/admin/users` → разбанить david
6. `/admin/settings` → отключить autoApprove
7. Логин как david → создать листинг → статус PENDING_REVIEW
8. `/admin/moderation` → увидеть листинг в очереди → Approve
9. На странице товара нажать Report → модалка → отправить жалобу
10. `/admin/reports` → увидеть жалобу → Resolve
11. `/admin/payments` → все транзакции с типами
12. `/admin/settings` → изменить listing fee
