- 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
574 lines
17 KiB
Markdown
574 lines
17 KiB
Markdown
# Вводная часть
|
||
|
||
Веб‑приложение, в котором авторизованный пользователь выбирает стек (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)
|
||
```
|