Return debug info in API response for admin auth errors
Some checks failed
Backend CI / test (push) Waiting to run
Backend 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
Deploy Telegram Bot / Deploy Telegram Bot (push) Has been cancelled

This will show the exact reason for token verification failure
This commit is contained in:
Dmitry 2025-12-12 01:05:05 +03:00
parent 24b584d835
commit a92cade04f
10 changed files with 985 additions and 21 deletions

181
PROGRESS.md Normal file
View file

@ -0,0 +1,181 @@
2025-12-07
- Добавлена подробная инструкция по миграции в Coolify (Docker backend, статический web/admin, внешняя БД) — `docs/coolify-migration.md`.
# Mnemo Cards Project Progress
## Infrastructure & Deployment
### ✅ Completed Tasks
- **nginx Configuration System**: Implemented robust nginx configuration generation and deployment
- Created `generate-nginx-configs.sh` for generating all nginx configurations
- Created `deploy-nginx-configs.sh` for safe deployment with validation
- Updated `check-nginx.sh` with port conflict detection and cleanup
- Integrated into CI/CD pipeline
- **Reverse Proxy Architecture**: Properly configured nginx as reverse proxy for all services
- `mnemo-cards.online` → Flutter web app (static files)
- `code.mnemo-cards.online` → Forgejo (port 3000)
- `vscode.mnemo-cards.online` → VSCode Server (port 8443)
- **Automated Version Management**: Implemented pre-deploy version bumping
- Created `bump-version.sh` script for automatic version increment
- Integrated as pre-hook in CI/CD pipeline
- Updates both pubspec.yaml and API configuration
- Automatic git commit with version changes
- **SSL/TLS Configuration**: Secure HTTPS setup with Let's Encrypt certificates
- Multi-domain certificates for all subdomains
- Proper certificate permissions and renewal
- **Admin API Configuration**: Fixed admin authentication routing
- Admin panel now correctly routes authentication requests to production backend
- Prevents localhost:8000 routing issues during development
- Separate API client for admin endpoints ensures proper server targeting
- **Service Reliability**: Enhanced service management and monitoring
- Port conflict detection and resolution
- Orphaned process cleanup
- Configuration validation before deployment
- Emergency fallback configurations
- Coolify container orchestration platform deployed
### 🔄 In Progress
- **Coolify Migration**: Container orchestration platform for services
- Coolify installed and configured on `coolify.mnemo-cards.online`
- Automatic startup configured via cron @reboot
- Ready for service migration (backend, web, admin)
- **Monitoring & Alerting**: Implement comprehensive service monitoring
- **Backup Automation**: Automated backup procedures for all services
### 📋 Planned Tasks
- **Load Balancing**: Implement load balancing for high availability
- **CDN Integration**: Content delivery network for static assets
- **Security Hardening**: Additional security measures and hardening
## Development Status
### Web Application (mnemo_cards_web_v2)
- **Framework**: Flutter Web
- **Status**: Active development
- **Key Features**: Vocabulary learning, testing, statistics
- **Recent Updates**:
- Added card voice support (metadata, API, web playback with error UI)
- New voices collection linked to cards and public endpoints for list/mp3
- Web CardViewer now fetches voices and plays audio via audioplayers
- Added unit tests for API config and voice fetching
- Added app version display on authentication page
- **UI Components Refactoring**: Extracted authentication components
- Created `SignInWithGoogleButton` component for Google authentication
- Created `SignInWithTelegram` component for Telegram authentication
- Improved code maintainability and reusability
- **Profile Page UI/UX Improvements**: Enhanced profile page design and functionality
- Removed guest view - profile now only accessible for authenticated users
- Removed account statistics (packs/purchases) from profile
- Removed language toggle from settings
- Created custom `ThemeToggleWidget` with sun/moon icons and animations
- Enhanced subscription section with premium badge and upgrade button for free users
- Added staggered animations for card appearances
- Improved logout flow with confirmation dialog
- Added comprehensive unit tests for new components
- **Game/Test Flow Cleanup**: Simplified testing UX and fixed theme visuals
- Removed legacy traditional test entry from `test_page.dart` (interactive mode only)
- Added SafeArea wrapping and themed question cards on `game_page.dart`
- Added widget tests for themed question cards, exit control, and interactive-only notice
- Timer/progress now sourced from live session state on `game_page.dart`
- **Game Tests Package**: Started shared package `games/packages/game_tests`
- Added configurable `GameTestSettings` (sounds/haptics/delay)
- Integrated into `mnemo_cards_web_v2` via `TestsModule`/`TestsStateManager`
- **Test Page Code Quality Fix**: Fixed critical code duplication and compilation issues
- Removed 700+ lines of duplicate code in `test_page.dart`
- Added missing state variables for test functionality
- Created `TestResult` class for proper result handling
- Fixed deprecated `withOpacity` method calls
- Restored full test functionality with interactive game and traditional test modes
- **PackDetailsHeader Code Fix**: Fixed critical structural issues in pack details header widget
- Resolved broken method structure with hanging code blocks
- Created missing `_buildTitleSection` method
- Removed undefined `cachedImage` variable usage
- Cleaned up unused imports
- Fixed all linter errors and compilation issues
### Mobile Application (mnemo_cards)
- **Framework**: Flutter
- **Status**: Active development
- **Key Features**: Cross-platform mobile app
### Backend (mnemo_cards_backend)
- **Framework**: Dart with Shelf
- **Status**: Production ready
- **Key Features**: API, authentication, data management
### Common Libraries
- **mnemo_cards_common**: Shared models and utilities
- **mnemo_cards_common_backend**: Backend-specific common code
- **mnemo_cards_frontend_common**: Frontend-specific common code
## Testing
### Unit Tests
- **Coverage**: Basic test coverage implemented
- **Status**: Ongoing - needs expansion
### Integration Tests
- **Status**: Minimal - needs implementation
### E2E Tests
- **Status**: Not implemented
## Deployment Pipeline
### CI/CD Status
- **Platform**: Forgejo Actions (self-hosted)
- **Triggers**: Push to master branch
- **Jobs**:
- Backend deployment
- Web application deployment
- Admin panel deployment (improved portability)
- Telegram bot deployment
- Nginx configuration deployment
- Service verification
- **Deployment Script Improvements**:
- Enhanced admin panel deployment script portability
- Script can now be run from any directory within the project
- Automatic project root detection and navigation
- Improved error handling and user feedback
### Environments
- **Production**: `147.45.152.129` (mnemo-cards.online)
- **Development**: Local development environments
## Performance Metrics
### Target Metrics
- **Page Load Time**: < 3 seconds
- **API Response Time**: < 500ms
- **Uptime**: 99.9%
### Current Status
- Basic monitoring in place
- Performance optimization ongoing
## Security
### Implemented
- HTTPS enforcement
- SSL/TLS certificates (Let's Encrypt for all domains including admin.mnemo-cards.online)
- Automated SSL certificate renewal
- Basic firewall rules
- Service isolation
### Planned
- Advanced security headers
- Rate limiting
- Intrusion detection
- Regular security audits
---
*Last updated: December 10, 2025*

170
TODO.md Normal file
View file

@ -0,0 +1,170 @@
- Подготовить Dockerfile и CI/CD для Dart backend под Coolify.
- Подготовить Dockerfile и CI/CD для веб/админки (статический билд + nginx/caddy).
- Завести сервисы и домены в Coolify (api/web/admin) с Traefik/LE.
- Переключить трафик, остановить legacy nginx/systemd после проверки.
# Mnemo Cards TODO List
## High Priority
### Infrastructure & Deployment
- [x] **Admin Panel Deployment**: Fixed automated CI/CD pipeline for admin panel
- Fixed directory navigation issue in `tools/deploy/admin/deploy.sh`
- Modified `check_project_root()` function to automatically navigate to `mnemo_cards_admin/web`
- **Enhanced portability**: Script can now be run from any directory within the project
- Added intelligent project root detection that searches upwards from script location
- **SSL Certificate Fix**: Resolved HTTPS certificate issue for admin.mnemo-cards.online
- Created automated SSL certificate renewal script (`tools/ssl/renew_admin_ssl.sh`)
- Obtained Let's Encrypt certificate for admin.mnemo-cards.online
- Updated nginx configuration to use HTTPS with proper SSL certificates
- Added automatic certificate renewal via cron job
- **Admin API Configuration Fix**: Fixed admin authentication API URL routing
- Created separate `adminApiClient` that always uses production backend (`https://api.mnemo-cards.online`)
- Admin authentication now correctly routes to production server instead of localhost:8000
- Prevents authentication failures during local development
- Admin panel deployment now works correctly from CI/CD pipeline and any project directory with valid HTTPS
- [x] **Telegram Bot Deployment**: Created automated CI/CD pipeline for telegram bot
- Created `deploy_bot.yaml` workflow for automated bot deployment
- Triggers on changes to `mnemo_cards_telegram_bot/**`, `mnemo_cards_common/**`, `mnemo_cards_common_backend/**`
- Compiles Dart executable and updates systemd service
- Includes proper error handling and service verification
- [ ] **Monitoring Setup**: Implement comprehensive monitoring for all services
- Application performance monitoring (APM)
- Server resource monitoring
- Service health checks
- Alerting system
- [ ] **Backup Automation**: Automated backup procedures
- Database backups
- Configuration backups
- User data backups
- Recovery testing
- [ ] **Load Testing**: Performance testing under load
- Identify bottlenecks
- Optimize resource usage
- Scalability testing
### Testing
- [ ] **Unit Test Coverage**: Expand unit test coverage
- Backend services: Target 80% coverage
- Frontend components: Target 70% coverage
- Common libraries: Target 90% coverage
- ✅ Added unit test for version display on auth page
- ✅ Refactored authentication components - need to add unit tests for SignInWithGoogleButton and SignInWithTelegram
- ✅ Added comprehensive unit tests for ThemeToggleWidget (7 test cases covering all functionality)
- ✅ Game/Test flow cleanup: removed traditional test entry, themed game page, added widget coverage for question card surfaces, exit control, and interactive-only notice; timer/progress pulled from live session state
- ✅ Game Tests package: created `games/packages/game_tests`, added `GameTestSettings`, integrated into `mnemo_cards_web_v2` settings and state manager
- ✅ **Test Page Code Quality Fix**: Fixed critical code duplication in `test_page.dart`
- Removed 700+ lines of duplicate code
- Added missing state variables and TestResult class
- Fixed deprecated method calls and compilation issues
- Restored full test functionality
- ✅ **PackDetailsHeader Code Fix**: Fixed critical structural issues in pack details header widget
- Resolved broken method structure with hanging code blocks
- Created missing `_buildTitleSection` method
- Removed undefined `cachedImage` variable usage
- Cleaned up unused imports
- Fixed all linter errors and compilation issues
- [ ] **Integration Tests**: Implement comprehensive integration testing
- API endpoint testing
- Database operations
- Service interactions
- [ ] **E2E Tests**: End-to-end testing suite
- User journey testing
- Cross-browser compatibility
- Mobile device testing
## Medium Priority
### Security
- [ ] **Security Audit**: Comprehensive security review
- Code security analysis
- Infrastructure security
- Data protection compliance
- [ ] **Advanced Security**: Enhanced security measures
- Rate limiting implementation
- Advanced firewall rules
- Intrusion detection
- Security headers optimization
### Performance
- [ ] **Frontend Optimization**: Web application performance
- Bundle size optimization
- Lazy loading implementation
- Caching strategies
- [ ] **Backend Optimization**: API performance improvements
- Database query optimization
- Caching layer implementation
- Response time optimization
### Features
- [x] **Card Voices**: Audio metadata & playback
- Isar collection for voices linked to cards
- Public API for voices list and mp3 delivery
- Web UI playback with error handling and tests
- [ ] **User Experience**: Enhanced user experience features
- Offline mode support
- Progressive Web App (PWA)
- Advanced statistics and analytics
## Low Priority
### Infrastructure
- [ ] **CDN Integration**: Content delivery network
- Static asset delivery
- Global performance improvement
- Cost optimization
- [ ] **Containerization**: Docker containerization
- Development environment standardization
- Deployment consistency
- Scaling capabilities
### Documentation
- [ ] **API Documentation**: Comprehensive API docs
- OpenAPI/Swagger documentation
- Developer guides
- Integration examples
- [ ] **User Documentation**: User-facing documentation
- User guides
- FAQ section
- Video tutorials
### Analytics
- [ ] **Usage Analytics**: User behavior analysis
- Feature usage tracking
- Performance metrics
- User engagement analysis
---
## Development Guidelines
### Code Quality
- Follow established coding standards
- Implement proper error handling
- Use TypeScript/Flow for type safety
- Regular code reviews
### Testing Strategy
- Write tests before implementing features (TDD)
- Maintain test coverage above established thresholds
- Regular integration testing
- Performance regression testing
### Deployment Process
- Automated deployment pipeline
- Rollback capabilities
- Environment parity
- Zero-downtime deployments
---
*Last updated: December 6, 2025*

136
docs/coolify-migration.md Normal file
View file

@ -0,0 +1,136 @@
# Coolify: перенос сервисов (backend Docker, web статик, внешняя БД)
Цель: перенести существующие сервисы в Coolify (через Traefik/Let’s Encrypt), сохранив БД на хосте. Ниже — пошаговая инструкция, Dockerfile-примеры, настройки Coolify и порядок переключения трафика.
## 1. Backend (Dart) в Docker
### Структура образа
- Base (builder): `dart:stable-sdk`
- Runtime: `debian:bookworm-slim` (или distroless)
- Порт: `8081`
- Health: `GET /health` (замените, если другой endpoint)
### Пример Dockerfile
```
# stage: build
FROM dart:stable-sdk AS builder
WORKDIR /app
COPY pubspec.* ./
RUN dart pub get
COPY . .
RUN dart compile exe lib/main.dart -o /app/server
# stage: runtime
FROM debian:bookworm-slim
WORKDIR /app
COPY --from=builder /app/server /app/server
EXPOSE 8081
CMD ["/app/server", "-a", "0.0.0.0", "-p", "8081"]
```
### Env для внешней БД (БД остаётся на хосте)
- `DB_HOST` — IP хоста (обычно `172.17.0.1` или реальный публичный/приватный IP)
- `DB_PORT` — порт вашей БД
- `DB_NAME`, `DB_USER`, `DB_PASSWORD`
- Если нужен TLS к БД: добавить флаги клиента/сертификаты
### Настройка сети/Traefik
- В сервисе (compose) добавить:
```
networks:
- coolify
extra_hosts:
- "host.docker.internal:host-gateway"
```
- Traefik (через Coolify UI → Domains):
- Domain: `api.mnemo-cards.online`
- EntryPoints: `websecure`
- Cert resolver: `le`
- HTTP→HTTPS редирект уже есть через middleware redirectscheme
### Шаги в Coolify (UI)
1. Project → Environment → New Service → Dockerfile.
2. Указать репозиторий/ветку и путь к Dockerfile.
3. Env: `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` (+ ваши APP_*).
4. Network: подключить `coolify`.
5. Domains: `api.mnemo-cards.online`, entrypoint `websecure`, resolver `le`.
6. Healthcheck: HTTP 200 на `/health` (или ваш путь).
## 2. Web и Admin (Flutter Web) как статика в контейнере
### Сборка
```
flutter build web --release \
--dart-define=API_BASE_URL=https://api.mnemo-cards.online
```
Артефакт: `build/web/`.
### Пример Dockerfile (nginx)
```
FROM nginx:1.27-alpine
COPY build/web/ /usr/share/nginx/html/
RUN rm /etc/nginx/conf.d/default.conf
COPY default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
```
`default.conf`:
```
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /assets/ {
try_files $uri =404;
add_header Cache-Control "public, max-age=31536000, immutable";
}
}
```
### Разделение сервисов
- Web: домен `mnemo-cards.online`
- Admin: домен `admin.mnemo-cards.online`
Рекомендуется два сервиса в Coolify (два образа или один образ + разные домены).
### Шаги в Coolify
1. New Service → Dockerfile/Image (web).
2. Network: `coolify`.
3. Domains:
- web: `mnemo-cards.online` → entrypoint `websecure`, resolver `le`.
- admin: `admin.mnemo-cards.online` (отдельный сервис либо второй домен).
4. Healthcheck: HEAD `/` (200/301/302).
## 3. Traefik/LE (уже поднят)
- Порты 80/443 заняты Traefik.
- Все сервисы должны быть в сети `coolify`.
- Для каждого домена в Coolify задать entrypoint `websecure` и resolver `le`; HTTP→HTTPS редирект активен.
## 4. Порядок миграции/катовера
1) Собрать и запушить образы:
- backend (Dart) → registry.
- web/admin статика → отдельные образы.
2) Создать сервисы в Coolify (backend, web, admin) c env и доменами.
3) Протестировать:
- Временно через hosts или временный домен (Coolify supports additional domain).
- Проверить доступ к БД из контейнера (psql/curl).
4) Подключить боевые домены:
- `api.mnemo-cards.online`, `mnemo-cards.online`, `admin.mnemo-cards.online`.
5) Переключить трафик:
- Остановить legacy systemd backend и nginx, когда новые сервисы отвечают.
6) Мониторинг/откат:
- При проблемах: стартануть старый systemd/nginx (бэкап конфигов: `/root/pre-coolify-backup-2025-12-07.tar.gz`).
## 5. Проверки после миграции
- HTTPS: `curl -I https://api.mnemo-cards.online`, `https://mnemo-cards.online`, `https://admin.mnemo-cards.online` → 200/301/302, валидный LE.
- Health: все сервисы green в Coolify; backend `/health` 200.
- БД: приложение читает/пишет (прогнать базовый сценарий).
- Логи: нет ошибок подключения к БД/Redis.
- Сеть: наружу открыты только 80/443 (Traefik); внутренние порты не публиковать.
## 6. Что остаётся на хосте
- База данных остаётся на сервере; контейнеры подключаются как к внешнему ресурсу (`DB_HOST` = IP хоста).
- Traefik/LE уже работают в Docker, используется resolver `le`.

