Update: 2026-08-02 18:28:40
This commit is contained in:
@@ -10,6 +10,7 @@ class BoxName {
|
||||
static const String gender = "gender";
|
||||
static const String themeMode = "themeMode";
|
||||
static const String jwt = "jwt";
|
||||
static const String walletJwt = "walletJwt";
|
||||
static const String lowEndMode = "lowEndMode";
|
||||
static const String deviceFpEncrypted = "deviceFpEncrypted";
|
||||
static const String appVersionChecked = "appVersionChecked";
|
||||
|
||||
@@ -284,9 +284,64 @@ class LoginController extends GetxController {
|
||||
// • يرجع hmac مع الـ jwt ويخزنه في GetStorage
|
||||
// • الـ JWT لا يُشفَّر ثلاثياً (يُستخدم مباشرة في الـ header)
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// • يُخزَّن التوكن ويُعاد استخدامه ما دام أمامه أكثر من 10 ثوانٍ
|
||||
// (عمره 300ث)، فلا نُسجّل دخولاً مع كل طلب محفظة.
|
||||
static Future<String?>? _walletJwtFuture;
|
||||
|
||||
Future<String?> getJwtWallet() async {
|
||||
// طلب قيد التنفيذ؟ شارك نتيجته بدل إطلاق طلب ثانٍ.
|
||||
if (_walletJwtFuture != null) {
|
||||
Log.print('⏳ getJwtWallet: طلب قيد التنفيذ — إعادة استخدام نفس الـ future.');
|
||||
return _walletJwtFuture!;
|
||||
}
|
||||
_walletJwtFuture = _getJwtWalletInternal();
|
||||
try {
|
||||
return await _walletJwtFuture!;
|
||||
} finally {
|
||||
_walletJwtFuture = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// هل التوكن المخزَّن صالح ويخصّ الراكب الحالي؟
|
||||
/// نتحقق من `user_id` أيضاً كي لا يُعاد استخدام توكن حساب سابق
|
||||
/// على نفس الجهاز بعد تبديل المستخدم.
|
||||
bool _isWalletJwtUsable(String jwt, String passengerId) {
|
||||
try {
|
||||
final parts = jwt.split('.');
|
||||
if (parts.length != 3) return false;
|
||||
String payload = parts[1];
|
||||
while (payload.length % 4 != 0) {
|
||||
payload += '=';
|
||||
}
|
||||
final decoded = jsonDecode(utf8.decode(base64Url.decode(payload)));
|
||||
|
||||
if ((decoded['user_id'] ?? '').toString() != passengerId) return false;
|
||||
|
||||
final exp = decoded['exp'];
|
||||
if (exp == null) return false;
|
||||
// هامش 10 ثوانٍ حتى لا ينتهي التوكن أثناء الطلب نفسه
|
||||
return DateTime.now().millisecondsSinceEpoch < (exp * 1000 - 10000);
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> _getJwtWalletInternal() async {
|
||||
dev = Platform.isAndroid ? 'android' : 'ios';
|
||||
|
||||
final String passengerId = box.read(BoxName.passengerID)?.toString() ?? '';
|
||||
|
||||
// ── إعادة استخدام التوكن المخزَّن إن كان صالحاً ────────────────
|
||||
final String cachedJwt = box.read(BoxName.walletJwt)?.toString() ?? '';
|
||||
final String cachedHmac = box.read(BoxName.hmac)?.toString() ?? '';
|
||||
if (cachedJwt.isNotEmpty &&
|
||||
cachedHmac.isNotEmpty &&
|
||||
passengerId.isNotEmpty &&
|
||||
_isWalletJwtUsable(cachedJwt, passengerId)) {
|
||||
Log.print('🔑 توكن محفظة صالح في التخزين — تخطّي طلب السيرفر.');
|
||||
return cachedJwt;
|
||||
}
|
||||
|
||||
// نعيد حساب البصمة أولاً كي لا نرسل قيمة GCM قديمة عالقة في التخزين
|
||||
// من نسخة سابقة من التطبيق (مثل getJWT تماماً).
|
||||
await DeviceHelper.getDeviceFingerprint();
|
||||
@@ -323,6 +378,11 @@ class LoginController extends GetxController {
|
||||
box.write(BoxName.hmac, hmac);
|
||||
}
|
||||
|
||||
// نخزّن التوكن ليُعاد استخدامه حتى قرب انتهائه بدل طلب جديد كل مرة
|
||||
if (jwt != null) {
|
||||
box.write(BoxName.walletJwt, jwt);
|
||||
}
|
||||
|
||||
return jwt;
|
||||
}
|
||||
|
||||
|
||||
@@ -284,6 +284,16 @@ class CRUD {
|
||||
// التغيير: إضافة X-Device-FP header
|
||||
// 3 headers معاً: JWT + HMAC + FP
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
/// توكن المحفظة صار يُخزَّن ويُعاد استخدامه. لو رفضه السيرفر (توكن
|
||||
/// مُلغى أو تغيّر سرّ التوقيع) نمسح النسخة المخزّنة كي يُطلب توكن
|
||||
/// جديد في المحاولة التالية بدل أن نعلق على توكن ميت.
|
||||
void _invalidateWalletJwtIfRejected(dynamic result) {
|
||||
if (result == 'token_expired') {
|
||||
Log.print('🗑️ توكن المحفظة مرفوض — مسح النسخة المخزّنة.');
|
||||
box.remove(BoxName.walletJwt);
|
||||
}
|
||||
}
|
||||
|
||||
Future<dynamic> postWallet({
|
||||
required String link,
|
||||
Map<String, dynamic>? payload,
|
||||
@@ -297,11 +307,13 @@ class CRUD {
|
||||
'X-HMAC-Auth': hmac.toString(),
|
||||
'X-Device-FP': _getFpHeader(),
|
||||
};
|
||||
Log.print('headers: $headers');
|
||||
Log.print('payload: $payload');
|
||||
Log.print('link: $link');
|
||||
|
||||
return await _makeRequest(link: link, payload: payload, headers: headers);
|
||||
final result =
|
||||
await _makeRequest(link: link, payload: payload, headers: headers);
|
||||
_invalidateWalletJwtIfRejected(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<dynamic> getWallet({
|
||||
@@ -318,7 +330,10 @@ class CRUD {
|
||||
'X-Device-FP': _getFpHeader(),
|
||||
};
|
||||
|
||||
return await _makeRequest(link: link, payload: payload, headers: headers);
|
||||
final result =
|
||||
await _makeRequest(link: link, payload: payload, headers: headers);
|
||||
_invalidateWalletJwtIfRejected(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
|
||||
Reference in New Issue
Block a user