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>
14 KiB
AGENTS.md — LMS Second Brain
Собственная 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 |
| 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
Команды
# Разработка
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)+ shadow6px 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 |
Resend, домен mailsend.second-brain.ru |
|
| Бэкапы | PostgreSQL → Backblaze B2 (ежедневно, 03:00, ротация 7 дней) |
Переменные окружения
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, нет секретов в коде
Тестовые аккаунты
| Пароль | Роль | |
|---|---|---|
| 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