436 lines
19 KiB
Markdown
Executable File
436 lines
19 KiB
Markdown
Executable File
# Next.js Админ-Панель — Руководство
|
||
|
||
## Обзор
|
||
|
||
Новая админ-панель на **Next.js 16** (App Router) + **TypeScript** + **shadcn/ui** + **Prisma** (SQLite) + **Recharts**.
|
||
Полная замена EJS/SmartAdmin шаблонизатора.
|
||
|
||
## Стек
|
||
|
||
| Слой | Технология |
|
||
|---|---|
|
||
| Фреймворк | Next.js 16 (App Router, Turbopack) |
|
||
| Язык | TypeScript 5 |
|
||
| Стилизация | Tailwind CSS 4 + shadcn/ui (New York) |
|
||
| БД | Prisma ORM + SQLite |
|
||
| Графики | Recharts |
|
||
| Состояние | Zustand |
|
||
| ИИ/LLM | z-ai-web-dev-sdk (chat, vision) |
|
||
| Тема | next-themes (dark/light) |
|
||
| Уведомления | Sonner (toast) |
|
||
| Иконки | Lucide React |
|
||
|
||
## Авторизация
|
||
|
||
- Пароль: значение `ADMIN_SECRET` из `.env` (по умолчанию `changeme`)
|
||
- Если задан `SUPER_ADMIN_SECRET` (и ≠ `ADMIN_SECRET`) — вход с ним даёт роль `super_admin`
|
||
- Если не задан — все админы = `super_admin`
|
||
- Кука `admin_token` (httpOnly, sameSite=lax, 24ч, secure в production)
|
||
- Rate-limit: 5 попыток / 15 мин с IP
|
||
- HMAC-SHA256 подпись токена
|
||
|
||
## Экраны (15)
|
||
|
||
### Основные
|
||
1. **Dashboard** — 4 KPI-карточки, 8 графиков (recharts), лента активности, авто-обновление
|
||
2. **Catalog** — дерево локаций → категории → подкатегории → товары, CRUD, clone, экспорт
|
||
3. **Users** — таблица + детальная страница, batch ban/unban, заметки, поиск, статус-вкладки
|
||
4. **Wallets** — 4 вкладки (All/Owner/Transactions/Seeds), запись платежей, экспорт seed-фраз
|
||
5. **Purchases** — таблица с batch approve/cancel, фильтры по статусу/дате, модаль деталей
|
||
6. **Audit Log** — журнал действий, фильтры по действию/дате/поиску, копирование записей
|
||
7. **Categories** — CRUD с просмотром товаров, подсчёт подкатегорий и продуктов
|
||
8. **Locations** — CRUD (страна/город/район)
|
||
9. **Settings** — редактирование параметров, экспорт/импорт данных (super_admin)
|
||
10. **Locales** — inline-редактор EN/ES/DE переводов
|
||
11. **Danger Zone** — Seed / Clear с reauth (super_admin)
|
||
|
||
### ИИ-ассистент и Лиды
|
||
12. **AI Chatbot** (`#/chatbot`) — полная конфигурация ИИ-бота:
|
||
- Включение/выключение бота
|
||
- **Спящий режим** — при включении `/start` показывает ИИ-чат вместо каталога
|
||
- Системный промпт и приветственное сообщение
|
||
- Температура, max tokens, глубина истории
|
||
- **База знаний** — словарный лексикон (товары, цены, FAQ)
|
||
- Провайдер: OpenAI / DeepSeek / OpenRouter / Ollama / Custom
|
||
- API endpoint и ключ (маскированный при отображении)
|
||
|
||
13. **Leads** (`#/leads`) — управление лидами и переписками:
|
||
- Таблица лидов с поиском, фильтрами по статусу
|
||
- AI-профиль клиента (intent, interests, sentiment, readiness)
|
||
- Просмотр переписок в виде чат-пузырей
|
||
- **Вход оператора** — переключение бота на ручной режим
|
||
- Статусы: Новый / Контакт / Квалифицированный / Потерянный / Спам
|
||
- Заметки и quick-статус
|
||
|
||
## Спящий режим магазина
|
||
|
||
Когда спящий режим включён в настройках AI Chatbot:
|
||
- Пользователь через `/start` получает ИИ-чат вместо каталога
|
||
- Бот сообщает что магазин пополняется
|
||
- Можно собрать контакты для уведомления при открытии
|
||
- Все сессии сохраняются в таблице `chat_sessions`
|
||
- Из переписок автоматически извлекаются лиды
|
||
|
||
## API маршруты (48+)
|
||
|
||
### Основные
|
||
| Метод | Маршрут | Описание |
|
||
|---|---|---|
|
||
| POST | `/api/auth/login` | Вход, установка куки |
|
||
| GET | `/api/auth/session` | Проверка сессии |
|
||
| POST | `/api/auth/logout` | Выход |
|
||
| GET | `/api/stats/dashboard` | Статистика дашборда |
|
||
| GET/PUT | `/api/settings` | Настройки |
|
||
| GET | `/api/settings/export` | Полный экспорт БД (JSON) |
|
||
| POST | `/api/settings/import` | Импорт (в разработке) |
|
||
|
||
### Товары и каталог
|
||
| Метод | Маршрут | Описание |
|
||
|---|---|---|
|
||
| GET | `/api/products/bulk` | Список товаров |
|
||
| POST | `/api/products/add` | Создание товара |
|
||
| GET/PUT/DELETE | `/api/products/[id]` | CRUD товара |
|
||
| POST | `/api/products/[id]/clone` | Дублирование товара |
|
||
| GET | `/api/catalog/tree` | Дерево каталога |
|
||
| GET/POST | `/api/categories/bulk` | Список/создание категорий |
|
||
| GET/PUT/DELETE | `/api/categories/[id]` | CRUD категории |
|
||
| GET/POST | `/api/subcategories/bulk` | Список/создание подкатегорий |
|
||
| GET/PUT/DELETE | `/api/subcategories/[id]` | CRUD подкатегории |
|
||
| GET/POST | `/api/locations/bulk` | Список/создание локаций |
|
||
| GET/PUT/DELETE | `/api/locations/[id]` | CRUD локации |
|
||
|
||
### Пользователи и кошельки
|
||
| Метод | Маршрут | Описание |
|
||
|---|---|---|
|
||
| GET | `/api/users/bulk` | Список пользователей |
|
||
| GET/PUT | `/api/users/[id]` | Профиль пользователя |
|
||
| POST | `/api/users/[id]/adjust-balance` | Корректировка баланса |
|
||
| POST | `/api/users/batch-status` | Batch ban/unban |
|
||
| GET | `/api/wallets/bulk` | Список кошельков |
|
||
| GET | `/api/wallets/overview` | Обзор кошельков |
|
||
| GET | `/api/wallets/[userId]` | Кошельки пользователя |
|
||
| POST | `/api/wallets/record-payment` | Запись платежа |
|
||
| GET | `/api/wallets/seeds` | Seed-фразы (super_admin) |
|
||
| GET | `/api/wallets/export-seeds` | Экспорт seed-фраз CSV |
|
||
|
||
### Покупки и транзакции
|
||
| Метод | Маршрут | Описание |
|
||
|---|---|---|
|
||
| GET | `/api/purchases/bulk` | Список покупок |
|
||
| GET/PUT | `/api/purchases/[id]` | Детали/статус покупки |
|
||
| POST | `/api/purchases/batch-status` | Batch approve/cancel |
|
||
| GET | `/api/transactions/bulk` | Список транзакций |
|
||
|
||
### Аудит и локали
|
||
| Метод | Маршрут | Описание |
|
||
|---|---|---|
|
||
| GET | `/api/audit/bulk` | Журнал аудита |
|
||
| GET | `/api/locales` | Локализации (EN/ES/DE) |
|
||
| POST | `/api/seed/demo` | Генерация тестовых данных |
|
||
| POST | `/api/seed/clear` | Очистка БД |
|
||
|
||
### ИИ-чатбот и лиды
|
||
| Метод | Маршрут | Описание |
|
||
|---|---|---|
|
||
| GET/PUT | `/api/admin/chatbot` | Настройки ИИ-бота |
|
||
| POST | `/api/chat` | Точка входа чата (для TG-бота) |
|
||
| GET | `/api/leads/bulk` | Список лидов |
|
||
| GET/PUT | `/api/leads/[id]` | Профиль лида |
|
||
| GET | `/api/leads/[id]/sessions` | Чат-сессии лида |
|
||
| POST | `/api/operator` | Подключение/отключение оператора |
|
||
|
||
## Модель данных (Prisma)
|
||
|
||
Всего **15 моделей**:
|
||
|
||
| Модель | Описание |
|
||
|---|---|
|
||
| `TgUser` | Пользователи Telegram |
|
||
| `CryptoWallet` | Криптокошельки |
|
||
| `Transaction` | Транзакции |
|
||
| `Location` | Локации (страна/город/район) |
|
||
| `Category` | Категории товаров |
|
||
| `Subcategory` | Подкатегории |
|
||
| `Product` | Товары |
|
||
| `Purchase` | Покупки |
|
||
| `CommissionPayment` | Комиссионные платежи |
|
||
| `AuditLog` | Журнал аудита |
|
||
| `UserState` | Состояния пользователей ( FSM ) |
|
||
| `ChatSession` | Чат-сессии с ИИ-ботом |
|
||
| `Lead` | Лиды (авто-экстракция из чата) |
|
||
| `SiteSetting` | Ключ-значение настройки |
|
||
|
||
### ChatSession
|
||
```prisma
|
||
model ChatSession {
|
||
id Int @id @default(autoincrement())
|
||
sessionId String @unique
|
||
telegramId String?
|
||
leadId Int?
|
||
messages String // JSON: [{role, content, timestamp}]
|
||
customerProfile String? // AI-профиль клиента (JSON)
|
||
isActive Boolean @default(true)
|
||
operatorName String? // Подключённый оператор
|
||
autoReplyDisabled Boolean @default(false) // Ручной режим
|
||
operatorConnectedAt DateTime?
|
||
device / ip / country
|
||
createdAt / updatedAt
|
||
}
|
||
```
|
||
|
||
### Lead
|
||
```prisma
|
||
model Lead {
|
||
id Int @id @default(autoincrement())
|
||
telegramId String? @unique
|
||
name / phone / email / telegram
|
||
status String @default("new")
|
||
aiLeadScore Float? // 0-1 скор от ИИ
|
||
customFields String // JSON
|
||
chatSessions ChatSession[]
|
||
}
|
||
```
|
||
|
||
## Архитектура ИИ-бота
|
||
|
||
### Конфигурация (SiteSetting)
|
||
Все настройки бота хранятся в таблице `site_settings` как ключ-значение. Кэшируются 5 минут.
|
||
|
||
| Ключ | Тип | По умолчанию |
|
||
|---|---|---|
|
||
| `chatbot_enabled` | bool | true |
|
||
| `chatbot_sleep_mode` | bool | false |
|
||
| `chatbot_sleep_message` | text | "Магазин пополняется..." |
|
||
| `chatbot_system_prompt` | text | "Ты — AI-ассистент..." |
|
||
| `chatbot_welcome_message` | text | "Здравствуйте!..." |
|
||
| `chatbot_temperature` | float | 0.7 |
|
||
| `chatbot_max_tokens` | int | 500 |
|
||
| `chatbot_max_history` | int | 20 |
|
||
| `chatbot_knowledge_base` | text | "" |
|
||
| `chatbot_provider` | enum | openai |
|
||
| `chatbot_api_endpoint` | url | https://api.openai.com/v1 |
|
||
| `chatbot_api_key` | string | "" |
|
||
|
||
### Поток чата
|
||
1. TG-бот отправляет `POST /api/chat` с `{sessionId, message, telegramId}`
|
||
2. Сервер находит/создаёт ChatSession
|
||
3. Загружает конфигурацию бота (из кэша/БД)
|
||
4. Формирует системный промпт: `system_prompt + knowledge_base + customer_profile + catalog_context`
|
||
5. Если `sleep_mode` — добавляет информацию о режиме пополнения
|
||
6. Отправляет в LLM (через z-ai-web-dev-sdk)
|
||
7. Сохраняет сообщения в ChatSession
|
||
8. Извлекает данные лида (имя, телефон, email, telegram) из текста
|
||
9. Генерирует/обновляет AI-профиль клиента (intent, interests, sentiment, readiness)
|
||
10. Автоматически создаёт/обновляет запись Lead
|
||
11. Возвращает `{reply, sessionId, leadId?, profile?}`
|
||
|
||
### Профилирование клиента
|
||
Из каждой переписки ИИ автоматически генерирует профиль:
|
||
```json
|
||
{
|
||
"intent": "покупка|информация|поддержка|пробный период",
|
||
"interests": ["VPN-коды", "аналитика"],
|
||
"sentiment": "positive|neutral|negative",
|
||
"budgetHint": "бюджетный|высокий",
|
||
"readiness": "exploring|considering|ready",
|
||
"summary": "Цель: покупка. Интересуется VPN."
|
||
}
|
||
```
|
||
|
||
### Оператор
|
||
- Оператор может подключиться к любой сессии (`POST /api/operator`)
|
||
- При подключении: `autoReplyDisabled=true`, бот перестаёт отвечать
|
||
- Оператор видит всю историю переписки
|
||
- При отключении — бот снова отвечает
|
||
|
||
## Развертывание (Docker)
|
||
|
||
### Сравнение со старой версией
|
||
|
||
| | Старая (Node.js бот) | Новая (Next.js админка) |
|
||
|---|---|---|
|
||
| Dockerfile | `FROM node:22`, `npm install`, `CMD node src/index.js` | Multi-stage build, standalone output, non-root user |
|
||
| docker-compose | 1 сервис, BOT_TOKEN + ADMIN_IDS | 1 сервис, ADMIN_SECRET + CHATBOT_API_KEY |
|
||
| БД | `./db/shop.db` (sqlite3 напрямую) | `./db/custom.db` (через Prisma ORM) |
|
||
| Порт | Нет (только TG webhook) | 3000 (веб-интерфейс + API) |
|
||
| RAM | ~64 MB | ~256–512 MB |
|
||
|
||
### Быстрый старт (docker compose)
|
||
|
||
```bash
|
||
# 1. Клонировать ветку
|
||
GIT_LFS_SKIP_SMUDGE=1 git clone -b feat/nextjs-admin https://git.softuniq.eu/Telegram-Market/telegram-shop.git
|
||
CD telegram-shop
|
||
|
||
# 2. Создать .env файл
|
||
cat > .env << 'EOF'
|
||
ADMIN_SECRET=your_admin_password
|
||
SUPER_ADMIN_SECRET=your_super_admin_password
|
||
CHATBOT_API_KEY=sk-your-openai-key # опционально, для ИИ-бота
|
||
CHATBOT_API_ENDPOINT=https://api.openai.com/v1 # или другой провайдер
|
||
EOF
|
||
|
||
# 3. Создать папку для БД
|
||
mkdir -p db
|
||
|
||
# 4. Собрать и запустить
|
||
COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose up -d --build
|
||
|
||
# 5. Дождаться запуска (15-30 сек)
|
||
docker compose logs -f
|
||
```
|
||
|
||
После запуска админка доступна на `http://<IP>:3000`.
|
||
|
||
### Ручной Docker (без compose)
|
||
|
||
```bash
|
||
# Сборка
|
||
docker build -t tg-shop-admin .
|
||
|
||
# Запуск
|
||
docker run -d \
|
||
--name tg_shop_admin \
|
||
--restart always \
|
||
-p 3000:3000 \
|
||
-v $(pwd)/db:/app/db \
|
||
-e ADMIN_SECRET=your_admin_password \
|
||
-e SUPER_ADMIN_SECRET=your_super_admin_password \
|
||
-e CHATBOT_API_KEY=sk-xxx \
|
||
-m 512m \
|
||
tg-shop-admin
|
||
```
|
||
|
||
### Переменные окружения
|
||
|
||
| Переменная | Обязательна | По умолчанию | Описание |
|
||
|---|---|---|---|
|
||
| `DATABASE_URL` | Нет | `file:/app/db/custom.db` | Путь к SQLite БД внутри контейнера |
|
||
| `ADMIN_SECRET` | **Да** | `changeme` | Пароль для роли admin |
|
||
| `SUPER_ADMIN_SECRET` | Нет | — | Пароль для роли super_admin |
|
||
| `CHATBOT_API_KEY` | Нет | — | API-ключ для ИИ-бота (OpenAI/DeepSeek/etc) |
|
||
| `CHATBOT_API_ENDPOINT` | Нет | `https://api.openai.com/v1` | Endpoint LLM-провайдера |
|
||
|
||
### Запуск без Docker (разработка)
|
||
|
||
```bash
|
||
# Установить зависимости
|
||
bun install
|
||
|
||
# Инициализация БД
|
||
bun run db:push
|
||
|
||
# Создать .env
|
||
echo 'DATABASE_URL=file:./db/custom.db' > .env
|
||
echo 'ADMIN_SECRET=admin123' >> .env
|
||
echo 'SUPER_ADMIN_SECRET=superadmin456' >> .env
|
||
|
||
# Генерация тестовых данных (опционально)
|
||
curl -X POST http://localhost:3000/api/seed/demo \
|
||
-H 'Cookie: admin_token=<your_token>'
|
||
|
||
# Dev-сервер
|
||
bun run dev # http://localhost:3000
|
||
|
||
# Production
|
||
bun run build # сборка standalone
|
||
bun run start # запуск production
|
||
```
|
||
|
||
### Первоначальная настройка после деплоя
|
||
|
||
1. Открыть `http://<IP>:3000`
|
||
2. Войти с паролем из `ADMIN_SECRET`
|
||
3. Перейти в **Settings** (`#/settings`) — настроить параметры магазина
|
||
4. Перейти в **AI Chatbot** (`#/chatbot`) — настроить ИИ-бота:
|
||
- Ввести API-ключ и выбрать провайдера
|
||
- Настроить системный промпт
|
||
- Добавить базу знаний (словарный лексикон)
|
||
5. Перейти в **Danger Zone** → **Seed Demo Data** для заполнения тестовыми данными
|
||
6. Очистить тестовые данные через **Clear Database** перед продакшеном
|
||
|
||
### Обновление
|
||
|
||
```bash
|
||
# docker compose
|
||
git pull
|
||
docker compose up -d --build
|
||
|
||
# ручной
|
||
git pull
|
||
docker build -t tg-shop-admin .
|
||
docker stop tg_shop_admin && docker rm tg_shop_admin
|
||
# повторить docker run ...
|
||
```
|
||
|
||
**Важно:** БД хранится в volume `./db`, она сохраняется при пересборке.
|
||
|
||
### Требования к серверу
|
||
|
||
| Ресурс | Минимум | Рекомендуется |
|
||
|---|---|---|
|
||
| CPU | 1 ядро | 2 ядра |
|
||
| RAM | 256 MB | 512 MB |
|
||
| Диск | 500 MB (образ) + БД | 1 GB |
|
||
| Архитектура | x86_64, ARM64 | — |
|
||
|
||
### Reverse Proxy (Caddy / Nginx)
|
||
|
||
**Caddy** (авто HTTPS):
|
||
```
|
||
admin.yourdomain.com {
|
||
reverse_proxy localhost:3000
|
||
}
|
||
```
|
||
|
||
**Nginx**:
|
||
```nginx
|
||
server {
|
||
listen 443 ssl;
|
||
server_name admin.yourdomain.com;
|
||
# ... ssl certs ...
|
||
|
||
location / {
|
||
proxy_pass http://127.0.0.1:3000;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
}
|
||
```
|
||
|
||
## Seed-данные
|
||
|
||
POST `/api/seed/demo` создаёт данные:
|
||
- 3 локации, 5 категорий, 11 подкатегорий
|
||
- 10 пользователей (alice–jack)
|
||
- 10 товаров, 25–30 покупок
|
||
- 10 криптокошельков, 2 комиссионных платежа, 12 audit записей
|
||
|
||
## Общие компоненты
|
||
|
||
- `Pagination` — числовая пагинация с многоточиями
|
||
- `ErrorBoundary` — обработка ошибок рендеринга
|
||
- `ExportButton` — экспорт CSV + JSON
|
||
- `SortableHeader` — сортируемые заголовки таблиц
|
||
- `useDebounce` — хук debounce (300ms)
|
||
- `useMobile` — хук определения мобильного устройства
|
||
- `useKeyboardShortcuts` — хук горячих клавиш (1–9)
|
||
|
||
## Навигация
|
||
|
||
Хеш-роутинг (`window.location.hash`). Горячие клавиши:
|
||
`1` Dashboard, `2` Catalog, `3` Users, `4` Wallets, `5` Purchases,
|
||
`6` Audit, `7` Categories, `8` Locations, `9` Settings
|
||
`Ctrl+K` — палитра команд
|
||
|
||
## Визуальные эффекты
|
||
|
||
- **Matrix Rain** — CSS-анимация падающих символов на странице логина (низкая непрозрачность)
|
||
- **Glass-card** — стеклянный эффект карточек (backdrop-blur)
|
||
- **KPI shimmer** — мерцание при наведении на KPI-карточки
|
||
- **Page-enter** — fade-in анимация страниц
|
||
- **Gradient borders** — градиентные рамки хедера/футера
|
||
- **Alternating rows** — чередующиеся строки таблиц
|
||
- **Sticky table headers** — закреплённые заголовки таблиц |