Update: 2026-08-06 14:24:16
This commit is contained in:
@@ -237,7 +237,14 @@ class LoginDriverController extends GetxController {
|
||||
return d.isNegative ? Duration.zero : d;
|
||||
}
|
||||
|
||||
Future<bool> getJWT() async {
|
||||
/// [force] يتجاوز الـ backoff cooldown — يُستخدم فقط في المسارات التي
|
||||
/// يبدأها المستخدم (فتح التطبيق / تسجيل الدخول)، لا في الطلبات الخلفية،
|
||||
/// حتى لا يعلق الكابتن خارج التطبيق بانتظار انتهاء الـ cooldown.
|
||||
Future<bool> 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');
|
||||
|
||||
Reference in New Issue
Block a user