|
Some checks failed
Web App CI / test (push) Waiting to run
Web App CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
Backend CI / test (push) Has been cancelled
Backend CI / build (push) Has been cancelled
|
||
|---|---|---|
| .. | ||
| certs | ||
| data | ||
| docs | ||
| domain_certs | ||
| isar | ||
| lib | ||
| linux | ||
| macos | ||
| public | ||
| test | ||
| .dockerignore | ||
| admins | ||
| analysis_options.yaml | ||
| build_app.sh | ||
| codegen.sh | ||
| connect.sh | ||
| cursor_agent_loop.sh | ||
| Dockerfile | ||
| ENV_VARIABLES.md | ||
| ggg.json | ||
| libisar.dylib | ||
| libisar.so | ||
| mnemo_cards_server.service | ||
| prepare_python.sh | ||
| pubspec.lock | ||
| pubspec.yaml | ||
| README.md | ||
| restart_dev.sh | ||
| run_dev.sh | ||
| run_http_production.sh | ||
| run_https.sh | ||
| run_https_production.sh | ||
| rustore_jwt.py | ||
| rustore_jwt.sh | ||
| rustore_test_private_key.txt | ||
| test_api.sh | ||
🎴 Mnemo Cards Backend
Backend сервер для приложения Mnemo Cards - платформы для изучения иностранных слов с использованием мнемотехники.
📋 Оглавление
- Технологии
- Архитектура
- Быстрый старт
- Структура проекта
- API документация
- Разработка
- Деплой
- Тестирование
- Конфигурация
🚀 Технологии
Backend Stack
- Язык: Dart 3.8+
- HTTP сервер: Shelf
- База данных: PostgreSQL 16 + Drift ORM
- Аутентификация: JWT (jaguar_jwt)
- DI: get_it + injectable
- API: RESTful API v2
Интеграции
- Платежи: YooKassa (Российские платежи), Google Play, RuStore
- Telegram Bot API: Авторизация через Telegram
- PostgreSQL: Production-ready реляционная БД
DevOps
- Контейнеризация: Docker + Docker Compose
- Deployment: Coolify (или любой Docker хостинг)
- CI/CD: GitHub Actions (опционально)
🏗️ Архитектура
┌─────────────────┐
│ Web Client │
│ (Flutter Web) │
└────────┬────────┘
│ HTTPS
▼
┌─────────────────┐ ┌──────────────┐
│ Mnemo Backend │◄────►│ PostgreSQL │
│ (Dart/Shelf) │ │ Database │
└────────┬────────┘ └──────────────┘
│
├─► YooKassa API (Payments)
├─► Telegram Bot API (Auth)
├─► Google Play API (IAP)
└─► RuStore API (IAP)
Основные компоненты
- API Server (
lib/api/mnemo_shelf.dart) - HTTP сервер на Shelf - Database (
lib/database/) - Drift ORM + PostgreSQL - Managers (
lib/user/,lib/packs/, etc.) - Бизнес-логика - Cron Jobs (
lib/cron/) - Фоновые задачи - Auth (
lib/auth/,lib/api/authorize/) - JWT аутентификация
⚡ Быстрый старт
Требования
- Dart SDK: 3.8.0+
- PostgreSQL: 16+ (локально или Docker)
- Docker & Docker Compose (опционально, для PostgreSQL)
1. Клонирование репозитория
git clone <repository-url>
cd mnemo_cards_backend
2. Установка зависимостей
# Установка Dart зависимостей
dart pub get
# Генерация кода (Drift, Injectable, JSON)
dart run build_runner build --delete-conflicting-outputs
3. Настройка PostgreSQL
Вариант A: Docker (рекомендуется для разработки)
# Запустить PostgreSQL через Docker Compose
docker-compose up -d postgres
# Проверить статус
docker-compose ps
Вариант B: Локальная установка
# macOS
brew install postgresql@16
brew services start postgresql@16
# Linux
sudo apt install postgresql-16
sudo systemctl start postgresql
Создать базу данных:
CREATE DATABASE mnemo_cards_dev;
CREATE USER mnemo_user WITH PASSWORD 'dev_password';
GRANT ALL PRIVILEGES ON DATABASE mnemo_cards_dev TO mnemo_user;
4. Настройка переменных окружения
# Создать .env файл из примера
cp .env.example .env
# Отредактировать .env (указать реальные значения)
nano .env
Минимальная конфигурация для .env:
# PostgreSQL
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mnemo_cards_dev
DB_USER=mnemo_user
DB_PASSWORD=dev_password
DB_SSL_MODE=disable
# Backend
PORT=3000
SERVER_ADDRESS=0.0.0.0
WORK_DIR=/root/mnemo_cards_backend
DEBUG=true
# Admin IDs
ADMIN_IDS=1
# JWT Secrets (сгенерировать случайные!)
JWT_SECRET=dev_jwt_secret_change_me_12345
JWT_REFRESH_SECRET=dev_refresh_secret_change_me_12345
⚠️ Важно: Сгенерируйте надежные JWT секреты для продакшена:
openssl rand -base64 32
5. Запуск сервера
# Development mode
./run_dev.sh
# Или напрямую
dart run lib/main.dart
Сервер запустится на http://localhost:3000
6. Проверка работоспособности
# Health check
curl http://localhost:3000/health
# API version info
curl http://localhost:3000/api/v2/health
📁 Структура проекта
mnemo_cards_backend/
├── lib/
│ ├── api/ # API endpoints и middleware
│ │ ├── v2/ # RESTful API v2
│ │ │ ├── auth_api_v2.dart # Авторизация
│ │ │ ├── users_api_v2.dart # Пользователи
│ │ │ ├── packs_api_v2.dart # Наборы карточек
│ │ │ ├── tests_api_v2.dart # Тесты
│ │ │ ├── subscriptions_api_v2.dart # Подписки
│ │ │ ├── promocodes_api_v2.dart # Промокоды
│ │ │ ├── admin_*_api_v2.dart # Админ панель
│ │ │ └── ...
│ │ ├── authorize/ # Middleware для авторизации
│ │ ├── purchase/ # Платежи (YooKassa, Google Play, RuStore)
│ │ ├── subscription/ # Управление подписками
│ │ ├── di/ # Dependency Injection (get_it)
│ │ └── mnemo_shelf.dart # Главный HTTP сервер
│ │
│ ├── database/ # PostgreSQL + Drift ORM
│ │ ├── database.dart # Главный класс БД
│ │ ├── tables/ # Определения таблиц
│ │ │ ├── users.dart
│ │ │ ├── packs.dart
│ │ │ ├── auth.dart
│ │ │ ├── payments.dart
│ │ │ ├── word_statistics.dart # Статистика ответов на карточки
│ │ │ ├── audit.dart # Audit trail (инфраструктура)
│ │ │ └── ...
│ │ └── daos/ # Data Access Objects
│ │ ├── user_dao.dart
│ │ ├── pack_dao.dart
│ │ ├── word_statistics_dao.dart # Работа со статистикой слов
│ │ ├── audit_dao.dart # Audit logging
│ │ └── mixins/
│ │ └── soft_delete_mixin.dart # Soft delete функциональность
│ │
│ ├── user/ # User management
│ │ └── user_manager.dart
│ │
│ ├── packs/ # Pack management
│ │ ├── pack_manager.dart
│ │ └── free_packs_distributor.dart
│ │
│ ├── tests/ # Test management
│ │ └── test_manager.dart
│ │
│ ├── tasks/ # Task management
│ │ └── task_manager.dart
│ │
│ ├── promo_codes/ # Promo codes
│ │ └── promo_codes_manager.dart
│ │
│ ├── discounts/ # Discounts
│ │ └── discounts_manager.dart
│ │
│ ├── statistics/ # User statistics
│ │ ├── session_tracker.dart
│ │ ├── statistics_calculator.dart
│ │ ├── word_statistics_manager.dart # Управление статистикой ответов
│ │ └── session_tracking_middleware.dart
│ │
│ ├── cron/ # Background jobs
│ │ ├── cron_executor.dart
│ │ ├── check_payment.dart
│ │ ├── backup.dart
│ │ ├── add_free_packs.dart
│ │ └── ...
│ │
│ ├── auth/ # Authentication utilities
│ │ ├── hash.dart
│ │ └── password.dart
│ │
│ └── main.dart # Entry point
│
├── test/ # Unit & integration tests
│ ├── api/v2/ # API tests
│ ├── database/ # Database tests
│ ├── models/ # Model tests
│ └── statistics/ # Statistics tests
│
├── data/ # Static data (images, audio)
├── public/ # Public files
├── docs/ # Documentation
│
├── docker-compose.yml # Docker Compose для разработки
├── Dockerfile # Production Docker image
├── pubspec.yaml # Dart dependencies
├── .env.example # Пример переменных окружения
│
├── run_dev.sh # Скрипт запуска (dev)
├── run_http_production.sh # Скрипт запуска (prod)
├── build_app.sh # Скрипт сборки
├── codegen.sh # Генерация кода
│
├── COOLIFY_SETUP.md # Инструкции по деплою в Coolify
├── ENVIRONMENT_VARIABLES.md # Описание env переменных
├── DB_PLAN.md # План миграции БД
└── README.md # Этот файл
🌐 API документация
Base URL
- Local:
http://localhost:3000/api/v2 - Production:
https://your-domain.com/api/v2
Авторизация
Backend использует JWT Bearer tokens для авторизации:
Authorization: Bearer <access_token>
Основные endpoints
Аутентификация
POST /api/v2/auth/register # Регистрация
POST /api/v2/auth/login # Логин
POST /api/v2/auth/refresh # Обновить токен
POST /api/v2/auth/telegram # Авторизация через Telegram
GET /api/v2/auth/telegram/code # Получить код для Telegram
Пользователи
GET /api/v2/users/me # Текущий пользователь
PATCH /api/v2/users/me # Обновить профиль
GET /api/v2/users/:id # Получить пользователя
GET /api/v2/users/:id/statistics # Статистика пользователя
POST /api/v2/users/:id/balance # Обновить баланс
Наборы карточек (Packs)
GET /api/v2/packs # Список паков (preview)
GET /api/v2/packs/:id # Детали пака
GET /api/v2/packs/:id/cards # Карточки пака
POST /api/v2/packs/:id/purchase # Купить пак
Тесты
GET /api/v2/tests # Список тестов
GET /api/v2/tests/:id # Детали теста
POST /api/v2/tests/:id/start # Начать тест
POST /api/v2/tests/:id/submit # Отправить ответы
GET /api/v2/tests/:id/results # Результаты теста
Подписки
GET /api/v2/subscriptions/plans # Доступные планы
POST /api/v2/subscriptions/purchase # Купить подписку
GET /api/v2/subscriptions/my # Мои подписки
Промокоды
POST /api/v2/promocodes/apply # Применить промокод
GET /api/v2/promocodes/:code # Проверить промокод
Админ панель
GET /api/v2/admin/users # Все пользователи
GET /api/v2/admin/packs # Все паки
POST /api/v2/admin/packs # Создать пак
PUT /api/v2/admin/packs/:id # Обновить пак
DELETE /api/v2/admin/packs/:id # Удалить пак
GET /api/v2/admin/analytics # Аналитика
Health Check
GET /health # Health check endpoint
Response:
OK
💻 Разработка
Генерация кода
Backend использует code generation для:
- Drift (database code)
- Injectable (dependency injection)
- Shelf Router (routing)
- JSON Serializable (JSON mapping)
# Генерация кода
dart run build_runner build --delete-conflicting-outputs
# Watch mode (автоматическая генерация при изменениях)
dart run build_runner watch --delete-conflicting-outputs
# Или использовать скрипт
./codegen.sh
Работа с базой данных
Создание новой таблицы
- Создать файл в
lib/database/tables/:
// lib/database/tables/my_table.dart
import 'package:drift/drift.dart';
class MyTables extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get name => text()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
}
- Добавить таблицу в
lib/database/database.dart:
@DriftDatabase(
tables: [
// ... existing tables
MyTables,
],
// ...
)
- Сгенерировать код:
dart run build_runner build --delete-conflicting-outputs
Создание DAO
// lib/database/daos/my_dao.dart
import 'package:drift/drift.dart';
import '../database.dart';
part 'my_dao.g.dart';
@DriftAccessor(tables: [MyTables])
class MyDao extends DatabaseAccessor<AppDatabase> with _$MyDaoMixin {
MyDao(super.db);
Future<List<MyTable>> getAll() => select(myTables).get();
Future<MyTable?> getById(int id) =>
(select(myTables)..where((t) => t.id.equals(id))).getSingleOrNull();
Future<int> create(MyTablesCompanion entry) => into(myTables).insert(entry);
}
Добавление нового API endpoint
- Создать файл в
lib/api/v2/:
// lib/api/v2/my_api_v2.dart
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
import 'package:injectable/injectable.dart';
@lazySingleton
class MyApiV2 {
final AppDatabase _db;
MyApiV2(this._db);
Router get router {
final router = Router();
router.get('/my-endpoint', _handleGet);
router.post('/my-endpoint', _handlePost);
return router;
}
Future<Response> _handleGet(Request request) async {
// Implementation
return Response.ok('{"status": "ok"}');
}
Future<Response> _handlePost(Request request) async {
// Implementation
return Response.ok('{"status": "created"}');
}
}
- Зарегистрировать в
lib/api/mnemo_shelf.dart:
v2Router.mount('/', getIt.get<MyApiV2>().router);
Cron Jobs
Фоновые задачи запускаются автоматически при старте сервера.
Создание нового cron job:
// lib/cron/my_task.dart
import 'package:neat_periodic_task/neat_periodic_task.dart';
import 'task.dart';
class MyTask extends Task {
@override
String get name => 'My Task';
@override
Duration get interval => const Duration(hours: 1);
@override
Duration get timeout => const Duration(minutes: 5);
@override
Future<void> run() async {
print('Running my task...');
// Implementation
}
}
Добавить в lib/main.dart:
CronManager([
// ... existing tasks
MyTask(),
]).init();
Debugging
# Запуск с дебагом
DEBUG=true dart run lib/main.dart
# Логи PostgreSQL
docker-compose logs -f postgres
# Логи backend
docker-compose logs -f backend
# Подключение к PostgreSQL
docker-compose exec postgres psql -U mnemo_user -d mnemo_cards_dev
🚢 Деплой
Docker (рекомендуется)
1. Сборка образа
# Build Docker image
docker build -t mnemo_backend:latest .
# Или использовать скрипт
./build_app.sh
2. Запуск через Docker Compose
# Production
docker-compose -f docker-compose.prod.yml up -d
# Проверка
docker-compose ps
docker-compose logs -f backend
Coolify (PaaS)
Подробная инструкция по деплою в Coolify: COOLIFY_SETUP.md
Краткая версия:
- Создать PostgreSQL в Coolify
- Создать приложение (Dockerfile)
- Настроить environment variables
- Deploy
Переменные окружения для продакшена
Полное описание всех переменных: ENVIRONMENT_VARIABLES.md
Критически важные:
# PostgreSQL (используйте internal hostname в Coolify)
DB_HOST=mnemo-postgres
DB_PORT=5432
DB_NAME=mnemo_cards
DB_USER=mnemo_user
DB_PASSWORD=<сгенерированный_пароль>
DB_SSL_MODE=require
# Backend
PORT=3000
SERVER_ADDRESS=0.0.0.0
DEBUG=false
# JWT (минимум 32 символа!)
JWT_SECRET=<сгенерированный_секрет>
JWT_REFRESH_SECRET=<другой_сгенерированный_секрет>
# Admin IDs
ADMIN_IDS=1,2,3
# YooKassa (если используете)
YOOKASSA_SHOP_ID=<ваш_shop_id>
YOOKASSA_SECRET_KEY=<ваш_secret_key>
Health Checks
Backend предоставляет /health endpoint для мониторинга:
curl https://your-domain.com/health
# Response: OK
Настройка в Docker Compose:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
🧪 Тестирование
Запуск тестов
# Все тесты
dart test
# Конкретный файл
dart test test/api/v2/users_api_v2_test.dart
# С coverage
dart test --coverage=coverage
dart pub global activate coverage
format_coverage --lcov --in=coverage --out=coverage/lcov.info --report-on=lib
Структура тестов
test/
├── api/v2/ # API endpoint tests
│ ├── auth_api_v2_test.dart
│ ├── users_api_v2_test.dart
│ ├── packs_api_v2_test.dart
│ └── ...
├── database/ # Database tests
├── models/ # Model tests
└── statistics/ # Statistics tests
Написание тестов
import 'package:test/test.dart';
import 'package:mockito/mockito.dart';
import 'package:mnemo_cards_backend/database/database.dart';
void main() {
late AppDatabase db;
setUpAll(() async {
// Setup test database
db = AppDatabase.connect(
host: 'localhost',
port: 5433, // Test port
database: 'mnemo_cards_test',
username: 'test_user',
password: 'test_pass',
);
await db.migrator.createAll();
});
tearDownAll(() async {
await db.close();
});
group('UserDao', () {
test('создание пользователя', () async {
// Arrange
final user = UsersCompanion.insert(
name: 'Test User',
externalUserId: 'test123',
);
// Act
final userId = await db.userDao.createUser(user);
final created = await db.userDao.getUserById(userId);
// Assert
expect(created, isNotNull);
expect(created!.name, equals('Test User'));
});
});
}
API тестирование
# Используйте ./test_api.sh или curl
curl -X POST http://localhost:3000/api/v2/auth/register \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com", "password": "password123"}'
⚙️ Конфигурация
Основные переменные окружения
| Переменная | Описание | По умолчанию | Обязательная |
|---|---|---|---|
DB_HOST |
PostgreSQL host | localhost |
✅ |
DB_PORT |
PostgreSQL port | 5432 |
✅ |
DB_NAME |
Database name | mnemo_cards |
✅ |
DB_USER |
Database user | mnemo_user |
✅ |
DB_PASSWORD |
Database password | - | ✅ |
DB_SSL_MODE |
SSL mode | disable |
❌ |
PORT |
Backend port | 3000 |
✅ |
SERVER_ADDRESS |
Bind address | 0.0.0.0 |
✅ |
WORK_DIR |
Working directory | /app |
✅ |
DEBUG |
Debug mode | false |
❌ |
JWT_SECRET |
JWT secret | - | ✅ |
JWT_REFRESH_SECRET |
Refresh token secret | - | ✅ |
ADMIN_IDS |
Admin user IDs | - | ✅ |
YOOKASSA_SHOP_ID |
YooKassa shop ID | - | ❌ |
YOOKASSA_SECRET_KEY |
YooKassa secret | - | ❌ |
Полное описание: ENVIRONMENT_VARIABLES.md
Генерация секретов
# JWT Secret (минимум 32 символа)
openssl rand -base64 32
# Database password
openssl rand -base64 24
📚 Дополнительные ресурсы
Документация
- COOLIFY_SETUP.md - Деплой в Coolify
- ENVIRONMENT_VARIABLES.md - Переменные окружения
- DB_PLAN.md - План миграции БД (Isar → PostgreSQL)
Полезные команды
# Генерация кода
./codegen.sh
# Запуск dev сервера
./run_dev.sh
# Подключение к PostgreSQL
./connect.sh
# Сборка для продакшена
./build_app.sh
# Тестирование API
./test_api.sh
Troubleshooting
Backend не запускается
-
Проверить PostgreSQL:
docker-compose ps postgres docker-compose logs postgres -
Проверить переменные окружения:
cat .env -
Проверить подключение к БД:
docker-compose exec postgres psql -U mnemo_user -d mnemo_cards_dev
Ошибка "Connection refused"
- Убедитесь, что PostgreSQL запущен
- Проверьте
DB_HOSTиDB_PORT - В Docker используйте service name (
postgres), а неlocalhost
Ошибка "Invalid JWT secret"
- JWT секрет должен быть минимум 32 символа
- Сгенерируйте новый:
openssl rand -base64 32
Drift генерация не работает
# Очистить кэш
rm -rf .dart_tool/
dart pub get
dart run build_runner build --delete-conflicting-outputs
🤝 Контрибуция
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open Pull Request
Code Style
- Следуйте Dart Style Guide
- Используйте
dart formatперед коммитом - Пишите unit тесты для новой функциональности
📝 Лицензия
[Your License Here]
📧 Контакты
Если возникли вопросы или проблемы, создайте Issue в репозитории.
Made with ❤️ using Dart & PostgreSQL