# 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 | | 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 ` ### 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 # Новая миграция 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