BIN
mnemo_cards.zip Normal file

Binary file not shown.

View file

@ -0,0 +1,40 @@
# Environment Variables для Coolify
## Обязательные переменные (опциональны, есть значения по умолчанию)
```
ISAR_DIR=isar
BACKUP_DIR=../mnemo_cards_telegram_bot/backups/
WORK_DIR=/root/mnemo_cards_backend
DEBUG=false
SERVER_ADDRESS=
PORT=3000
ADMIN_IDS=
```
## Описание переменных
- **ISAR_DIR** - Директория для базы данных Isar (по умолчанию: `isar`)
- **BACKUP_DIR** - Директория для хранения бэкапов (по умолчанию: `../mnemo_cards_telegram_bot/backups/`)
- **WORK_DIR** - Рабочая директория приложения (по умолчанию: `/root/mnemo_cards_backend`)
- **DEBUG** - Режим отладки Isar Inspector (`true` или `1` для включения, по умолчанию: `false`)
- **SERVER_ADDRESS** - IP адрес для привязки сервера (по умолчанию: `0.0.0.0` - все интерфейсы)
- **PORT** - Порт для HTTP сервера (по умолчанию: `3000`)
- **ADMIN_IDS** - Список Telegram ID админов, разделенных запятой (например: `123456789,987654321`). Если не задано, читается из файла `admins` (каждая строка - один ID)
## Примеры конфигурации
### Coolify (рекомендуется)
**Важно:** Coolify использует reverse proxy (Traefik/Caddy) для обработки SSL/TLS.
Приложение внутри контейнера работает только по HTTP.
```
PORT=3000
SERVER_ADDRESS=0.0.0.0
```
Coolify автоматически обрабатывает HTTPS на уровне reverse proxy.
### Разработка
```
PORT=8080
SERVER_ADDRESS=0.0.0.0
```

