Update: 2026-08-02 18:28:40

This commit is contained in:
Hamza-Ayed
2026-08-02 18:28:40 +03:00
parent 03a21aa95d
commit 02e652a786
9 changed files with 98 additions and 9 deletions
+1
View File
@@ -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;
}
+18 -3
View File
@@ -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;
}
// =======================================================================
+6 -6
View File
@@ -1297,10 +1297,10 @@ packages:
dependency: transitive
description:
name: matcher
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
url: "https://pub.dev"
source: hosted
version: "0.12.19"
version: "0.12.18"
material_color_utilities:
dependency: transitive
description:
@@ -1313,10 +1313,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev"
source: hosted
version: "1.18.0"
version: "1.17.0"
mime:
dependency: "direct main"
description:
@@ -1901,10 +1901,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
url: "https://pub.dev"
source: hosted
version: "0.7.11"
version: "0.7.9"
timezone:
dependency: transitive
description: