Update codebase
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:tripz_rider/constant/box_name.dart';
|
||||
import 'package:tripz_rider/constant/links.dart';
|
||||
import 'package:tripz_rider/controller/auth/login_controller.dart';
|
||||
import 'package:tripz_rider/main.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/constant/links.dart';
|
||||
import 'package:siro_rider/controller/auth/login_controller.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:tripz_rider/env/env.dart';
|
||||
import 'package:siro_rider/env/env.dart';
|
||||
|
||||
import '../../constant/api_key.dart';
|
||||
|
||||
@@ -24,7 +24,6 @@ class CRUD {
|
||||
final NetGuard _netGuard = NetGuard();
|
||||
final _client = SslPinning.createPinnedClient();
|
||||
|
||||
static bool _isRefreshingJWT = false;
|
||||
static String _lastErrorSignature = '';
|
||||
static DateTime _lastErrorTimestamp = DateTime(2000);
|
||||
static const Duration _errorLogDebounceDuration = Duration(minutes: 1);
|
||||
@@ -98,31 +97,50 @@ class CRUD {
|
||||
|
||||
Future<String> _getJwt() async {
|
||||
try {
|
||||
final String? encryptedJwt = await storage.read(key: BoxName.jwt);
|
||||
if (encryptedJwt == null || encryptedJwt.isEmpty) {
|
||||
final String? fallback = box.read(BoxName.jwt);
|
||||
final jwt = await storage.read(key: BoxName.jwt);
|
||||
if (jwt == null || jwt.toString().isEmpty) {
|
||||
// إذا كان التخزين الآمن فارغاً، نحاول استخراج التوكن القديم من GetStorage للركاب القدامى
|
||||
final fallback = box.read(BoxName.jwt);
|
||||
if (fallback != null) {
|
||||
return r(fallback).toString().split(Env.addd)[0];
|
||||
try {
|
||||
return r(fallback).toString().split(Env.addd)[0]; // فك تشفير القديم
|
||||
} catch (_) {
|
||||
return fallback.toString(); // ربما تم تخزينه بدون تشفير
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
return r(encryptedJwt).toString().split(Env.addd)[0];
|
||||
} catch (e) {
|
||||
Log.print('Error reading JWT from SecureStorage: $e');
|
||||
final String? fallback = box.read(BoxName.jwt);
|
||||
if (fallback != null) {
|
||||
return r(fallback).toString().split(Env.addd)[0];
|
||||
// التحقق السريع إذا كان التوكن لا يزال مشفراً (يبدأ برموز غريبة وليس ey)
|
||||
if (!jwt.startsWith('ey')) {
|
||||
try {
|
||||
return r(jwt).toString().split(Env.addd)[0];
|
||||
} catch (_) {}
|
||||
}
|
||||
return jwt;
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// _ensureJwt — يضمن وجود توكن صالح قبل الإرسال
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
Future<String> _ensureJwt() async {
|
||||
String token = await _getJwt();
|
||||
if (_isJwtValid(token)) return token;
|
||||
|
||||
final ok = await Get.put(LoginController()).getJWT();
|
||||
if (!ok) return '';
|
||||
return await _getJwt();
|
||||
}
|
||||
|
||||
/// Centralized request handler with retry for weak networks.
|
||||
/// For Syria (3G): 60s total timeout, 3 retries, exponential backoff.
|
||||
Future<dynamic> _makeRequest({
|
||||
required String link,
|
||||
Map<String, dynamic>? payload,
|
||||
required Map<String, String> headers,
|
||||
bool allowRefresh = true,
|
||||
}) async {
|
||||
const totalTimeout = Duration(seconds: 60);
|
||||
|
||||
@@ -180,17 +198,33 @@ class CRUD {
|
||||
}
|
||||
}
|
||||
|
||||
// 429 → السيرفر رافض بسبب الضغط؛ ممنوع نجدّد التوكن أو نعيد المحاولة
|
||||
if (sc == 429) {
|
||||
Log.print('🛑 [RES] 429 rate limited — $link');
|
||||
return 'rate_limited';
|
||||
}
|
||||
|
||||
// 401 → تجديد التوكن مرة واحدة ثم إعادة الطلب مرة واحدة فقط
|
||||
if (sc == 401) {
|
||||
// تخطي تجديد التوكن لـ endpoints غير حرجة (مثل تسجيل الأخطاء)
|
||||
final isNonCritical = link.contains('errorApp.php');
|
||||
if (!_isRefreshingJWT && !isNonCritical) {
|
||||
_isRefreshingJWT = true;
|
||||
try {
|
||||
await Get.put(LoginController()).getJWT();
|
||||
} finally {
|
||||
_isRefreshingJWT = false;
|
||||
}
|
||||
}
|
||||
return 'token_expired';
|
||||
if (isNonCritical || !allowRefresh) return 'token_expired';
|
||||
|
||||
final refreshed = await Get.put(LoginController()).getJWT();
|
||||
if (!refreshed) return 'token_expired';
|
||||
|
||||
final newToken = await _getJwt();
|
||||
if (newToken.isEmpty) return 'token_expired';
|
||||
|
||||
// إعادة الطلب بالتوكن الجديد — allowRefresh: false يمنع أي تكرار إضافي
|
||||
final retryHeaders = Map<String, String>.from(headers)
|
||||
..['Authorization'] = 'Bearer $newToken';
|
||||
return await _makeRequest(
|
||||
link: link,
|
||||
payload: payload,
|
||||
headers: retryHeaders,
|
||||
allowRefresh: false,
|
||||
);
|
||||
}
|
||||
|
||||
if (sc >= 500) {
|
||||
@@ -206,7 +240,14 @@ class CRUD {
|
||||
required String link,
|
||||
Map<String, dynamic>? payload,
|
||||
}) async {
|
||||
String token = await _getJwt();
|
||||
String token = await _ensureJwt();
|
||||
if (token.isEmpty) {
|
||||
// إذا فشل الحصول على توكن، لا ترسل الطلب للباك إند لأنّه سيرفض حتماً.
|
||||
// باستثناء تسجيل الدخول لأنه لا يحتاج توكن
|
||||
if (!link.contains('login') && !link.contains('errorApp.php')) {
|
||||
return 'token_expired';
|
||||
}
|
||||
}
|
||||
|
||||
final headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
@@ -221,7 +262,12 @@ class CRUD {
|
||||
required String link,
|
||||
Map<String, dynamic>? payload,
|
||||
}) async {
|
||||
String token = await _getJwt();
|
||||
String token = await _ensureJwt();
|
||||
if (token.isEmpty) {
|
||||
if (!link.contains('login') && !link.contains('errorApp.php')) {
|
||||
return 'token_expired';
|
||||
}
|
||||
}
|
||||
|
||||
final headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
@@ -238,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,
|
||||
@@ -251,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({
|
||||
@@ -272,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