Add balance transactions to user admin panel
Introduces BalanceTransaction model to track per-user balance history (prepayments, refunds, partner credits). Admin can add/delete transactions; current balance is computed as the running sum. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,308 @@
|
||||
<!-- BEGIN:nextjs-agent-rules -->
|
||||
# This is NOT the Next.js you know
|
||||
# AGENTS.md — LMS Second Brain
|
||||
|
||||
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
|
||||
<!-- END:nextjs-agent-rules -->
|
||||
Собственная LMS-платформа для образовательных курсов по PKM и Obsidian.
|
||||
Заменяет emdesell.ru. Масштаб: ~1000 аккаунтов, ~200 активных, до 10 курсов.
|
||||
Production: **https://school.second-brain.ru**
|
||||
|
||||
> Подробная техническая документация — в `TECHNICAL.md`.
|
||||
> Роадмап и текущий статус — в `ROADMAP.md`.
|
||||
> Полные правила для Claude Code — в `CLAUDE.md`.
|
||||
|
||||
---
|
||||
|
||||
## Стек
|
||||
|
||||
| Слой | Технология | Версия |
|
||||
|------|-----------|--------|
|
||||
| Фреймворк | Next.js (App Router) | **16.2.2** |
|
||||
| Язык | TypeScript (strict) | 5.x |
|
||||
| UI | React | 19 |
|
||||
| Стили | Tailwind CSS (CSS-based, **без** tailwind.config.ts) | 4.x |
|
||||
| Компоненты | shadcn/ui (Base UI, **не Radix**) | v4 |
|
||||
| ORM | Prisma | 7.x |
|
||||
| Auth | Better Auth (**не NextAuth**) | 1.6.0 |
|
||||
| Редактор | TipTap WYSIWYG | 2.x |
|
||||
| Drag-and-drop | @dnd-kit | latest |
|
||||
| БД | PostgreSQL | 16 |
|
||||
| Email | Resend | latest |
|
||||
| Хранилище | Hetzner Object Storage (S3-совместимый) | — |
|
||||
| Видео | Kinescope (iframe embed) | — |
|
||||
| Валидация | Zod | 3.x |
|
||||
|
||||
---
|
||||
|
||||
## Критические отличия от стандартных версий
|
||||
|
||||
Эти технологии отличаются от того, что содержится в обучающих данных большинства моделей. **Читай документацию перед написанием кода.**
|
||||
|
||||
### Next.js 16.2.2
|
||||
- Используется `proxy.ts` вместо `middleware.ts`
|
||||
- Экспортируемая функция называется `proxy`, не `middleware`
|
||||
- Перед написанием кода смотри `node_modules/next/dist/docs/`
|
||||
|
||||
### Tailwind CSS v4
|
||||
- **Нет файла `tailwind.config.ts`** — вся кастомизация через CSS
|
||||
- Конфиг: `@import "tailwindcss"` и `@theme` в `globals.css`
|
||||
|
||||
### shadcn/ui v4
|
||||
- Базируется на `@base-ui/react`, **не Radix**
|
||||
- Нет пропа `asChild` — триггеры обычные элементы
|
||||
- Установка: `npx shadcn@latest add <component>`
|
||||
|
||||
### Prisma 7.x
|
||||
- Импорт: `from "@/generated/prisma/client"` (не `from "@/generated/prisma"`)
|
||||
- Требует адаптер: `new PrismaPg({ connectionString })`
|
||||
- Не генерирует `index.ts`
|
||||
|
||||
### Better Auth 1.6.0
|
||||
- **Не путать с NextAuth** — другая библиотека, другое API
|
||||
- В этом проекте используется **bcrypt** (не scrypt по умолчанию)
|
||||
- Настройки `password.hash` / `password.verify` в `src/lib/auth.ts`
|
||||
- `auth-client.ts` не использует `baseURL` — берёт `window.location.origin`
|
||||
- Seed-пользователи вставлены через SQL с `emailVerified = true`
|
||||
|
||||
---
|
||||
|
||||
## Команды
|
||||
|
||||
```bash
|
||||
# Разработка
|
||||
npm run dev # localhost:3000
|
||||
docker compose up -d # Поднять PostgreSQL локально
|
||||
|
||||
# Проверка качества
|
||||
npm run lint # ESLint
|
||||
npm run type-check # tsc --noEmit
|
||||
|
||||
# Сборка
|
||||
npm run build
|
||||
npm run start
|
||||
|
||||
# База данных
|
||||
npx prisma migrate dev --name <snake_case_name> # Новая миграция
|
||||
npx prisma migrate deploy # Применить в production
|
||||
npx prisma generate # Пересоздать клиент
|
||||
npx prisma db seed # Заполнить тестовыми данными
|
||||
npx prisma studio # GUI для БД
|
||||
|
||||
# Production-деплой (на сервере в /root/digital-household/lms-sb/)
|
||||
git pull
|
||||
docker compose -f docker-compose.prod.yml up -d --build
|
||||
```
|
||||
|
||||
При старте production-контейнер автоматически запускает `prisma migrate deploy`, затем `node server.js`.
|
||||
|
||||
---
|
||||
|
||||
## Структура проекта
|
||||
|
||||
```
|
||||
lms-system/
|
||||
├── src/
|
||||
│ ├── app/ # Next.js App Router
|
||||
│ │ ├── (auth)/ # login, register, verify-email
|
||||
│ │ ├── (student)/ # dashboard, courses/[slug], lessons/[lessonId]
|
||||
│ │ ├── curator/ # homework review, dashboard
|
||||
│ │ ├── admin/ # courses, users, settings, categories
|
||||
│ │ └── api/ # REST endpoints + Better Auth handler
|
||||
│ ├── components/
|
||||
│ │ ├── ui/ # shadcn/ui (автогенерация, не трогать)
|
||||
│ │ ├── editor/ # TipTap WYSIWYG
|
||||
│ │ ├── player/ # Kinescope Player wrapper
|
||||
│ │ ├── course/ # Компоненты курса
|
||||
│ │ └── layout/ # Header, Sidebar, Footer
|
||||
│ ├── lib/
|
||||
│ │ ├── auth.ts # Better Auth config (сервер)
|
||||
│ │ ├── auth-client.ts # Better Auth client (браузер)
|
||||
│ │ ├── prisma.ts # Prisma singleton
|
||||
│ │ ├── s3.ts # Hetzner S3 клиент
|
||||
│ │ ├── email.ts # Resend email helpers
|
||||
│ │ └── utils.ts # cn() и утилиты
|
||||
│ ├── types/ # TypeScript-типы
|
||||
│ ├── proxy.ts # Auth middleware (защита маршрутов)
|
||||
│ └── middleware.ts # Обёртка над proxy
|
||||
├── prisma/
|
||||
│ ├── schema.prisma # Схема БД (~314 строк)
|
||||
│ ├── seed.ts # Тестовые данные
|
||||
│ └── migrations/ # НЕ РЕДАКТИРОВАТЬ ВРУЧНУЮ
|
||||
├── docker-compose.yml # Локальная разработка
|
||||
├── docker-compose.prod.yml # Production
|
||||
├── Dockerfile # Multi-stage build
|
||||
├── .env.example # Шаблон переменных (без секретов)
|
||||
└── .env.local # Локальные секреты (в .gitignore)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Роли и маршруты
|
||||
|
||||
| Роль | Маршруты | Описание |
|
||||
|------|---------|----------|
|
||||
| `admin` | `/admin/*`, `/curator/*`, всё | Полный доступ |
|
||||
| `curator` | `/curator/*`, `/dashboard` | Проверка ДЗ, комментарии |
|
||||
| `student` | `/dashboard`, `/courses/*` | Просмотр курсов, прогресс |
|
||||
|
||||
Защита маршрутов — в `src/proxy.ts` + проверка сессии в layout/page.
|
||||
|
||||
---
|
||||
|
||||
## Модель данных (ключевые сущности)
|
||||
|
||||
```
|
||||
User → Session, Account, Verification # Better Auth
|
||||
Category → Course → Module → Lesson # Структура контента
|
||||
Lesson → LessonFile # Файлы к уроку
|
||||
CourseEnrollment (userId + courseId) # Доступ с expiresAt
|
||||
AccessLog # Аудит доступов
|
||||
LessonProgress (userId + lessonId) # Прогресс ученика
|
||||
Lesson → Homework → HomeworkSubmission → HomeworkFeedback # ДЗ
|
||||
Lesson → LessonComment # Обсуждения (soft-delete)
|
||||
Lesson → Quiz → QuizQuestion → QuizOption # Тесты
|
||||
Quiz → QuizAttempt # Результаты тестов
|
||||
Settings (key-value) # Настройки платформы
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Дизайн-система «Second Brain Aubade»
|
||||
|
||||
Типографский, монохромный, газетный стиль.
|
||||
|
||||
| Токен | Значение |
|
||||
|-------|---------|
|
||||
| Шрифт | Fira Mono (400/500/700, Latin + Cyrillic) |
|
||||
| Фон | `#F5F5F0` (тёплый off-white) |
|
||||
| Текст | `#323232` |
|
||||
| Поверхность | `#E8E8E0` |
|
||||
| Акцент | `#E8F0D8` (зелёный) |
|
||||
| Border | `#AAAAAA` |
|
||||
| Сайдбар | `#2A2A28` (тёмный) |
|
||||
|
||||
**Aubade-эффект** (карточки и кнопки):
|
||||
- Border: `2px solid #AAAAAA`
|
||||
- Shadow: `4px 4px 0 0 #AAAAAA`
|
||||
- Hover: `translate(-2px, -2px)` + shadow `6px 6px`
|
||||
- Active: `translate(2px, 2px)`, shadow убирается
|
||||
- CSS-классы: `.card-aubade`, `.btn-aubade`, `.btn-aubade-accent`, `.tag-aubade`
|
||||
|
||||
---
|
||||
|
||||
## Инфраструктура
|
||||
|
||||
| Компонент | Значение |
|
||||
|-----------|---------|
|
||||
| Сервер | Hetzner VPS — 8 vCPU / 16 GB RAM / 320 GB NVMe |
|
||||
| Reverse proxy | Caddy (auto HTTPS, Let's Encrypt) |
|
||||
| Порт | 3010 (внутри контейнера 3000) |
|
||||
| БД | PostgreSQL 16 (контейнер `lms-sb-db-1`) |
|
||||
| Object Storage | Hetzner S3, endpoint `nbg1.your-objectstorage.com`, бакет `second-brain-lms` |
|
||||
| Git | Gitea — `https://git.second-brain.ru/admins/lms-sb` |
|
||||
| Email | Resend, домен `mailsend.second-brain.ru` |
|
||||
| Бэкапы | PostgreSQL → Backblaze B2 (ежедневно, 03:00, ротация 7 дней) |
|
||||
|
||||
---
|
||||
|
||||
## Переменные окружения
|
||||
|
||||
```env
|
||||
DATABASE_URL="postgresql://lms_user:password@localhost:5432/lms_db"
|
||||
BETTER_AUTH_SECRET="generate-with-openssl-rand-base64-32"
|
||||
BETTER_AUTH_URL="http://localhost:3000"
|
||||
RESEND_API_KEY=""
|
||||
EMAIL_FROM="noreply@mailsend.second-brain.ru"
|
||||
S3_ENDPOINT="https://nbg1.your-objectstorage.com"
|
||||
S3_BUCKET="second-brain-lms"
|
||||
S3_ACCESS_KEY=""
|
||||
S3_SECRET_KEY=""
|
||||
S3_REGION="eu-central"
|
||||
```
|
||||
|
||||
Секреты — **только в `.env.local`**. При добавлении новых переменных обновлять `.env.example`.
|
||||
|
||||
---
|
||||
|
||||
## Правила написания кода
|
||||
|
||||
### Языки
|
||||
- **UI-строки** (заголовки, кнопки, сообщения): русский
|
||||
- **Переменные, функции, файлы, комментарии**: английский
|
||||
- **Коммиты**: английский, imperative mood (`Add lesson progress`, `Fix auth redirect`)
|
||||
|
||||
### Стиль
|
||||
- Server Actions для форм и мутаций
|
||||
- Не добавлять абстракции «на будущее» — только текущий этап
|
||||
- Нет `console.log` в production (только `console.error` для реальных ошибок)
|
||||
- Нет захардкоженных секретов, URL, ID
|
||||
|
||||
### Миграции БД
|
||||
- **Никогда** не редактировать `prisma/migrations/` вручную
|
||||
- **Всегда** спрашивать перед миграцией, которая меняет или удаляет существующие поля
|
||||
- Имена миграций: английский, snake_case (`add_lesson_progress`)
|
||||
- Перед `prisma migrate deploy` на production — бэкап БД
|
||||
|
||||
### Файлы и загрузки
|
||||
- Все файлы (ДЗ, PDF, изображения) — через Hetzner Object Storage, **не на диск VPS**
|
||||
- Обложки курсов: 16:9, max 5 MB, JPG/PNG/WebP
|
||||
- Изображения в уроках: max 10 MB
|
||||
- Файлы к уроку: max 100 MB, PDF/ZIP/DOCX
|
||||
|
||||
### Коммиты
|
||||
- Один коммит = одна логически завершённая единица
|
||||
- Перед коммитом: `npm run lint && npm run type-check`
|
||||
- После завершения каждого этапа ROADMAP — `git push` в Gitea
|
||||
|
||||
---
|
||||
|
||||
## Чек-лист перед коммитом
|
||||
|
||||
- [ ] `npm run lint` — без ошибок
|
||||
- [ ] `npm run type-check` — без ошибок
|
||||
- [ ] Новые `.env` переменные добавлены в `.env.example`
|
||||
- [ ] Миграция БД согласована (если есть)
|
||||
- [ ] Нет `console.log`, нет секретов в коде
|
||||
|
||||
---
|
||||
|
||||
## Тестовые аккаунты
|
||||
|
||||
| Email | Пароль | Роль |
|
||||
|-------|--------|------|
|
||||
| admin@second-brain.ru | Password123! | admin |
|
||||
| curator@second-brain.ru | Password123! | curator |
|
||||
| student@second-brain.ru | Password123! | student |
|
||||
|
||||
---
|
||||
|
||||
## Текущий статус проекта
|
||||
|
||||
**Завершено (9 из 13 этапов):**
|
||||
- Этап 0: Каркас, auth, роли, деплой
|
||||
- Этап 1: Курсы → Модули → Уроки (CRUD, drag-and-drop, TipTap, S3)
|
||||
- Этап 1.5: Расширенный доступ (сроки, категории, AccessLog)
|
||||
- Этап 2: Kinescope-интеграция, рендер уроков для ученика
|
||||
- Этап 3: Прогресс (кнопка завершения, прогресс-бар)
|
||||
- Этап 5: Домашние задания + обратная связь куратора
|
||||
- Этап 6: Обсуждения под уроками
|
||||
- Этап 7: Email-уведомления (Resend)
|
||||
- Этап 8: Импорт уроков из Markdown (Obsidian)
|
||||
|
||||
**В работе:**
|
||||
- Этап 9: Настройки платформы (Admin Settings)
|
||||
|
||||
**Впереди:**
|
||||
- Этап 11: Импорт/экспорт учеников (CSV, миграция с emdesell)
|
||||
- Этап 12: Telegram-бот + аналитика (Yandex.Metrika)
|
||||
- Этап 13: Тесты и квизы с автопроверкой
|
||||
|
||||
**Бэклог:** сертификаты, геймификация, платежи, медиатека, цифровой сад, CI/CD
|
||||
|
||||
Полный роадмап с деталями и критериями готовности — в `ROADMAP.md`.
|
||||
|
||||
---
|
||||
|
||||
## Известные ограничения
|
||||
|
||||
- Seed-пользователи вставлены через SQL с `emailVerified = true` (обход Better Auth)
|
||||
- Загрузка файлов: нет лимита на уровне Next.js (только S3)
|
||||
- Drag-and-drop: возможны race conditions при быстрых перетаскиваниях (некритично)
|
||||
- `expiresAt` проверяется в UI, но не блокирует доступ на уровне middleware
|
||||
|
||||
Reference in New Issue
Block a user