diff --git a/.cursor/rules/mnemo-cards-web.mdc b/.cursor/rules/mnemo-cards-web.mdc index e6250f9..e2a988b 100644 --- a/.cursor/rules/mnemo-cards-web.mdc +++ b/.cursor/rules/mnemo-cards-web.mdc @@ -1,5 +1,6 @@ --- alwaysApply: true --- -mnemo_cards_web_v2 is a web app. It should only run on web! Not mobile or macos. And tests should also only run on web! +mnemo_cards_web_v2 is a web app. It should only run on web! Not mobile or macos. And tests should also only run on web! It runs on a remote server. +Telegram bot also runs on a web server Актуальные доступы лежат в .access \ No newline at end of file diff --git a/.cursor/rules/write-tests.mdc b/.cursor/rules/write-tests.mdc index a319b5c..2bd28ed 100644 --- a/.cursor/rules/write-tests.mdc +++ b/.cursor/rules/write-tests.mdc @@ -3,6 +3,7 @@ alwaysApply: true --- Use yx_state and yx_scope, Follow clean architecture techique, +Modularize and split into files, You can use ../mnemo_cards app as a design and product features reference, but NEVER copy its architecture. When you finish with another step: Write unit tests for all the functionalities diff --git a/mnemo_cards_backend/isar/db.isar b/mnemo_cards_backend/isar/db.isar index 77bd147..1897306 100644 Binary files a/mnemo_cards_backend/isar/db.isar and b/mnemo_cards_backend/isar/db.isar differ diff --git a/mnemo_cards_backend/isar/db.isar.lock b/mnemo_cards_backend/isar/db.isar.lock index 557df06..f189490 100644 Binary files a/mnemo_cards_backend/isar/db.isar.lock and b/mnemo_cards_backend/isar/db.isar.lock differ diff --git a/mnemo_cards_telegram_bot/lib/bot_config.dart b/mnemo_cards_telegram_bot/lib/bot_config.dart index f02fa2c..51b01f5 100644 --- a/mnemo_cards_telegram_bot/lib/bot_config.dart +++ b/mnemo_cards_telegram_bot/lib/bot_config.dart @@ -23,7 +23,7 @@ class BotConfig { final String backendUrl; final int shareDailyLimit; - static const String defaultBackendUrl = 'http://localhost:8443'; + static const String defaultBackendUrl = 'https://api.mnemo-cards.online'; static const int defaultShareDailyLimit = 1; factory BotConfig.fromArgs( diff --git a/mnemo_cards_web_v2/build/unit_test_assets/NOTICES.Z b/mnemo_cards_web_v2/build/unit_test_assets/NOTICES.Z index 1b27d20..3ee5e1a 100644 Binary files a/mnemo_cards_web_v2/build/unit_test_assets/NOTICES.Z and b/mnemo_cards_web_v2/build/unit_test_assets/NOTICES.Z differ diff --git a/mnemo_cards_web_v2/build/web/flutter_bootstrap.js b/mnemo_cards_web_v2/build/web/flutter_bootstrap.js index d47555e..decb94a 100644 --- a/mnemo_cards_web_v2/build/web/flutter_bootstrap.js +++ b/mnemo_cards_web_v2/build/web/flutter_bootstrap.js @@ -38,6 +38,6 @@ _flutter.buildConfig = {"engineRevision":"d3d45dcf251823c1769909cd43698d126db38d _flutter.loader.load({ serviceWorkerSettings: { - serviceWorkerVersion: "1977728131" + serviceWorkerVersion: "2889484362" } }); diff --git a/mnemo_cards_web_v2/lib/domain/services/http_repository_v2.dart b/mnemo_cards_web_v2/lib/domain/services/http_repository_v2.dart index fd25fa3..acccf20 100644 --- a/mnemo_cards_web_v2/lib/domain/services/http_repository_v2.dart +++ b/mnemo_cards_web_v2/lib/domain/services/http_repository_v2.dart @@ -82,7 +82,7 @@ class HttpRepositoryV2 { } // Check dynamic paths (e.g., /auth/telegram/code-status/) - if (normalizedPath.startsWith('/auth/telegram/code-status/')) { + if (normalizedPath.startsWith('/auth/telegram/code-status/') || normalizedPath.startsWith('/auth/oauth/')) { return true; } diff --git a/mnemo_cards_web_v2/lib/presentation/pages/auth/auth_page.dart b/mnemo_cards_web_v2/lib/presentation/pages/auth/auth_page.dart index aaf1857..0b4502e 100644 --- a/mnemo_cards_web_v2/lib/presentation/pages/auth/auth_page.dart +++ b/mnemo_cards_web_v2/lib/presentation/pages/auth/auth_page.dart @@ -1,15 +1,13 @@ -import 'dart:async'; import 'dart:developer'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:go_router/go_router.dart'; -import 'package:url_launcher/url_launcher.dart'; import 'package:yx_scope_flutter/yx_scope_flutter.dart'; import 'package:mnemo_cards_web_v2/di/app_scope/app_scope_container.dart'; import 'package:mnemo_cards_web_v2/domain/config/api_config_v2.dart'; -import 'package:mnemo_cards_web_v2/domain/models/telegram_auth_code_status.dart'; +import 'package:mnemo_cards_web_v2/presentation/pages/auth/sign_in_with_google_button.dart'; +import 'package:mnemo_cards_web_v2/presentation/pages/auth/sign_in_with_telegram.dart'; /// Authentication page /// @@ -24,12 +22,6 @@ class AuthPage extends StatefulWidget { class _AuthPageState extends State { bool _isLoading = false; String? _errorMessage; - final _telegramCodeController = TextEditingController(); - TelegramAuthCodeStatus? _webCodeStatus; - Timer? _codeStatusTimer; - Timer? _countdownTimer; - DateTime? _codeExpiryTime; - bool _autoLoginAttempted = false; Future _loginWithGoogle(BuildContext context) async { setState(() { @@ -93,454 +85,15 @@ class _AuthPageState extends State { } } - Future _loginWithTelegram(BuildContext context) async { - final code = _telegramCodeController.text.trim(); - - if (code.isEmpty) { - setState(() { - _errorMessage = 'Введите код авторизации'; - }); - return; - } - - setState(() { - _isLoading = true; - _errorMessage = null; - }); - + Future _onTelegramLoginSuccess(BuildContext context) async { // Get router before async operations to avoid context issues final router = GoRouter.of(context); - - try { - final appScope = ScopeProvider.of( - context, - listen: false, - ); - - if (appScope == null) { - throw Exception('AppScope not available'); - } - - // Login via Telegram with code - final user = await appScope.authService.loginWithTelegram(code); - - if (!mounted) return; - - if (user != null) { - log('Telegram login successful: ${user.email}', name: 'AuthPage'); - - _stopCodePolling(); - _countdownTimer?.cancel(); - _countdownTimer = null; - setState(() { - _webCodeStatus = _webCodeStatus?.copyWith( - state: TelegramAuthCodeState.used, - remainingSeconds: 0, - isUsed: true, - isClaimed: true, - expiresAt: DateTime.now(), - ); - }); - - // Clear code field - _telegramCodeController.clear(); - - // Create UserScope if it doesn't exist - if (appScope.userScopeHolder.scope == null) { - await appScope.userScopeHolder.create(); - } - - // Update user state - appScope.userScopeHolder.scope!.userStateManager.setUser(user); - - // Notify that UserScope has changed - appScope.notifyUserScopeChanged(); - - // Notify router about auth change - appScope.userScopeHolder.notifyAuthChanged(); - - // Navigate to home router.go('/home'); - } else { - setState(() { - _errorMessage = 'Неверный код авторизации'; - }); - } - } catch (e, s) { - log('Error in Telegram login', error: e, stackTrace: s, name: 'AuthPage'); - if (mounted) { - setState(() { - _errorMessage = 'Ошибка авторизации: ${e.toString()}'; - }); - } - } finally { - if (mounted) { - setState(() => _isLoading = false); - } - } } - Future _generateTelegramCode(BuildContext context) async { - setState(() { - _isLoading = true; - _errorMessage = null; - }); - - try { - final appScope = ScopeProvider.of( - context, - listen: false, - ); - - if (appScope == null) { - throw Exception('AppScope not available'); - } - - final status = await appScope.authService.createTelegramWebCode(); - if (!mounted) return; - - _autoLoginAttempted = false; - _startCountdownTimer(status); - - setState(() { - _webCodeStatus = status; - _telegramCodeController.text = status.code; - }); - - // Copy code to clipboard for convenience - await Clipboard.setData(ClipboardData(text: status.code)); - - if (mounted) { - _startCodePolling(context, status.code); - // Automatically open Telegram bot with the code - await _openTelegramBotWithCode(status.code); - } - } catch (e, s) { - log( - 'Error generating Telegram code', - error: e, - stackTrace: s, - name: 'AuthPage', - ); - if (mounted) { - setState(() { - _errorMessage = 'Не удалось получить код. Попробуйте ещё раз.'; - }); - } - } finally { - if (mounted) { - setState(() => _isLoading = false); - } - } - } - - void _startCodePolling(BuildContext context, String code) { - _stopCodePolling(); - // Trigger immediate status check - unawaited(_refreshCodeStatus(context, code)); - _codeStatusTimer = Timer.periodic( - const Duration(seconds: 3), - (_) => _refreshCodeStatus(context, code), - ); - } - - void _stopCodePolling() { - _codeStatusTimer?.cancel(); - _codeStatusTimer = null; - } - - Future _refreshCodeStatus(BuildContext context, String code) async { - try { - final appScope = ScopeProvider.of( - context, - listen: false, - ); - if (appScope == null) { - return; - } - - final status = await appScope.authService.getTelegramCodeStatus(code); - if (!mounted) return; - - _startCountdownTimer(status); - - setState(() { - _webCodeStatus = status; - }); - - if (status.isExpired) { - _stopCodePolling(); - if (mounted) { - setState(() { - _errorMessage = 'Срок действия кода истёк. Сгенерируйте новый.'; - }); - } - } else if (status.isConsumed) { - _stopCodePolling(); - } else if (status.isReadyForLogin && !_autoLoginAttempted && mounted) { - _autoLoginAttempted = true; - await _loginWithTelegram(context); - } - } catch (e, s) { - log( - 'Failed to refresh Telegram code status', - error: e, - stackTrace: s, - name: 'AuthPage', - ); - } - } - - void _startCountdownTimer(TelegramAuthCodeStatus status) { - final now = DateTime.now(); - final expiresAt = - status.expiresAt ?? now.add(Duration(seconds: status.remainingSeconds)); - _codeExpiryTime = expiresAt; - - _countdownTimer?.cancel(); - - if (status.isExpired || status.isConsumed) { - _countdownTimer = null; - return; - } - - _countdownTimer = Timer.periodic(const Duration(seconds: 1), (_) { - if (!mounted) { - _countdownTimer?.cancel(); - return; - } - if (_codeExpiryTime != null && DateTime.now().isAfter(_codeExpiryTime!)) { - _countdownTimer?.cancel(); - setState(() {}); - } else { - setState(() {}); - } - }); - } - - String _formatRemaining() { - final expiresAt = _codeExpiryTime; - if (expiresAt == null) { - return '00:00'; - } - final remaining = expiresAt.difference(DateTime.now()); - if (remaining.isNegative) { - return '00:00'; - } - final minutes = remaining.inMinutes; - final seconds = remaining.inSeconds % 60; - return '${minutes.toString().padLeft(2, '0')}:' - '${seconds.toString().padLeft(2, '0')}'; - } - - Widget _buildTelegramStatusCard(BuildContext context) { - final status = _webCodeStatus!; - final theme = Theme.of(context); - - IconData icon; - String statusLabel; - Color iconColor; - - switch (status.state) { - case TelegramAuthCodeState.claimed: - icon = Icons.verified; - statusLabel = 'Код подтверждён'; - iconColor = theme.colorScheme.secondary; - break; - case TelegramAuthCodeState.used: - icon = Icons.done_all; - statusLabel = 'Код использован'; - iconColor = theme.colorScheme.secondary; - break; - case TelegramAuthCodeState.expired: - icon = Icons.timer_off; - statusLabel = 'Код истёк'; - iconColor = Colors.red; - break; - case TelegramAuthCodeState.pending: - icon = Icons.hourglass_top; - statusLabel = 'Ожидаем отправку кода в боте'; - iconColor = theme.colorScheme.onSurface; - break; - } - - final isActive = !status.isExpired && !status.isConsumed; - - return Card( - elevation: 2, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - icon, - color: iconColor, - size: 20, - ), - const SizedBox(width: 8), - Expanded( - child: Text( - statusLabel, - style: theme.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w700, - color: theme.colorScheme.onSurface, - ), - ), - ), - ], - ), - const SizedBox(height: 12), - SelectableText( - 'Код: ${status.code}', - style: theme.textTheme.bodyLarge?.copyWith( - fontFamily: 'monospace', - fontWeight: FontWeight.w700, - color: theme.colorScheme.onSurface, - ), - ), - const SizedBox(height: 8), - Text( - isActive - ? 'Осталось времени: ${_formatRemaining()}' - : 'Код больше не активен', - style: theme.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w700, - color: theme.colorScheme.onSurfaceVariant, - ), - ), - if (status.isReadyForLogin) - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Text( - 'Код отправлен. Нажмите «Войти» для завершения.', - style: theme.textTheme.bodySmall?.copyWith( - fontWeight: FontWeight.w700, - color: theme.colorScheme.onSurfaceVariant, - ), - ), - ) - else if (status.isExpired) - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Text( - 'Получите новый код и повторите попытку.', - style: theme.textTheme.bodySmall?.copyWith( - fontWeight: FontWeight.w700, - color: theme.colorScheme.onSurfaceVariant, - ), - ), - ) - else if (status.isConsumed) - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Text( - 'Код уже использован для входа.', - style: theme.textTheme.bodySmall?.copyWith( - fontWeight: FontWeight.w700, - color: theme.colorScheme.onSurfaceVariant, - ), - ), - ) - else - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Text( - 'Бот должен открыться автоматически. Отправьте код в боте, чтобы продолжить.', - style: theme.textTheme.bodySmall?.copyWith( - fontWeight: FontWeight.w700, - color: theme.colorScheme.onSurfaceVariant, - ), - ), - ), - ], - ), - ), - ); - } - - /// Open Telegram bot with a specific code in the start parameter - Future _openTelegramBotWithCode(String code) async { - if (mounted && _errorMessage != null) { - setState(() { - _errorMessage = null; - }); - } - - // Format: https://t.me/bot_username?start=code - // Bot extracts 6-digit code from payload using regex (\d{6})$ - final deepLink = Uri.parse( - ApiConfigV2.telegramBotDeepLink(code), - ); - - try { - final launched = await launchUrl( - deepLink, - mode: LaunchMode.externalApplication, - ); - if (!launched) { - throw Exception('Could not launch Telegram'); - } - } catch (e, s) { - log( - 'Error opening Telegram bot', - error: e, - stackTrace: s, - name: 'AuthPage', - ); - if (mounted) { - setState(() { - _errorMessage = - 'Не удалось открыть Telegram. Попробуйте вручную: ${deepLink.toString()}'; - }); - } - } - } - - /// Open Telegram bot without code (base link) - Future _openTelegramBot() async { - if (mounted && _errorMessage != null) { - setState(() { - _errorMessage = null; - }); - } - - final deepLink = Uri.parse(ApiConfigV2.telegramBotDeepLinkBase); - - try { - final launched = await launchUrl( - deepLink, - mode: LaunchMode.externalApplication, - ); - if (!launched) { - throw Exception('Could not launch Telegram'); - } - } catch (e, s) { - log( - 'Error opening Telegram bot', - error: e, - stackTrace: s, - name: 'AuthPage', - ); - if (mounted) { - setState(() { - _errorMessage = - 'Не удалось открыть Telegram. Попробуйте вручную: ${deepLink.toString()}'; - }); - } - } - } @override void dispose() { - _codeStatusTimer?.cancel(); - _countdownTimer?.cancel(); - _telegramCodeController.dispose(); super.dispose(); } @@ -643,35 +196,9 @@ class _AuthPageState extends State { ), ], // Login buttons - SizedBox( - width: double.infinity, - height: 56, - child: ElevatedButton.icon( - onPressed: _isLoading - ? null - : () => _loginWithGoogle(context), - icon: _isLoading - ? SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator( - strokeWidth: 2, - color: theme.colorScheme.onPrimary, - ), - ) - : const Icon(Icons.g_mobiledata, size: 28), - label: Text( - _isLoading ? 'Signing in...' : 'Sign in with Google', - style: theme.textTheme.labelLarge?.copyWith( - fontWeight: FontWeight.w700, - ), - ), - style: ElevatedButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - ), - ), + SignInWithGoogleButton( + onPressed: () => _loginWithGoogle(context), + isLoading: _isLoading, ), const SizedBox(height: 24), // Divider @@ -703,154 +230,10 @@ class _AuthPageState extends State { ), const SizedBox(height: 24), // Telegram auth code input - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Введите код из Telegram бота', - style: theme.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w700, - color: theme.colorScheme.onSurface, - ), - ), - const SizedBox(height: 12), - TextField( - controller: _telegramCodeController, - enabled: !_isLoading, - style: theme.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w700, - color: theme.colorScheme.onSurface, - ), - decoration: InputDecoration( - hintText: '123456', - hintStyle: theme.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w700, - color: theme.hintColor, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide( - color: theme.dividerColor, - width: 1, - ), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide( - color: theme.dividerColor, - width: 1, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide( - color: theme.colorScheme.primary, - width: 2, - ), - ), - filled: true, - fillColor: theme.cardColor, - prefixIcon: Icon( - Icons.code, - color: theme.colorScheme.onSurface, - ), - counterText: '', - ), - keyboardType: TextInputType.number, - textInputAction: TextInputAction.done, - maxLength: 6, - onSubmitted: (_) => _loginWithTelegram(context), - ), - const SizedBox(height: 16), - Row( - children: [ - Expanded( - child: ElevatedButton.icon( - onPressed: _isLoading - ? null - : () => _generateTelegramCode(context), - icon: const Icon(Icons.bolt, size: 20), - label: Text( - _isLoading ? 'Создание...' : 'Получить код', - style: theme.textTheme.labelLarge?.copyWith( - fontWeight: FontWeight.w700, - ), - ), - style: ElevatedButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - ), - ), - ), - const SizedBox(width: 12), - Expanded( - child: OutlinedButton.icon( - onPressed: _isLoading ? null : _openTelegramBot, - icon: const Icon(Icons.telegram, size: 20), - label: Text( - 'Открыть бота', - style: theme.textTheme.labelLarge?.copyWith( - fontWeight: FontWeight.w700, - ), - ), - style: OutlinedButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - side: BorderSide( - color: theme.dividerColor, - width: 1, - ), - ), - ), - ), - ], - ), - if (_webCodeStatus != null) ...[ - const SizedBox(height: 16), - _buildTelegramStatusCard(context), - ], - ], - ), - const SizedBox(height: 24), - SizedBox( - width: double.infinity, - height: 56, - child: ElevatedButton.icon( - onPressed: _isLoading - ? null - : () => _loginWithTelegram(context), - icon: const Icon(Icons.telegram, size: 24), - label: Text( - 'Войти с кодом Telegram', - style: theme.textTheme.labelLarge?.copyWith( - fontWeight: FontWeight.w700, - ), - ), - style: ElevatedButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - ), - ), - ), - const SizedBox(height: 24), - TextButton( - onPressed: _isLoading ? null : () => context.go('/home'), - style: TextButton.styleFrom( - padding: const EdgeInsets.symmetric( - horizontal: 24, - vertical: 16, - ), - ), - child: Text( - 'Continue as guest', - style: theme.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w700, - color: theme.colorScheme.onSurfaceVariant, - ), - ), + SignInWithTelegram( + onLoginSuccess: () => _onTelegramLoginSuccess(context), + onError: (error) => setState(() => _errorMessage = error), + isLoading: _isLoading, ), const SizedBox(height: 16), Text( diff --git a/mnemo_cards_web_v2/pubspec.lock b/mnemo_cards_web_v2/pubspec.lock index 7137173..9905c61 100644 --- a/mnemo_cards_web_v2/pubspec.lock +++ b/mnemo_cards_web_v2/pubspec.lock @@ -533,54 +533,6 @@ packages: url: "https://pub.dev" source: hosted version: "5.9.3" - flutter_secure_storage: - dependency: transitive - description: - name: flutter_secure_storage - sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" - url: "https://pub.dev" - source: hosted - version: "9.2.4" - flutter_secure_storage_linux: - dependency: transitive - description: - name: flutter_secure_storage_linux - sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 - url: "https://pub.dev" - source: hosted - version: "1.2.3" - flutter_secure_storage_macos: - dependency: transitive - description: - name: flutter_secure_storage_macos - sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" - url: "https://pub.dev" - source: hosted - version: "3.1.3" - flutter_secure_storage_platform_interface: - dependency: transitive - description: - name: flutter_secure_storage_platform_interface - sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 - url: "https://pub.dev" - source: hosted - version: "1.1.2" - flutter_secure_storage_web: - dependency: transitive - description: - name: flutter_secure_storage_web - sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - flutter_secure_storage_windows: - dependency: transitive - description: - name: flutter_secure_storage_windows - sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 - url: "https://pub.dev" - source: hosted - version: "3.1.2" flutter_svg: dependency: transitive description: diff --git a/tools/deploy/backend-build_app_webroot.sh b/tools/deploy/backend-build_app_webroot.sh index 25c8d14..e8bb816 100755 --- a/tools/deploy/backend-build_app_webroot.sh +++ b/tools/deploy/backend-build_app_webroot.sh @@ -55,17 +55,7 @@ server { add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; - # CORS headers for API - add_header Access-Control-Allow-Origin "*" always; - add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS" always; - add_header Access-Control-Allow-Headers "Origin, Content-Type, Accept, Authorization, user_token, request_token, app_version" always; - add_header Access-Control-Expose-Headers "Authorization" always; - add_header Access-Control-Max-Age "86400" always; - - # Handle preflight OPTIONS requests - if ($request_method = OPTIONS) { - return 204; - } + # CORS headers are handled by the backend server, nginx only proxies # Proxy to HTTP backend (SSL termination in nginx) location / { diff --git a/tools/deploy/generate-nginx-configs.sh b/tools/deploy/generate-nginx-configs.sh index 384eb04..058cd6c 100755 --- a/tools/deploy/generate-nginx-configs.sh +++ b/tools/deploy/generate-nginx-configs.sh @@ -169,22 +169,11 @@ EOF # API Backend specific settings client_max_body_size 100M; - # CORS headers for API - add_header Access-Control-Allow-Origin "*" always; - add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS" always; - add_header Access-Control-Allow-Headers "Origin, Content-Type, Accept, Authorization, user_token, request_token, app_version" always; - add_header Access-Control-Expose-Headers "Authorization" always; - add_header Access-Control-Max-Age "86400" always; + # CORS headers are handled by the backend server, nginx only proxies - # Handle preflight OPTIONS requests - if (\$request_method = OPTIONS) { - return 204; - } - - # Proxy to backend server (HTTPS for API, HTTP for others) + # Proxy to backend server (HTTP for API - backend runs in dual mode) location / { - proxy_pass https://127.0.0.1:$upstream_port; - proxy_ssl_verify off; # Skip SSL verification for local connection + proxy_pass http://127.0.0.1:$upstream_port; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; diff --git a/tools/deploy/generated_configs/api.mnemo-cards.online.conf b/tools/deploy/generated_configs/api.mnemo-cards.online.conf index 23fab93..53767db 100644 --- a/tools/deploy/generated_configs/api.mnemo-cards.online.conf +++ b/tools/deploy/generated_configs/api.mnemo-cards.online.conf @@ -1,5 +1,5 @@ # Nginx configuration for api.mnemo-cards.online -# Generated by generate-nginx-configs.sh on Thu Nov 27 22:14:27 MSK 2025 +# Generated by generate-nginx-configs.sh on Thu Nov 27 22:41:03 MSK 2025 # Service: api server { @@ -28,22 +28,11 @@ server { # API Backend specific settings client_max_body_size 100M; - # CORS headers for API - add_header Access-Control-Allow-Origin "*" always; - add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS" always; - add_header Access-Control-Allow-Headers "Origin, Content-Type, Accept, Authorization, user_token, request_token, app_version" always; - add_header Access-Control-Expose-Headers "Authorization" always; - add_header Access-Control-Max-Age "86400" always; + # CORS headers are handled by the backend server, nginx only proxies - # Handle preflight OPTIONS requests - if ($request_method = OPTIONS) { - return 204; - } - - # Proxy to backend server (HTTPS for API, HTTP for others) + # Proxy to backend server (HTTP for API - backend runs in dual mode) location / { - proxy_pass https://127.0.0.1:8081; - proxy_ssl_verify off; # Skip SSL verification for local connection + proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/tools/deploy/generated_configs/code.mnemo-cards.online.conf b/tools/deploy/generated_configs/code.mnemo-cards.online.conf index c048fb5..570b4d0 100644 --- a/tools/deploy/generated_configs/code.mnemo-cards.online.conf +++ b/tools/deploy/generated_configs/code.mnemo-cards.online.conf @@ -1,5 +1,5 @@ # Nginx configuration for code.mnemo-cards.online -# Generated by generate-nginx-configs.sh on Thu Nov 27 22:14:27 MSK 2025 +# Generated by generate-nginx-configs.sh on Thu Nov 27 22:41:03 MSK 2025 # Service: forgejo server { diff --git a/tools/deploy/generated_configs/mnemo-cards.online.conf b/tools/deploy/generated_configs/mnemo-cards.online.conf index c6491a9..7b1b5ee 100644 --- a/tools/deploy/generated_configs/mnemo-cards.online.conf +++ b/tools/deploy/generated_configs/mnemo-cards.online.conf @@ -1,5 +1,5 @@ # Nginx configuration for mnemo-cards.online -# Generated by generate-nginx-configs.sh on Thu Nov 27 22:14:27 MSK 2025 +# Generated by generate-nginx-configs.sh on Thu Nov 27 22:41:03 MSK 2025 # Service: webapp server { diff --git a/tools/deploy/generated_configs/vscode.mnemo-cards.online.conf b/tools/deploy/generated_configs/vscode.mnemo-cards.online.conf index eb79f7d..502e62b 100644 --- a/tools/deploy/generated_configs/vscode.mnemo-cards.online.conf +++ b/tools/deploy/generated_configs/vscode.mnemo-cards.online.conf @@ -1,5 +1,5 @@ # Nginx configuration for vscode.mnemo-cards.online -# Generated by generate-nginx-configs.sh on Thu Nov 27 22:14:27 MSK 2025 +# Generated by generate-nginx-configs.sh on Thu Nov 27 22:41:03 MSK 2025 # Service: vscode server {