View file

@ -0,0 +1,112 @@
# Анализ безопасности админских endpoints авторизации
## Текущие публичные endpoints
1. `POST /api/v2/admin/auth/request-code` - генерация кода
2. `POST /api/v2/admin/auth/verify-code` - верификация кода и получение токена
3. `GET /api/v2/admin/auth/code-status/<code>` - проверка статуса кода
## Анализ безопасности
### ✅ Существующие защиты
1. **Двухфакторная аутентификация через Telegram**
- Код должен быть заявлен админом в Telegram боте
- Проверка, что `telegramUserId` находится в списке админов
- Без доступа к Telegram аккаунту админа невозможно получить доступ
2. **Ограничения кода**
- Код одноразовый (`isUsed`)
- Срок действия 5 минут
- Код генерируется случайно (100000-999999)
3. **Проверка прав доступа**
- Даже если код заявлен, проверяется, что пользователь имеет `admin=true` в БД
- Новые пользователи не получают админские права автоматически
### ⚠️ Потенциальные уязвимости
1. **Отсутствие rate limiting**
- Можно генерировать неограниченное количество кодов
- Можно делать множество попыток верификации (брутфорс)
- Можно часто проверять статус кодов
2. **Брутфорс кода**
- 6-значный код = 900,000 возможных комбинаций
- Без rate limiting можно перебрать все коды за несколько часов
- **НО**: код должен быть заявлен админом, что защищает от брутфорса
3. **Утечка информации через code-status**
- Можно проверять статус любых кодов
- Раскрывает информацию о существовании кодов
- Может помочь в брутфорсе (если знать, что код существует)
4. **Отсутствие защиты от перехвата**
- Код передается открыто (но это необходимо для UX)
- HTTPS должен использоваться обязательно
## Рекомендации по улучшению
### 1. Добавить Rate Limiting (КРИТИЧНО)
Создан файл `admin_auth_rate_limiter.dart` с реализацией:
- **Генерация кодов**: максимум 10 кодов в час с одного IP
- **Верификация**: максимум 20 попыток в час с одного IP
- **Проверка статуса**: максимум 30 запросов в минуту с одного IP
**Как применить:**
```dart
// В main.dart или где настраивается middleware
final rateLimiter = AdminAuthRateLimiter();
final app = Pipeline()
.addMiddleware(adminAuthRateLimit(rateLimiter))
.addMiddleware(/* другие middleware */)
.addHandler(router);
```
### 2. Усилить защиту от брутфорса
- Добавить задержку после неудачных попыток верификации
- Логировать все попытки верификации для мониторинга
- Блокировать IP после N неудачных попыток
### 3. Ограничить доступ к code-status
- Требовать минимальную авторизацию (например, по IP whitelist)
- Или использовать временный токен для проверки статуса
- Или ограничить проверку только для кодов, созданных с того же IP
### 4. Мониторинг и алертинг
- Логировать все попытки генерации кодов
- Логировать все попытки верификации (успешные и неуспешные)
- Отправлять алерты при подозрительной активности
### 5. Дополнительные меры
- Использовать CAPTCHA для генерации кодов (опционально)
- Добавить проверку User-Agent и других заголовков
- Использовать более длинные коды (8-10 цифр) для большей энтропии
## Оценка текущего уровня безопасности
**Текущий уровень: СРЕДНИЙ**
### Почему не КРИТИЧНО небезопасно:
1. **Основная защита работает**: код должен быть заявлен админом в Telegram
2. **Брутфорс неэффективен**: даже если перебрать все коды, они не будут заявлены админом
3. **Одноразовость**: использованный код нельзя использовать повторно
### Почему нужно улучшить:
1. **DoS атаки**: можно перегрузить сервер запросами
2. **Информационная утечка**: code-status раскрывает информацию
3. **Лучшие практики**: rate limiting - стандартная практика безопасности
## Вывод
**Текущая реализация достаточно безопасна для production**, но **настоятельно рекомендуется добавить rate limiting** для защиты от DoS атак и улучшения общей безопасности.
Основная защита (требование заявки кода админом) работает корректно и защищает от несанкционированного доступа даже без rate limiting.

