docs: add full project documentation
- Architecture: overview, 7 ADR, tech stack - Principles: code-style, git-workflow, security - API contracts: auth, profile, tests, admin endpoints - Database schema: tables, relationships, indexes - LLM strategy: prompts, fallback, validation, Qwen 2.5 14B - Onboarding: setup, Docker, .env template - Progress: roadmap, changelog - Agents: context, backend instructions Made-with: Cursor
This commit is contained in:
573
intro.md
Normal file
573
intro.md
Normal file
@@ -0,0 +1,573 @@
|
||||
# Вводная часть
|
||||
|
||||
Веб‑приложение, в котором авторизованный пользователь выбирает стек (HTML/CSS/JS/Web basics) и уровень (базовый/начинающий), получает тест из 10 или 20 теоретических вопросов, сгенерированных LLM, отвечает, а в конце видит результат с разбором ошибок.
|
||||
|
||||
## Минимальный MVP
|
||||
|
||||
Регистрация / логин / выход
|
||||
|
||||
```text
|
||||
✅ Подтверждение email
|
||||
✅ Восстановление пароля
|
||||
✅ Профиль (никнейм, страна)
|
||||
✅ Создание теста (стек + уровень + количество вопросов)
|
||||
✅ Прохождение теста (вопросы, таймер, прогресс)
|
||||
✅ Результаты (балл, разбор, объяснения)
|
||||
✅ Базовая история (последние 10 тестов)
|
||||
✅ LLM генерация вопросов + fallback на банк
|
||||
✅ Минимальная админка (QA очередь вопросов)
|
||||
```
|
||||
|
||||
## Максимальный MVP (целевое состояние продукта)
|
||||
|
||||
Это не то, что делаем сейчас — это то, к чему ведём. Все решения в коде, БД и архитектуре принимаем с оглядкой на эту картину.
|
||||
|
||||
### Пользователи и роли
|
||||
|
||||
```text
|
||||
Guest → видит лендинг, описание тарифов, примеры вопросов
|
||||
Free → базовый функционал, 5 тестов/день, 3 стека
|
||||
Pro → полный функционал, безлимит, все стеки
|
||||
Admin → модерация контента, управление пользователями
|
||||
```
|
||||
|
||||
### Модули и их максимальный объём
|
||||
|
||||
#### Auth
|
||||
|
||||
```text
|
||||
✅ Email + пароль
|
||||
✅ Подтверждение email
|
||||
✅ Восстановление пароля
|
||||
✅ OAuth (GitHub + Google)
|
||||
✅ 2FA (TOTP: Google Authenticator)
|
||||
✅ Управление сессиями (список устройств, logout везде)
|
||||
```
|
||||
|
||||
#### Профиль
|
||||
|
||||
```text
|
||||
✅ Никнейм, аватар, страна, город
|
||||
✅ Уровень (jun/mid/sen — самооценка)
|
||||
✅ Статистика на странице профиля
|
||||
✅ Публичный профиль (по username)
|
||||
✅ Приватность (скрыть из рейтингов)
|
||||
```
|
||||
|
||||
#### Тестирование
|
||||
|
||||
```text
|
||||
✅ Стеки: HTML, CSS, JS, TS, React, Vue, Node.js, Git, Web basics
|
||||
✅ Уровни: Базовый, Начинающий, Средний, Продвинутый, Эксперт
|
||||
✅ Типы вопросов:
|
||||
- Single choice
|
||||
- Multiple select
|
||||
- True/False
|
||||
- Short text (LLM-верификация)
|
||||
- Code reading (что выведет код) ← Phase 3
|
||||
- Bug fixing (найди ошибку) ← Phase 3
|
||||
✅ Режимы:
|
||||
- Фиксированный (10/20/30 вопросов)
|
||||
- Бесконечный (только Pro)
|
||||
- Марафон (100 вопросов, только Pro) ← Phase 3
|
||||
✅ Таймер (на весь тест или на вопрос)
|
||||
✅ Подсказки (Pro, 1 на вопрос)
|
||||
✅ Пауза теста (сохранение прогресса) ← Phase 2
|
||||
```
|
||||
|
||||
#### Результаты и аналитика
|
||||
|
||||
```text
|
||||
✅ Итоговый экран (балл, время, статус)
|
||||
✅ Детальный разбор (вопрос → ответ → объяснение)
|
||||
✅ История всех попыток (фильтры, поиск)
|
||||
✅ Статистика по темам (% правильных)
|
||||
✅ График прогресса по времени
|
||||
✅ Слабые места + LLM-рекомендации (Pro)
|
||||
✅ Экспорт истории в CSV (Pro)
|
||||
```
|
||||
|
||||
#### Рейтинги (Pro)
|
||||
|
||||
```text
|
||||
✅ Глобальный лидерборд (Top 100)
|
||||
✅ Рейтинг по стекам
|
||||
✅ Сезонный рейтинг (ежемесячный)
|
||||
✅ Позиция пользователя (даже вне Top 100)
|
||||
✅ Бейджи и достижения
|
||||
✅ Архив сезонов
|
||||
```
|
||||
|
||||
#### Подписка и биллинг
|
||||
|
||||
```text
|
||||
✅ Free план (всегда)
|
||||
✅ Pro: 699₽/мес или 6999₽/год
|
||||
✅ Пробный период 5 дней (с привязкой карты)
|
||||
✅ Автопродление / отмена
|
||||
✅ ЮKassa (основной провайдер)
|
||||
✅ CloudPayments (резервный)
|
||||
✅ Региональные цены (Казахстан, Беларусь, Закавказье)
|
||||
✅ Промокоды и скидки
|
||||
✅ История платежей в ЛК
|
||||
```
|
||||
|
||||
#### Уведомления
|
||||
|
||||
```text
|
||||
✅ Email: verify, reset, trial, billing
|
||||
✅ In-app баннеры и тосты
|
||||
✅ Telegram-бот (Phase 3)
|
||||
✅ Push-уведомления (Phase 3)
|
||||
```
|
||||
|
||||
#### Контент / LLM
|
||||
|
||||
```text
|
||||
✅ Генерация вопросов по стеку/уровню/типу
|
||||
✅ Проверка short text через LLM
|
||||
✅ Подсказки (один наводящий вопрос)
|
||||
✅ LLM-рекомендации по слабым местам
|
||||
✅ Мультимодельная оркестрация (local + cloud fallback)
|
||||
✅ Банк вопросов с QA-циклом
|
||||
✅ Кэш и переиспользование вопросов
|
||||
```
|
||||
|
||||
#### Админка
|
||||
|
||||
```text
|
||||
✅ QA очередь вопросов (approve/reject/edit)
|
||||
✅ Управление пользователями (бан, сброс лимитов)
|
||||
✅ Просмотр жалоб на вопросы
|
||||
✅ Операционная панель (метрики, ошибки)
|
||||
✅ Управление промокодами
|
||||
✅ Прогрев кэша вопросов
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### Полная схема данных (ориентир)
|
||||
|
||||
```text
|
||||
users
|
||||
├── subscriptions (план, статус, даты)
|
||||
├── sessions (устройства, refresh токены)
|
||||
├── oauth_accounts (GitHub/Google)
|
||||
├── totp_secrets (2FA)
|
||||
│
|
||||
├── tests
|
||||
│ └── test_questions (снепшот вопросов)
|
||||
│
|
||||
├── user_stats (агрегаты по темам)
|
||||
├── user_achievements (бейджи)
|
||||
└── user_question_log (какие вопросы видел, когда)
|
||||
|
||||
question_bank
|
||||
├── question_cache_meta (LLM метаданные)
|
||||
└── question_reports (жалобы)
|
||||
|
||||
payments
|
||||
└── payment_events (webhook лог)
|
||||
|
||||
audit_logs (действия админов)
|
||||
notifications_log (история отправок)
|
||||
promo_codes (промокоды)
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### Что закладываем архитектурно с первого дня
|
||||
|
||||
Это **принципы, не обсуждаемые** при написании любого кода:
|
||||
|
||||
```text
|
||||
1. user.plan всегда читается из БД через subscription middleware
|
||||
→ никогда не хардкодить права в контроллерах
|
||||
|
||||
2. Вопросы копируются в test_questions при старте теста (снепшот)
|
||||
→ никогда не читать question_bank "живьём" во время теста
|
||||
|
||||
3. Все LLM-вызовы только через LlmService
|
||||
→ остальной код не знает какая модель работает
|
||||
|
||||
4. Все внешние события (webhooks, LLM) валидируются по JSON-схеме
|
||||
→ никогда не доверять внешним данным без валидации
|
||||
|
||||
5. Все проверки прав и лимитов только на backend
|
||||
→ frontend только отображает состояние
|
||||
|
||||
6. Все даты хранятся в UTC
|
||||
→ конвертация в часовой пояс только на фронте
|
||||
|
||||
7. Все конфигурации через env переменные
|
||||
→ никаких хардкодов в коде
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### Фазы разработки
|
||||
|
||||
```text
|
||||
MVP 0 (сейчас)
|
||||
└── Auth + Test (2 стека) + Results + Базовая история
|
||||
|
||||
Phase 1 (платный запуск)
|
||||
└── Все стеки + Подписка + Trial + Лимиты FREE/PRO
|
||||
|
||||
Phase 2 (рост)
|
||||
└── Рейтинги + Аналитика Pro + 2FA + OAuth + Multiple select + Short text
|
||||
|
||||
Phase 3 (зрелость)
|
||||
└── Код-задачи + Бесконечный режим + Telegram + Достижения + Региональные цены
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### Фиксируем в docs-репо
|
||||
|
||||
```markdown
|
||||
# architecture/decisions/001-max-mvp-scope.md
|
||||
|
||||
## Контекст
|
||||
Определяем максимальный целевой объём продукта,
|
||||
чтобы архитектурные решения MVP не конфликтовали
|
||||
с будущим функционалом.
|
||||
|
||||
## Решение
|
||||
[ссылка на этот документ]
|
||||
|
||||
## Последствия
|
||||
- Схема БД создаётся с заделом на все фазы
|
||||
- LLM-слой абстрагирован с первого дня
|
||||
- Subscription middleware существует с первого дня
|
||||
(даже когда все пользователи Free)
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Структура репозиториев
|
||||
|
||||
```text
|
||||
samreshu-backend (Node.js + TS + API)
|
||||
samreshu-frontend (React + TS + Vite)
|
||||
samreshu-shared (общие TS-типы/интерфейсы)
|
||||
samreshu-docs (документация, ADR, прогресс)
|
||||
```
|
||||
|
||||
### Структура samreshu-docs
|
||||
|
||||
```text
|
||||
samreshu-docs/
|
||||
├── README.md # Навигация по доке
|
||||
├── architecture/
|
||||
│ ├── overview.md # Общая архитектура
|
||||
│ ├── decisions/ # ADR
|
||||
│ │ ├── 001-monorepo-vs-polyrepo.md
|
||||
│ │ ├── 002-postgresql-provider.md
|
||||
│ │ ├── 003-llm-abstraction.md
|
||||
│ │ └── ...
|
||||
│ └── diagrams/ # Схемы (Mermaid / draw.io)
|
||||
├── principles/
|
||||
│ ├── code-style.md # Соглашения по коду
|
||||
│ ├── git-workflow.md # Ветки, коммиты, PR
|
||||
│ └── security.md # Базовые правила безопасности
|
||||
├── api/
|
||||
│ └── openapi.yaml # Контракт API (или ссылка)
|
||||
├── database/
|
||||
│ └── schema.md # Описание таблиц и связей
|
||||
├── progress/
|
||||
│ ├── roadmap.md # Что планируем
|
||||
│ └── changelog.md # Что сделали
|
||||
└── onboarding/
|
||||
└── setup.md # Как поднять проект локально
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Окружение разработчика
|
||||
|
||||
### Базовые требования
|
||||
|
||||
```text
|
||||
Node.js >= 20 LTS (через nvm)
|
||||
npm >= 10
|
||||
Git >= 2.40
|
||||
Docker + Docker Compose (для локальной БД и Redis)
|
||||
VS Code (рекомендован, настройки в репо)
|
||||
```
|
||||
|
||||
### .nvmrc (в корне каждого репо)
|
||||
|
||||
```text
|
||||
20
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Backend: вспомогательные инструменты
|
||||
|
||||
### 1. ESLint
|
||||
|
||||
```bash
|
||||
npm install -D \
|
||||
eslint \
|
||||
@eslint/js \
|
||||
typescript-eslint \
|
||||
eslint-plugin-import \
|
||||
eslint-plugin-security \
|
||||
eslint-config-prettier
|
||||
```
|
||||
|
||||
```ts
|
||||
// eslint.config.ts (flat config, актуальный формат ESLint 9+)
|
||||
import js from '@eslint/js'
|
||||
import tseslint from 'typescript-eslint'
|
||||
import security from 'eslint-plugin-security'
|
||||
import importPlugin from 'eslint-plugin-import'
|
||||
|
||||
export default tseslint.config(
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
plugins: { security, import: importPlugin },
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'@typescript-eslint/explicit-function-return-type': 'warn',
|
||||
'security/detect-object-injection': 'warn',
|
||||
'import/order': ['error', { 'newlines-between': 'always' }],
|
||||
'no-console': 'warn',
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### 2. Prettier
|
||||
|
||||
```bash
|
||||
npm install -D prettier
|
||||
```
|
||||
|
||||
```json
|
||||
// .prettierrc
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100,
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
```
|
||||
|
||||
```text
|
||||
// .prettierignore
|
||||
dist/
|
||||
node_modules/
|
||||
*.sql
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### 3. Husky + lint-staged (проверки перед коммитом)
|
||||
|
||||
```bash
|
||||
npm install -D husky lint-staged
|
||||
npx husky init
|
||||
```
|
||||
|
||||
```json
|
||||
// package.json
|
||||
{
|
||||
"lint-staged": {
|
||||
"*.{ts,js}": ["eslint --fix", "prettier --write"],
|
||||
"*.{json,md}": ["prettier --write"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
# .husky/pre-commit
|
||||
npx lint-staged
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### 4. Commitlint (единый стиль коммитов)
|
||||
|
||||
```bash
|
||||
npm install -D @commitlint/cli @commitlint/config-conventional
|
||||
```
|
||||
|
||||
```ts
|
||||
// commitlint.config.ts
|
||||
export default {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
# .husky/commit-msg
|
||||
npx --no -- commitlint --edit $1
|
||||
```
|
||||
|
||||
**Формат коммитов:**
|
||||
|
||||
```text
|
||||
feat: добавить генерацию вопросов через LLM
|
||||
fix: исправить подсчёт результатов теста
|
||||
chore: обновить зависимости
|
||||
docs: добавить описание LLM-модуля
|
||||
refactor: переработать subscription middleware
|
||||
test: добавить тесты для auth сервиса
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### 5. TypeScript (строгий режим)
|
||||
|
||||
```json
|
||||
// tsconfig.json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### 6. Vitest (тесты)
|
||||
|
||||
```bash
|
||||
npm install -D vitest @vitest/coverage-v8
|
||||
```
|
||||
|
||||
```ts
|
||||
// vitest.config.ts
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'node',
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'lcov'],
|
||||
thresholds: { lines: 70 }, // минимальное покрытие MVP
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Frontend: вспомогательные инструменты
|
||||
|
||||
```bash
|
||||
npm install -D \
|
||||
eslint \
|
||||
typescript-eslint \
|
||||
eslint-plugin-react \
|
||||
eslint-plugin-react-hooks \
|
||||
eslint-plugin-jsx-a11y \
|
||||
prettier \
|
||||
husky \
|
||||
lint-staged \
|
||||
@commitlint/cli \
|
||||
@commitlint/config-conventional \
|
||||
vitest \
|
||||
@testing-library/react \
|
||||
@testing-library/user-event
|
||||
```
|
||||
|
||||
**Отличия от backend:**
|
||||
|
||||
- `eslint-plugin-react-hooks` — обязателен, ловит типичные ошибки с хуками
|
||||
- `eslint-plugin-jsx-a11y` — базовая доступность (a11y) прямо в линтере
|
||||
- `@testing-library/react` вместо чистого vitest для компонентов
|
||||
|
||||
***
|
||||
|
||||
## .editorconfig (общий для всех репо)
|
||||
|
||||
```ini
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## VS Code: рекомендуемые расширения
|
||||
|
||||
```json
|
||||
// .vscode/extensions.json
|
||||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"prisma.prisma",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"eamodio.gitlens",
|
||||
"streetsidesoftware.code-spell-checker-russian",
|
||||
"usernamehw.errorlens",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"mikestead.dotenv"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
// .vscode/settings.json
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative"
|
||||
}
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Итог: что фиксируем в docs-репо прямо сейчас
|
||||
|
||||
```markdown
|
||||
# principles/code-style.md
|
||||
|
||||
- Язык кода: английский (переменные, функции, комментарии)
|
||||
- Язык коммитов: английский (conventional commits)
|
||||
- Язык документации: русский
|
||||
- Форматтер: Prettier (конфиг в репо — не обсуждается)
|
||||
- Линтер: ESLint strict + security plugin
|
||||
- Тесты: Vitest, минимум 70% покрытие на сервисном слое
|
||||
- any в TypeScript: запрещён (только через явный комментарий с обоснованием)
|
||||
- console.log в коде: запрещён в prod (только через logger)
|
||||
```
|
||||
Reference in New Issue
Block a user