diff --git a/.claude/settings.local.json b/.claude/settings.local.json index e6ffa67d..14573e6f 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -756,7 +756,8 @@ "Bash(set DATABASE_URL=postgresql://user:pass@localhost:5432/db)", "Bash(cmd /c \"set DATABASE_URL=postgresql://user:pass@localhost:5432/db && npx prisma migrate dev --name add_nickname_to_synced_legacy_users --create-only\")", "Bash(dir \"c:\\\\Users\\\\dong\\\\Desktop\\\\rwadurian\\\\frontend\")", - "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(mining-app\\): fix login bugs and connect contribution page to real API\n\nLogin fixes:\n- Add AuthEventBus for global 401 error handling with auto-logout\n- Add route guards with GoRouter redirect to protect authenticated routes\n- Remove setMockUser\\(\\) security vulnerability and legacy login\\(\\) dead code\n- Remove unused AuthInterceptor class\n\nContribution page:\n- Add ContributionRecord entity and model for records API\n- Connect contribution details card to GET /accounts/{id}/records endpoint\n- Display real team stats \\(direct referrals, unlocked levels/tiers\\)\n- Calculate expiration countdown from actual record data\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")" + "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(mining-app\\): fix login bugs and connect contribution page to real API\n\nLogin fixes:\n- Add AuthEventBus for global 401 error handling with auto-logout\n- Add route guards with GoRouter redirect to protect authenticated routes\n- Remove setMockUser\\(\\) security vulnerability and legacy login\\(\\) dead code\n- Remove unused AuthInterceptor class\n\nContribution page:\n- Add ContributionRecord entity and model for records API\n- Connect contribution details card to GET /accounts/{id}/records endpoint\n- Display real team stats \\(direct referrals, unlocked levels/tiers\\)\n- Calculate expiration countdown from actual record data\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")", + "Bash(dependency of a provider changed\" error when 401 responses triggered\nlogout during provider rebuilds.\n\nNow 401 handling is done through normal exception flow in splash page\nand route guards respond to isLoggedInProvider state changes.\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")" ], "deny": [], "ask": [] diff --git a/frontend/mining-app/lib/core/network/api_client.dart b/frontend/mining-app/lib/core/network/api_client.dart index d7e8921d..8d3bcee0 100644 --- a/frontend/mining-app/lib/core/network/api_client.dart +++ b/frontend/mining-app/lib/core/network/api_client.dart @@ -1,30 +1,8 @@ -import 'dart:async'; import 'package:dio/dio.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../constants/app_constants.dart'; import '../error/exceptions.dart'; -/// 全局未授权事件控制器,用于通知 401 错误 -class AuthEventBus { - static final AuthEventBus _instance = AuthEventBus._internal(); - factory AuthEventBus() => _instance; - AuthEventBus._internal(); - - final _unauthorizedController = StreamController.broadcast(); - - /// 监听未授权事件 - Stream get onUnauthorized => _unauthorizedController.stream; - - /// 触发未授权事件 - void emitUnauthorized() { - _unauthorizedController.add(null); - } - - void dispose() { - _unauthorizedController.close(); - } -} - class ApiClient { final Dio dio; @@ -129,8 +107,6 @@ class ApiClient { case DioExceptionType.badResponse: final statusCode = e.response?.statusCode; if (statusCode == 401) { - // 触发全局未授权事件,通知应用进行登出处理 - AuthEventBus().emitUnauthorized(); return UnauthorizedException(); } final message = e.response?.data?['error']?['message']?[0] ?? '服务器错误'; diff --git a/frontend/mining-app/lib/core/router/app_router.dart b/frontend/mining-app/lib/core/router/app_router.dart index cc7774ac..1f6eaa11 100644 --- a/frontend/mining-app/lib/core/router/app_router.dart +++ b/frontend/mining-app/lib/core/router/app_router.dart @@ -1,4 +1,3 @@ -import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; @@ -14,7 +13,6 @@ import '../../presentation/pages/asset/asset_page.dart'; import '../../presentation/pages/profile/profile_page.dart'; import '../../presentation/widgets/main_shell.dart'; import '../../presentation/providers/user_providers.dart'; -import '../network/api_client.dart'; import 'routes.dart'; /// 不需要登录就能访问的公开路由 @@ -28,15 +26,6 @@ const _publicRoutes = { /// 路由刷新通知器,用于在登录状态变化时刷新路由 class AuthNotifier extends ChangeNotifier { bool _isLoggedIn = false; - StreamSubscription? _unauthorizedSubscription; - - AuthNotifier() { - // 监听 401 未授权事件 - _unauthorizedSubscription = AuthEventBus().onUnauthorized.listen((_) { - _isLoggedIn = false; - notifyListeners(); - }); - } bool get isLoggedIn => _isLoggedIn; @@ -46,12 +35,6 @@ class AuthNotifier extends ChangeNotifier { notifyListeners(); } } - - @override - void dispose() { - _unauthorizedSubscription?.cancel(); - super.dispose(); - } } final authNotifierProvider = ChangeNotifierProvider((ref) {