diff --git a/siro_driver/lib/controller/auth/captin/login_captin_controller.dart b/siro_driver/lib/controller/auth/captin/login_captin_controller.dart index c63ebd1f..cfbef3cb 100755 --- a/siro_driver/lib/controller/auth/captin/login_captin_controller.dart +++ b/siro_driver/lib/controller/auth/captin/login_captin_controller.dart @@ -237,7 +237,14 @@ class LoginDriverController extends GetxController { return d.isNegative ? Duration.zero : d; } - Future getJWT() async { + /// [force] يتجاوز الـ backoff cooldown — يُستخدم فقط في المسارات التي + /// يبدأها المستخدم (فتح التطبيق / تسجيل الدخول)، لا في الطلبات الخلفية، + /// حتى لا يعلق الكابتن خارج التطبيق بانتظار انتهاء الـ cooldown. + Future getJWT({bool force = false}) async { + if (force) { + _jwtCooldownUntil = DateTime(2000); + _jwtFailures = 0; + } if (_jwtFuture != null) { Log.print('⏳ getJWT: تجديد قيد التنفيذ — إعادة استخدام نفس الـ future.'); return _jwtFuture!; @@ -297,7 +304,8 @@ class LoginDriverController extends GetxController { final driverId = box.read(BoxName.driverID); final isRegistering = driverId == null || driverId.toString().isEmpty; - if (isRegistering && box.read(BoxName.firstTimeLoadKey).toString() != 'false') { + if (isRegistering && + box.read(BoxName.firstTimeLoadKey).toString() != 'false') { var payload = { 'id': box.read(BoxName.driverID) ?? AK.newId, 'password': AK.passnpassenger, @@ -306,18 +314,21 @@ class LoginDriverController extends GetxController { await DeviceHelper.getDeviceFingerprint(), }; - var response0 = await http.post( - Uri.parse(AppLink.loginFirstTimeDriver), - body: payload, - ).timeout(const Duration(seconds: 30)); - + var response0 = await http + .post( + Uri.parse(AppLink.loginFirstTimeDriver), + body: payload, + ) + .timeout(const Duration(seconds: 30)); + if (response0.statusCode == 429) { - final retryAfter = int.tryParse(response0.headers['retry-after'] ?? '') ?? 60; + final retryAfter = + int.tryParse(response0.headers['retry-after'] ?? '') ?? 60; _jwtCooldownUntil = DateTime.now().add(Duration(seconds: retryAfter)); Log.print('🛑 getJWT(firstTime): 429 — cooldown ${retryAfter}s'); return false; } - + if (response0.statusCode == 200) { final decodedResponse1 = jsonDecode(response0.body); String? jwt; @@ -350,14 +361,17 @@ class LoginDriverController extends GetxController { 'fingerPrint': box.read(BoxName.deviceFingerprint) ?? await DeviceHelper.getDeviceFingerprint(), }; - - var response1 = await http.post( - Uri.parse(AppLink.loginJwtDriver), - body: payload, - ).timeout(const Duration(seconds: 30)); - + + var response1 = await http + .post( + Uri.parse(AppLink.loginJwtDriver), + body: payload, + ) + .timeout(const Duration(seconds: 30)); + if (response1.statusCode == 429) { - final retryAfter = int.tryParse(response1.headers['retry-after'] ?? '') ?? 60; + final retryAfter = + int.tryParse(response1.headers['retry-after'] ?? '') ?? 60; _jwtCooldownUntil = DateTime.now().add(Duration(seconds: retryAfter)); Log.print('🛑 getJWT: 429 — cooldown ${retryAfter}ث'); return false; @@ -394,7 +408,8 @@ class LoginDriverController extends GetxController { _jwtFailures++; final seconds = _jwtFailures >= 6 ? 60 : (1 << _jwtFailures); _jwtCooldownUntil = DateTime.now().add(Duration(seconds: seconds)); - Log.print('❌ getJWT فشل ($reason) — محاولة #$_jwtFailures، cooldown ${seconds}ث'); + Log.print( + '❌ getJWT فشل ($reason) — محاولة #$_jwtFailures، cooldown ${seconds}ث'); return false; } @@ -443,6 +458,23 @@ class LoginDriverController extends GetxController { } } + /// جلسة محلية صالحة: عندنا driverID + رقم متحقق منه مسبقاً. + /// في هذه الحالة أي فشل شبكة/توكن هو عارض ولا يجوز إرجاع الكابتن لـ OTP. + bool get hasLocalVerifiedSession { + final id = box.read(BoxName.driverID)?.toString() ?? ''; + return id.isNotEmpty && + box.read(BoxName.phoneVerified).toString() == '1' && + box.read(BoxName.firstTimeLoadKey).toString() == 'false'; + } + + /// نتائج عارضة من CRUD (ليست JSON) — شبكة/توكن/ضغط سيرفر. + static const _transientResults = { + 'failure', + 'no_internet', + 'token_expired', + 'rate_limited', + }; + loginDriver(String driverID, email) async { isloading = true; update(); @@ -452,6 +484,40 @@ class LoginDriverController extends GetxController { var res = await CRUD().get( link: AppLink.loginFromGoogleCaptin, payload: {'driver_id': driverID}); Log.print('loginDriver: ${res}'); + + // ═══════════════════════════════════════════════════════════════ + // فشل عارض (شبكة / توكن منتهي / rate limit): + // إذا الجهاز محفوظ والرقم متحقق ⇒ لا نرجع لـ OTP إطلاقاً. + // نجدّد التوكن مرة (متجاوزين الـ cooldown) ونعيد المحاولة مرة واحدة. + // ═══════════════════════════════════════════════════════════════ + if (res is String && _transientResults.contains(res)) { + if (hasLocalVerifiedSession) { + Log.print('⚠️ loginDriver: نتيجة عارضة "$res" مع جلسة محلية صالحة — ' + 'إعادة محاولة بعد تجديد التوكن قسراً.'); + final refreshed = await getJWT(force: true); + if (refreshed) { + res = await CRUD().get( + link: AppLink.loginFromGoogleCaptin, + payload: {'driver_id': driverID}); + Log.print('loginDriver(retry): $res'); + } + } + + // ما زالت النتيجة عارضة + if (res is String && _transientResults.contains(res)) { + isloading = false; + update(); + if (hasLocalVerifiedSession) { + // ندخله للتطبيق بجلسته المحفوظة بدل رميه على OTP. + mySnackbarError('Connection problem, please try again'.tr); + Get.offAll(() => HomeCaptain()); + return true; + } + await isPhoneVerified(); + return false; + } + } + if (res == 'failure') { await isPhoneVerified(); isloading = false; // <--- أضفت هذا أيضاً @@ -459,7 +525,21 @@ class LoginDriverController extends GetxController { return false; // Get.snackbar('Failure', '', backgroundColor: Colors.red); } else { - var jsonDecoeded = jsonDecode(res); + dynamic jsonDecoeded; + try { + jsonDecoeded = jsonDecode(res); + } catch (e) { + // رد غير JSON — لا نُسقط الجلسة بسببه. + Log.print('❌ loginDriver: رد غير صالح "$res" ($e)'); + isloading = false; + update(); + if (hasLocalVerifiedSession) { + Get.offAll(() => HomeCaptain()); + return true; + } + await isPhoneVerified(); + return false; + } var d = jsonDecoeded['data'][0]; if (jsonDecoeded.isNotEmpty) { if (jsonDecoeded['status'] == 'success' && @@ -537,7 +617,7 @@ class LoginDriverController extends GetxController { final serverFp = serverData['data'][0]['fingerPrint'].toString(); final tokenMismatch = storedToken.isNotEmpty && serverToken != storedToken; - final fingerprintMismatch = + final fingerprintMismatch = serverFp.isNotEmpty && serverFp != fingerPrint.toString(); Log.print( '🔍 [FP_COMPARE] serverFp: $serverFp | localFp: ${fingerPrint.toString()} | fingerprintMismatch: $fingerprintMismatch | tokenMismatch: $tokenMismatch | serverToken: $serverToken | storedToken: $storedToken'); diff --git a/siro_driver/lib/controller/home/splash_screen_controlle.dart b/siro_driver/lib/controller/home/splash_screen_controlle.dart index 7d130338..37371acf 100755 --- a/siro_driver/lib/controller/home/splash_screen_controlle.dart +++ b/siro_driver/lib/controller/home/splash_screen_controlle.dart @@ -6,6 +6,7 @@ import 'package:get/get.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:siro_driver/controller/auth/captin/login_captin_controller.dart'; import 'package:siro_driver/views/auth/captin/login_captin.dart'; +import 'package:siro_driver/views/home/Captin/home_captain/home_captin.dart'; import 'package:siro_driver/views/home/on_boarding_page.dart'; import '../functions/app_update_controller.dart'; @@ -131,7 +132,9 @@ class SplashScreenController extends GetxController await EncryptionHelper.initialize(); await DeviceHelper.getDeviceFingerprint(); - await loginController.getJWT(); + // بداية باردة يبدأها المستخدم ⇒ force حتى لا يمنعنا الـ cooldown + // من توليد توكن جديد بعد انتهاء صلاحية الـ 4 ساعات. + await loginController.getJWT(force: true); await loginController.loginDriver( box.read(BoxName.driverID).toString(), @@ -141,6 +144,12 @@ class SplashScreenController extends GetxController return null; // لأن loginWithGoogleCredential يوجّه } catch (e) { Log.print("Error during navigation logic: $e"); + // لا نُسقط جلسة كابتن محفوظة ومتحقق منها بسبب خطأ عارض (شبكة/تحليل رد). + try { + if (Get.put(LoginDriverController()).hasLocalVerifiedSession) { + return HomeCaptain(); + } + } catch (_) {} return LoginCaptin(); } }