From 83b776c00e60a136ae98cf3eba5ad54c5d16289d Mon Sep 17 00:00:00 2001 From: Dmitry Date: Sat, 22 Nov 2025 22:06:27 +0300 Subject: [PATCH] stuff --- .../lib/packs/pack_dto_converter.dart | 7 +-- mnemo_cards_web_v2/deploy/vscode-nginx.conf | 43 ++++++++++++++++++- mnemo_cards_web_v2/lib/app.dart | 2 + .../lib/di/user_scope/user_scope_holder.dart | 41 +++++++++++++++++- .../presentation/pages/auth/auth_page.dart | 6 +++ .../pages/profile/profile_page.dart | 13 +++++- .../lib/presentation/router/app_router.dart | 24 +++++++++++ mnemo_cards_web_v2/pubspec.yaml | 5 +-- tools/deploy/VSCODE_SERVER_SETUP.md | 32 +++++++++++--- 9 files changed, 156 insertions(+), 17 deletions(-) diff --git a/mnemo_cards_backend/lib/packs/pack_dto_converter.dart b/mnemo_cards_backend/lib/packs/pack_dto_converter.dart index 81f4eff..03f4637 100644 --- a/mnemo_cards_backend/lib/packs/pack_dto_converter.dart +++ b/mnemo_cards_backend/lib/packs/pack_dto_converter.dart @@ -54,11 +54,12 @@ class PackDtoConverter { tip: available ? null : canOpenForAdVal - ? TextPackTip( - '📺', + ? AssetPackTip( + 'asset:icons/ad.webp', position: PackTipPosition.bottomRight, ) - : TextPackTip('🔒'), + : AssetPackTip('asset:icons/lock.webp', + position: PackTipPosition.bottomRight,), version: model.version, ); } diff --git a/mnemo_cards_web_v2/deploy/vscode-nginx.conf b/mnemo_cards_web_v2/deploy/vscode-nginx.conf index 54284dc..73814ae 100644 --- a/mnemo_cards_web_v2/deploy/vscode-nginx.conf +++ b/mnemo_cards_web_v2/deploy/vscode-nginx.conf @@ -34,6 +34,44 @@ server { add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + # Static files - no rate limiting (VSCode loads many files simultaneously) + location ~ ^/(static|out|node_modules|_next)/ { + # No rate limiting for static files + proxy_pass http://127.0.0.1:8443; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Cache static files + proxy_cache_valid 200 1h; + add_header Cache-Control "public, max-age=3600"; + + # Timeout settings + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # Static file extensions - no rate limiting + location ~ \.(js|css|woff|woff2|ttf|eot|png|jpg|jpeg|gif|svg|ico|webp|map|json)$ { + # No rate limiting for static file extensions + proxy_pass http://127.0.0.1:8443; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Cache static files + proxy_cache_valid 200 1h; + add_header Cache-Control "public, max-age=3600"; + + # Timeout settings + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + # Special rate limiting for login attempts location /login { # Strict rate limiting for login @@ -62,9 +100,10 @@ server { } # Proxy to code-server running on port 8443 + # Increased rate limit for API and dynamic content location / { - # Apply rate limiting - limit_req zone=vscode_general burst=10 nodelay; + # Increased rate limiting (200 requests per minute with burst of 50) + limit_req zone=vscode_general burst=50 nodelay; limit_req_status 429; proxy_pass http://127.0.0.1:8443; diff --git a/mnemo_cards_web_v2/lib/app.dart b/mnemo_cards_web_v2/lib/app.dart index 57a163e..ebcb1f7 100644 --- a/mnemo_cards_web_v2/lib/app.dart +++ b/mnemo_cards_web_v2/lib/app.dart @@ -94,6 +94,8 @@ class _AppInitializerState extends State<_AppInitializer> { log('Auto-login successful, creating UserScope', name: 'App'); // Create UserScope only for authenticated users widget.appScope.userScopeHolder.scope!.userStateManager.setUser(user); + // Notify router about auth change + widget.appScope.userScopeHolder.notifyAuthChanged(); log('UserScope created and user set', name: 'App'); } else { log('No saved session, starting as guest without UserScope', name: 'App'); diff --git a/mnemo_cards_web_v2/lib/di/user_scope/user_scope_holder.dart b/mnemo_cards_web_v2/lib/di/user_scope/user_scope_holder.dart index 6f349a5..db24bf8 100644 --- a/mnemo_cards_web_v2/lib/di/user_scope/user_scope_holder.dart +++ b/mnemo_cards_web_v2/lib/di/user_scope/user_scope_holder.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:yx_scope/yx_scope.dart'; import 'user_scope.dart'; @@ -8,11 +9,49 @@ import 'user_scope_container.dart'; /// Управляет жизненным циклом UserScope class UserScopeHolder extends BaseChildScopeHolder { - UserScopeHolder(super.parent); + UserScopeHolder(super.parent) : _authNotifier = ValueNotifier(false) { + // Подписываемся на изменения состояния scope + listen((scope) { + _updateAuthNotifier(); + }, emitImmediately: true); + } + + final ValueNotifier _authNotifier; + + /// Notifier для отслеживания изменений состояния авторизации + /// + /// Используется для обновления роутера при изменении авторизации + ValueNotifier get authNotifier => _authNotifier; @override UserScopeContainer createContainer(UserScopeParent parent) { return UserScopeContainer(parent: parent); } + + void _updateAuthNotifier() { + final isAuth = isAuthenticated; + if (_authNotifier.value != isAuth) { + _authNotifier.value = isAuth; + } + } + + /// Проверяет, авторизован ли пользователь + /// + /// Возвращает true, если UserScope существует и пользователь авторизован + bool get isAuthenticated { + final userScope = scope; + if (userScope == null) { + return false; + } + return userScope.userStateManager.isAuthenticated; + } + + /// Уведомляет о изменении состояния авторизации + /// + /// Вызывается после изменения состояния пользователя (логин/логаут) + /// для немедленного обновления роутера + void notifyAuthChanged() { + _updateAuthNotifier(); + } } 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 bdad253..b7b1f7a 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 @@ -72,6 +72,9 @@ class _AuthPageState extends State { // Notify that UserScope has changed appScope.notifyUserScopeChanged(); + // Notify router about auth change + appScope.userScopeHolder.notifyAuthChanged(); + // Navigate to home router.go('/home'); } else { @@ -156,6 +159,9 @@ class _AuthPageState extends State { // Notify that UserScope has changed appScope.notifyUserScopeChanged(); + // Notify router about auth change + appScope.userScopeHolder.notifyAuthChanged(); + // Navigate to home router.go('/home'); } else { diff --git a/mnemo_cards_web_v2/lib/presentation/pages/profile/profile_page.dart b/mnemo_cards_web_v2/lib/presentation/pages/profile/profile_page.dart index ea54818..543dea3 100644 --- a/mnemo_cards_web_v2/lib/presentation/pages/profile/profile_page.dart +++ b/mnemo_cards_web_v2/lib/presentation/pages/profile/profile_page.dart @@ -46,13 +46,24 @@ class _ProfilePageState extends State { if (!mounted) return; - // UserScope will be automatically disposed when the holder is destroyed + // Update user state to guest + final userScope = appScope.userScopeHolder.scope; + if (userScope != null) { + userScope.userStateManager.logout(); + } // Notify that UserScope has changed appScope.notifyUserScopeChanged(); + // Notify router about auth change + appScope.userScopeHolder.notifyAuthChanged(); + log('User logged out successfully', name: 'ProfilePage'); + // Navigate to auth page + final router = GoRouter.of(context); + router.go('/auth'); + // Show success message messenger.showSnackBar( const SnackBar(content: Text('Logged out successfully')), diff --git a/mnemo_cards_web_v2/lib/presentation/router/app_router.dart b/mnemo_cards_web_v2/lib/presentation/router/app_router.dart index 1a5669a..36b1c85 100644 --- a/mnemo_cards_web_v2/lib/presentation/router/app_router.dart +++ b/mnemo_cards_web_v2/lib/presentation/router/app_router.dart @@ -20,6 +20,30 @@ GoRouter createAppRouter({ }) { return GoRouter( initialLocation: '/home', + refreshListenable: userScopeHolder.authNotifier, + redirect: (context, state) { + final isAuthenticated = userScopeHolder.isAuthenticated; + final location = state.uri.path; + + // Если пользователь не авторизован + if (!isAuthenticated) { + // Разрешаем доступ только к странице авторизации + if (location == '/auth') { + return null; // Разрешить доступ + } + // Перенаправляем на страницу авторизации + return '/auth'; + } + + // Если пользователь авторизован и пытается зайти на /auth + if (location == '/auth') { + // Перенаправляем на главную страницу + return '/home'; + } + + // Разрешаем доступ к остальным страницам + return null; + }, routes: [ // Главный Shell с Bottom Navigation ShellRoute( diff --git a/mnemo_cards_web_v2/pubspec.yaml b/mnemo_cards_web_v2/pubspec.yaml index 284ff87..c8ac6ac 100644 --- a/mnemo_cards_web_v2/pubspec.yaml +++ b/mnemo_cards_web_v2/pubspec.yaml @@ -102,9 +102,8 @@ dev_dependencies: flutter: uses-material-design: true - # assets: - # - assets/images/ - # - assets/icons/ + assets: + - icons/ fonts: - family: Nunito diff --git a/tools/deploy/VSCODE_SERVER_SETUP.md b/tools/deploy/VSCODE_SERVER_SETUP.md index 3502a98..4521b49 100644 --- a/tools/deploy/VSCODE_SERVER_SETUP.md +++ b/tools/deploy/VSCODE_SERVER_SETUP.md @@ -68,11 +68,12 @@ Deployed proper nginx configuration for vscode.mnemo-cards.online: Created the following scripts in `tools/deploy/`: 1. **fix-vscode-server.sh** - Diagnoses and fixes code-server installation and configuration -2. **check-nginx.sh** - Checks nginx status and reloads configuration -3. **deploy-vscode-nginx.sh** - Deploys VSCode nginx configuration to server -4. **fix-nginx-duplicates.sh** - Removes duplicate nginx configurations -5. **test-vscode-from-server.sh** - Tests VSCode connectivity from server -6. **test-vscode-final.sh** - Final tests of both URLs +2. **fix-vscode-rate-limit.sh** - Fixes 429 errors by updating rate limits in nginx.conf +3. **check-nginx.sh** - Checks nginx status and reloads configuration +4. **deploy-vscode-nginx.sh** - Deploys VSCode nginx configuration to server +5. **fix-nginx-duplicates.sh** - Removes duplicate nginx configurations +6. **test-vscode-from-server.sh** - Tests VSCode connectivity from server +7. **test-vscode-final.sh** - Final tests of both URLs ## How to Use @@ -143,6 +144,13 @@ nginx -t # Test configuration 2. Check nginx error logs: `tail -f /var/log/nginx/error.log` 3. Verify proxy_pass uses 127.0.0.1:8443 (not localhost) +### 429 Too Many Requests +If you get `429 (Too Many Requests)` errors when loading VSCode: +1. Run the fix script: `./fix-vscode-rate-limit.sh` +2. Redeploy nginx config: `./deploy-vscode-nginx.sh` +3. The issue is usually caused by too strict rate limiting for static files +4. Static files are now excluded from rate limiting automatically + ### SSL Certificate Issues Certificates are managed by Let's Encrypt and stored at: ``` @@ -173,12 +181,22 @@ code-server (127.0.0.1:8443) - VSCode Server - code-server runs as root user (WorkingDirectory=/root) - Authentication is enabled with password - WebSocket support is enabled for VSCode features -- Rate limiting is configured to prevent abuse +- Rate limiting is configured to prevent abuse: + - General requests: 200 requests/minute (burst: 50) + - Login attempts: 5 requests/minute (burst: 2) + - Static files (JS, CSS, images): No rate limiting - Automatic restart is configured (Restart=always) - Service is enabled to start on boot (enabled via systemctl) -## Recent Changes (2025-11-19) +## Recent Changes +### 2025-11-22 +1. ✅ Fixed 429 (Too Many Requests) error for static files + - Excluded static files (`/static/`, `/out/`, file extensions) from rate limiting + - Increased general rate limit from 30r/m to 200r/m + - Added caching for static files + +### 2025-11-19 1. ✅ Fixed code-server service configuration 2. ✅ Deployed nginx configuration 3. ✅ Fixed IPv6/IPv4 issue (localhost → 127.0.0.1)