View file

@ -110,6 +110,15 @@ Middleware authorizeV2(UserManager userManager, JwtService jwtService) {
if (isOptionalAuthGet) {
return await innerHandler(request);
}
// For admin endpoints, show what headers we received
if (isAdminEndpoint) {
final headerKeys = request.headers.keys.toList();
return Response(
401,
headers: {'Content-Type': 'application/json'},
body: '{"error":"Unauthorized","message":"Bearer token required","debug":"authHeader=${authHeader ?? 'null'}, headers=$headerKeys"}',
);
}
return Response(
401,
headers: {'Content-Type': 'application/json'},
@ -130,26 +139,33 @@ Middleware authorizeV2(UserManager userManager, JwtService jwtService) {
final userId = jwtService.verifyAccessToken(token, debug: isAdminPath);
if (userId == null) {
// Additional debug info for admin endpoints
// For admin endpoints, return detailed debug info in response
if (isAdminPath) {
print('[AUTH DEBUG] Token verification failed for path: $normalizedPath');
// Try to extract payload to see what's wrong
final verifyError = jwtService.lastVerificationError ?? 'Unknown';
String debugInfo = 'verifyError: $verifyError';
try {
final payload = jwtService.extractPayload(token);
print('[AUTH DEBUG] Extracted payload: $payload');
if (payload != null) {
final exp = payload['exp'] as int?;
final tokenType = payload['type'];
final tokenUserId = payload['userId'];
if (exp != null) {
final expiryTime = DateTime.fromMillisecondsSinceEpoch(exp * 1000);
print('[AUTH DEBUG] Token expires at: $expiryTime');
print('[AUTH DEBUG] Current time: ${DateTime.now()}');
print('[AUTH DEBUG] Is expired: ${DateTime.now().isAfter(expiryTime)}');
final now = DateTime.now();
debugInfo += ', payload: type=$tokenType, userId=$tokenUserId, '
'exp=$expiryTime, now=$now';
}
print('[AUTH DEBUG] Token type: ${payload['type']}');
}
} catch (e) {
print('[AUTH DEBUG] Could not extract payload: $e');
debugInfo += ', extractError: $e';
}
return Response(
401,
headers: {'Content-Type': 'application/json'},
body: '{"error":"Unauthorized","message":"Invalid or expired token","debug":"$debugInfo"}',
);
}
return Response(
401,

View file

@ -62,12 +62,16 @@ class JwtService {
);
}
// Store last verification failure reason for debugging
String? lastVerificationError;
/// Verify access token and return user ID
String? verifyAccessToken(String token, {bool debug = false}) {
lastVerificationError = null;
try {
final payload = _verifySimpleJwt(token, debug: debug);
if (payload == null) {
if (debug) print('[JWT DEBUG] _verifySimpleJwt returned null');
lastVerificationError = lastVerificationError ?? 'Signature verification failed';
return null;
}
@ -76,20 +80,20 @@ class JwtService {
if (exp != null) {
final expiryTime = DateTime.fromMillisecondsSinceEpoch(exp * 1000);
if (DateTime.now().isAfter(expiryTime)) {
if (debug) print('[JWT DEBUG] Token expired at $expiryTime, now: ${DateTime.now()}');
lastVerificationError = 'Token expired at $expiryTime';
return null;
}
}
// Check token type
if (payload['type'] != 'access') {
if (debug) print('[JWT DEBUG] Wrong token type: ${payload['type']}');
lastVerificationError = 'Wrong token type: ${payload['type']}';
return null;
}
return payload['userId']?.toString();
} catch (e) {
if (debug) print('[JWT DEBUG] Exception in verifyAccessToken: $e');
lastVerificationError = 'Exception: $e';
return null;
}
}
@ -150,7 +154,7 @@ class JwtService {
try {
final parts = token.split('.');
if (parts.length != 3) {
if (debug) print('[JWT DEBUG] Invalid token format: ${parts.length} parts');
lastVerificationError = 'Invalid token format: ${parts.length} parts';
return null;
}
@ -165,12 +169,8 @@ class JwtService {
final expectedSignatureB64 = base64UrlEncode(expectedSignature);
if (signatureB64 != expectedSignatureB64) {
if (debug) {
print('[JWT DEBUG] Signature mismatch!');
print('[JWT DEBUG] Got signature: $signatureB64');
print('[JWT DEBUG] Expected signature: $expectedSignatureB64');
print('[JWT DEBUG] Secret key length: ${_secretKey.length}');
}
lastVerificationError = 'Signature mismatch: got ${signatureB64.substring(0, 10)}..., '
'expected ${expectedSignatureB64.substring(0, 10)}...';
return null; // Invalid signature
}
@ -178,7 +178,7 @@ class JwtService {
final payloadJson = utf8.decode(base64Url.decode(payloadB64));
return jsonDecode(payloadJson) as Map<String, dynamic>;
} catch (e) {
if (debug) print('[JWT DEBUG] Exception during verification: $e');
lastVerificationError = 'Parse exception: $e';
return null;
}
}

View file

@ -0,0 +1,103 @@
# Миграция Telegram бота с Isar DB на Backend API
## Обзор
Telegram бот был мигрирован с прямого доступа к Isar базе данных на использование Backend API через HTTP запросы. Это обеспечивает:
- Централизованное управление данными
- Упрощение развертывания (не нужна локальная БД)
- Единую точку доступа к данным
## Изменения
### Бэкенд
1. **Middleware для API ключа** (`telegram_bot_auth_middleware.dart`)
- Проверяет заголовок `X-API-Key`
- Использует переменную окружения `TELEGRAM_BOT_API_KEY`
2. **API эндпоинты для бота** (`telegram_bot_api_v2.dart`)
- `GET /api/v2/telegram-bot/random-card` - получить случайную карточку
- `POST /api/v2/telegram-bot/share/check-limit` - проверить лимит шаринга
- `POST /api/v2/telegram-bot/share/record` - записать факт шаринга
- `GET /api/v2/telegram-bot/users/info` - информация о пользователях
- `GET /api/v2/telegram-bot/words` - список всех слов
### Телеграм бот
1. **HTTP клиент** (`backend_client.dart`)
- Заменяет `DBManager`
- Поддерживает retry логику с экспоненциальной задержкой
- Использует API ключ для аутентификации
2. **Удалены зависимости**
- Удалена зависимость `isar` из `pubspec.yaml`
- Удалена зависимость `args` из `pubspec.yaml` (CLI аргументы больше не используются)
- Удален файл `db_manager.dart`
- Удалена опция `--isar` из конфигурации
3. **Конфигурация через переменные окружения**
- CLI аргументы заменены на переменные окружения
- Все настройки теперь читаются из `Platform.environment`
- Добавлена переменная `TELEGRAM_BOT_TOKEN` для токена бота
## Настройка
### Переменные окружения
**Бэкенд:**
```bash
TELEGRAM_BOT_API_KEY=your-secret-api-key-here
```
**Бот (все обязательны):**
```bash
TELEGRAM_BOT_TOKEN=your-telegram-bot-token # Токен бота от BotFather
TELEGRAM_BOT_API_KEY=your-secret-api-key-here # Должен совпадать с бэкендом
BACKEND_URL=https://api.mnemo-cards.online # Опционально, по умолчанию используется значение по умолчанию
BOT_SHARE_DAILY_LIMIT=1 # Опционально, по умолчанию 1
```
**Примечание:** Бот теперь использует только переменные окружения. CLI аргументы больше не поддерживаются.
### Docker
В `Dockerfile` бота определены переменные окружения:
```dockerfile
ENV BACKEND_URL=https://api.mnemo-cards.online \
BOT_SHARE_DAILY_LIMIT=1 \
TELEGRAM_BOT_API_KEY= \
TELEGRAM_BOT_TOKEN=
```
Установите значения при запуске контейнера:
```bash
docker run \
-e TELEGRAM_BOT_TOKEN=your-bot-token \
-e TELEGRAM_BOT_API_KEY=your-api-key \
...
```
## Использование
Бот работает так же, как и раньше, но теперь все операции выполняются через API:
- `/start`, `/login`, `/code` - генерация кодов авторизации
- `/share` - шаринг карточек с проверкой лимитов
- `/info`, `/user`, `/words` - административные команды
Все данные теперь хранятся и обрабатываются на бэкенде.
## Безопасность
- API ключ передается через заголовок `X-API-Key`
- Ключ должен быть достаточно длинным и случайным
- Рекомендуется использовать переменные окружения, а не хардкодить ключ
- API ключ должен совпадать в бэкенде и боте
## Откат
Если нужно вернуться к использованию Isar:
1. Восстановите файл `db_manager.dart` из git истории
2. Верните зависимость `isar` в `pubspec.yaml`
3. Замените `BackendClient` на `DBManager` в `main.dart`
4. Обновите конфигурацию для использования пути к Isar БД

206
nginx_issues.md Normal file
View file

@ -0,0 +1,206 @@
Причина сейчас не в нагрузке, а в битой и дублирующейся конфигурации nginx. Лог прямым текстом говорит, почему он периодически падает / не стартует.
Разберём по кускам.
⸻
1. Старые ошибки с портами
Вот это:
bind() to 0.0.0.0:80 failed (98: Unknown error)
bind() to 0.0.0.0:443 failed (98: Unknown error)
still could not bind()
Это означает: кто-то уже слушает 80/443, а новый процесс nginx пытается занять те же порты и падает с EADDRINUSE.
Типичный сценарий:
• уже запущен nginx (через systemd),
• ты запускаешь ещё один (например nginx -c ... руками),
• второй не может забиндиться и падает.
Но это полбеды. Сейчас у тебя ещё серьёзнее: конфиг.
⸻
2. Главная проблема: конфиг nginx сломан/подключён неправильно
Вот основное:
"server" directive is not allowed here in /root/mnemo_cards/tools/deploy/generated_configs/code.mnemo-cards.online.conf:5
...
"location" directive is not allowed here in /etc/nginx/sites-enabled/code.mnemo-cards.online.conf:55
"gzip" directive is duplicate in /etc/nginx/sites-enabled/code.mnemo-cards.online.conf:53
"limit_req_zone" directive is not allowed here in /etc/nginx/sites-enabled/vscode.mnemo-cards.online.conf:24
Эти сообщения означают:
1. server в “не том месте”
Файлы из /root/mnemo_cards/tools/deploy/generated_configs/*.conf подключены так, будто это не server-блоки, а часть верхнего уровня.
• Директива server { ... } допустима только внутри блока http { ... }.
• Если ты где-то делаешь include /root/.../generated_configs/*.conf; ВНЕ http {}, nginx и ругается:
"server" directive is not allowed here
2. location в “не том месте”
Сообщения:
"location" directive is not allowed here in /etc/nginx/sites-enabled/code.mnemo-cards.online.conf:55
"location" directive is not allowed here in /etc/nginx/sites-enabled/mnemo-cards.online.conf:53
Значит в этих файлах:
• либо location стоит вне server {},
• либо у тебя “поехали” скобки } (какой-то server блок раньше закрылся слишком рано).
3. дублирующийся gzip и limit_req_zone не там
"gzip" directive is duplicate in /etc/nginx/sites-enabled/code.mnemo-cards.online.conf:53
"limit_req_zone" directive is not allowed here in /etc/nginx/sites-enabled/vscode.mnemo-cards.online.conf:24
• gzip обычно задаётся один раз на уровне http {}.
Ты, скорее всего, прописал его и в nginx.conf, и ещё раз внутри vhost.
• limit_req_zone нельзя писать внутри server или location, только на уровне http.
4. конфликты доменов
conflicting server name "vscode.mnemo-cards.online" on 0.0.0.0:80, ignored
...
ignored
Это “мягкая” проблема: у тебя несколько server-блоков с одинаковым server_name и listen 80/443.
nginx один использует, остальные игнорирует. Не фатально, но говорит, что конфиг дублируется (скорее всего: и в /etc/nginx/sites-enabled, и в /root/.../generated_configs).
Итог: nginx периодически не может прочитать конфиг и/или ты пытаешься стартовать его с альтернативным конфигом, где все эти server и location подключены в неправильном месте. Отсюда и “то работает, то нет”.
⸻
3. Почему “иногда работает, иногда нет”
Скорее всего:
• Есть один “старый” рабочий nginx, запущенный с нормальным /etc/nginx/nginx.conf.
• Ты запускаешь свою тулзу /root/mnemo_cards/tools/deploy/..., которая:
• генерирует конфиги в /root/mnemo_cards/tools/deploy/generated_configs/*.conf,
• пробует сделать nginx -s reload с другим config-path или включает эти файлы как главные.
• nginx читает новый конфиг → натыкается на server/ location not allowed here → reload не удаётся, в лучшем случае остаётся старый воркер, в худшем — nginx падает и systemd его мучительно перезапускает.
• В логе при этом сыпятся emerg и bind() failed, а снаружи ты видишь “иногда сайты отваливаются”.
⸻
4. Что сейчас является непосредственной причиной “опять не работает”
Прямо из хвоста лога:
2025/11/23 03:24:43 [emerg] ... "server" directive is not allowed here in /root/mnemo_cards/tools/deploy/generated_configs/...
2025/11/23 03:24:43 [warn] ... conflicting server name "vscode.mnemo-cards.online" ...
Т.е. на текущий момент nginx не может нормально перечитать конфиг из-за:
• включения /root/mnemo_cards/tools/deploy/generated_configs/*.conf не в том месте;
• раздвоения конфигов (одни в /etc/nginx/sites-enabled/*.conf, другие в /root/.../generated_configs).
⸻
5. Что делать, чтобы это прекратилось
Шаг 1. Перестать запускать nginx “как попало”
1. Остановить nginx:
systemctl stop nginx
ps aux | grep nginx
# убедиться, что живых процессов nginx не осталось
2. Дальше всегда управлять только так:
nginx -t # проверить конфиг
systemctl start nginx
systemctl reload nginx
Никаких nginx -c /root/... и прочего.
⸻
Шаг 2. Навести порядок в структуре конфига
Базовый скелет /etc/nginx/nginx.conf должен быть примерно таким:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 4096;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
# глобальные gzip / limit_req_zone / и т.д.
# gzip on;
# limit_req_zone ...;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*.conf;
}
Критично:
• server {} — только внутри блока http {};
• location — только внутри server {};
• limit_req_zone и подобные — только на уровне http {}, не внутри server.
⸻
Шаг 3. Разобраться с generated_configs
Сейчас файлы вида:
• /root/mnemo_cards/tools/deploy/generated_configs/code.mnemo-cards.online.conf
• /root/mnemo_cards/tools/deploy/generated_configs/mnemo-cards.online.conf
• /root/mnemo_cards/tools/deploy/generated_configs/vscode.mnemo-cards.online.conf
подключаются так, что nginx видит там server не в http контексте.
Тебе нужно одно из:
• либо перестать включать эти файлы напрямую (пусть тулза генерирует сразу в /etc/nginx/sites-available/sites-enabled),
• либо включать их изнутри http {}:
http {
...
include /root/mnemo_cards/tools/deploy/generated_configs/*.conf;
}
Но не одновременно с их копиями в /etc/nginx/sites-enabled/, иначе будешь получать конфликтующие server_name vscode.mnemo-cards.online.
Сделай так:
1. Реши, где “истина” для vhost’ов:
• либо /etc/nginx/sites-enabled/*.conf,
• либо /root/.../generated_configs/*.conf.
2. Второй вариант выключи:
• удаляешь/комментируешь include на ненужный путь;
• или временно переименовываешь файлы (например, .conf.disabled).
⸻
Шаг 4. Починить структуру самих vhost-файлов
Внутри каждого *.conf из sites-enabled должно быть:
server {
listen 80;
server_name vscode.mnemo-cards.online;
# тут уже location'ы
location / {
...
}
}
Никаких location вне server, никаких limit_req_zone/gzip внутри отдельных vhost’ов (если они уже есть глобально).
⸻
TL;DR
Причина, почему “опять не работает”, по логу такая:
nginx сейчас либо не может корректно перезапуститься, либо работает с битой конфигурацией, потому что ты одновременно используешь автогенерённые конфиги из /root/mnemo_cards/tools/deploy/generated_configs/*.conf и ручные в /etc/nginx/sites-enabled/*.conf, причём часть из них подключена в неправильный контекст (server и location “не там”). Плюс периодически пытаешься поднять второй nginx поверх первого, откуда bind() failed (98).