Fix: update destination limits to 3 and sync with Redis
This commit is contained in:
@@ -11,6 +11,7 @@ import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:siro_driver/env/env.dart';
|
||||
import 'package:siro_driver/print.dart';
|
||||
import 'package:siro_driver/controller/functions/package_info.dart';
|
||||
|
||||
import '../../constant/api_key.dart';
|
||||
import '../../views/widgets/error_snakbar.dart';
|
||||
@@ -21,7 +22,6 @@ import 'ssl_pinning.dart';
|
||||
class CRUD {
|
||||
final NetGuard _netGuard = NetGuard();
|
||||
final _client = SslPinning.createPinnedClient();
|
||||
|
||||
static bool _isRefreshingJWT = false;
|
||||
static String _lastErrorSignature = '';
|
||||
static DateTime _lastErrorTimestamp = DateTime(2000);
|
||||
@@ -93,15 +93,15 @@ class CRUD {
|
||||
// نفس القيمة المرسلة عند login وعُملها hash في JWT
|
||||
// السيرفر يتحقق: sha256(X-Device-FP + FP_PEPPER) == JWT.fingerPrint
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
String _getFpHeader() {
|
||||
return box.read(BoxName.deviceFingerprint)?.toString() ?? '';
|
||||
Future<String> _getFpHeader() async {
|
||||
return box.read(BoxName.deviceFingerprint)?.toString() ?? await DeviceHelper.getDeviceFingerprint();
|
||||
}
|
||||
|
||||
String _getJwt() {
|
||||
Future<String> _getJwt() async {
|
||||
try {
|
||||
final jwt = box.read(BoxName.jwt);
|
||||
final jwt = await storage.read(key: BoxName.jwt);
|
||||
if (jwt == null || jwt.toString().isEmpty) return '';
|
||||
return r(jwt).toString().split(Env.addd)[0];
|
||||
return jwt;
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
@@ -172,6 +172,7 @@ class CRUD {
|
||||
final body = response.body;
|
||||
|
||||
Log.print('📥 [RES-$requestId] [$sc] $link');
|
||||
Log.print('payload: $payload');
|
||||
Log.print('📄 [BODY-$requestId] $body');
|
||||
|
||||
// 2xx
|
||||
@@ -217,14 +218,14 @@ class CRUD {
|
||||
required String link,
|
||||
Map<String, dynamic>? payload,
|
||||
}) async {
|
||||
String token = _getJwt();
|
||||
String token = await _getJwt();
|
||||
|
||||
// فحص صلاحية التوكن قبل الإرسال — تجنب طلب مضمون الرفض
|
||||
if (!_isJwtValid(token) && !_isRefreshingJWT) {
|
||||
_isRefreshingJWT = true;
|
||||
try {
|
||||
await Get.put(LoginDriverController()).getJWT();
|
||||
token = _getJwt();
|
||||
token = await _getJwt();
|
||||
} finally {
|
||||
_isRefreshingJWT = false;
|
||||
}
|
||||
@@ -233,7 +234,7 @@ class CRUD {
|
||||
final headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Bearer $token',
|
||||
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
|
||||
'X-Device-FP': await _getFpHeader(), // ← إثبات الجهاز
|
||||
};
|
||||
|
||||
return await _makeRequest(link: link, payload: payload, headers: headers);
|
||||
@@ -249,12 +250,12 @@ class CRUD {
|
||||
}) async {
|
||||
try {
|
||||
// فحص صلاحية التوكن قبل الإرسال
|
||||
String token = _getJwt();
|
||||
String token = await _getJwt();
|
||||
if (!_isJwtValid(token) && !_isRefreshingJWT) {
|
||||
_isRefreshingJWT = true;
|
||||
try {
|
||||
await Get.put(LoginDriverController()).getJWT();
|
||||
token = _getJwt();
|
||||
token = await _getJwt();
|
||||
} finally {
|
||||
_isRefreshingJWT = false;
|
||||
}
|
||||
@@ -267,7 +268,7 @@ class CRUD {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Bearer $token',
|
||||
'X-Device-FP': _getFpHeader(),
|
||||
'X-Device-FP': await _getFpHeader(),
|
||||
},
|
||||
).timeout(const Duration(seconds: 60));
|
||||
|
||||
@@ -321,7 +322,7 @@ class CRUD {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Bearer $jwt',
|
||||
'X-HMAC-Auth': hmac.toString(),
|
||||
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
|
||||
'X-Device-FP': await _getFpHeader(), // ← إثبات الجهاز
|
||||
};
|
||||
|
||||
return await _makeRequest(link: link, payload: payload, headers: headers);
|
||||
@@ -346,7 +347,7 @@ class CRUD {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Bearer $s',
|
||||
'X-HMAC-Auth': hmac.toString(),
|
||||
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
|
||||
'X-Device-FP': await _getFpHeader(), // ← إثبات الجهاز
|
||||
},
|
||||
).timeout(const Duration(seconds: 60));
|
||||
|
||||
@@ -392,7 +393,7 @@ class CRUD {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Bearer $s',
|
||||
'X-HMAC-Auth': hmac.toString(),
|
||||
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
|
||||
'X-Device-FP': await _getFpHeader(), // ← إثبات الجهاز
|
||||
},
|
||||
).timeout(const Duration(seconds: 60));
|
||||
|
||||
@@ -571,17 +572,17 @@ class CRUD {
|
||||
// ── sendEmail — إصلاح: استخدام r() بدل X.r() القديم ─────────
|
||||
Future<void> sendEmail(String link, Map<String, String>? payload) async {
|
||||
// r() هي نفس دالة فك التشفير الثلاثي المختصرة
|
||||
String token = _getJwt();
|
||||
String token = await _getJwt();
|
||||
|
||||
if (!_isJwtValid(token)) {
|
||||
await LoginDriverController().getJWT();
|
||||
token = _getJwt();
|
||||
token = await _getJwt();
|
||||
}
|
||||
|
||||
final headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Bearer $token',
|
||||
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
|
||||
'X-Device-FP': await _getFpHeader(), // ← إثبات الجهاز
|
||||
};
|
||||
|
||||
final request = http.Request('POST', Uri.parse(link));
|
||||
|
||||
@@ -46,6 +46,15 @@ class EncryptionHelper {
|
||||
debugPrint("EncryptionHelper initialized successfully.");
|
||||
}
|
||||
|
||||
/// Encrypts a string using AES-256-CBC with constant IV (deterministic)
|
||||
/// Same input always produces the same output
|
||||
String encryptDataCbc(String plainText) {
|
||||
final cbcEncrypter =
|
||||
encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
|
||||
final encrypted = cbcEncrypter.encrypt(plainText, iv: iv);
|
||||
return encrypted.base64;
|
||||
}
|
||||
|
||||
/// ✅ FIX H-04: Encrypts a string using AES-256-GCM with a random IV
|
||||
/// Format: "GCM:<base64_iv>:<base64_ciphertext>"
|
||||
String encryptData(String plainText) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:siro_driver/constant/box_name.dart';
|
||||
import 'package:siro_driver/constant/colors.dart';
|
||||
import 'package:siro_driver/constant/links.dart';
|
||||
import 'package:siro_driver/controller/functions/crud.dart';
|
||||
import 'package:siro_driver/controller/functions/encrypt_decrypt.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -16,7 +17,6 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
import '../../constant/info.dart';
|
||||
import '../../main.dart';
|
||||
import '../../print.dart';
|
||||
import 'encrypt_decrypt.dart';
|
||||
|
||||
Future<void> checkForUpdate(BuildContext context) async {
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
@@ -168,13 +168,6 @@ void showUpdateDialog(BuildContext context) {
|
||||
|
||||
class DeviceHelper {
|
||||
static Future<String> getDeviceFingerprint() async {
|
||||
await EncryptionHelper.initialize();
|
||||
|
||||
final cached = box.read(BoxName.deviceFingerprint);
|
||||
if (cached != null && cached.toString().isNotEmpty) {
|
||||
return cached.toString();
|
||||
}
|
||||
|
||||
final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
|
||||
var deviceData;
|
||||
|
||||
@@ -190,19 +183,20 @@ class DeviceHelper {
|
||||
}
|
||||
|
||||
final String deviceId = Platform.isAndroid
|
||||
? deviceData['id'] ?? deviceData['androidId'] ?? deviceData['fingerprint'] ?? 'unknown'
|
||||
? deviceData['androidId'] ?? deviceData['fingerprint'] ?? 'unknown'
|
||||
: deviceData['identifierForVendor'] ?? 'unknown';
|
||||
|
||||
final String deviceModel = deviceData['model'] ?? 'unknown';
|
||||
|
||||
final String fingerprint =
|
||||
EncryptionHelper.instance.encryptData('${deviceId}_$deviceModel');
|
||||
|
||||
box.write(BoxName.deviceFingerprint, fingerprint);
|
||||
return (fingerprint);
|
||||
final String fingerprint = '${deviceId}_$deviceModel';
|
||||
final String encryptedFp =
|
||||
EncryptionHelper.instance.encryptDataCbc(fingerprint);
|
||||
box.write(BoxName.deviceFpEncrypted, encryptedFp);
|
||||
box.write(BoxName.deviceFingerprint, encryptedFp);
|
||||
Log.print('fingerprint: $encryptedFp');
|
||||
return encryptedFp;
|
||||
} catch (e) {
|
||||
debugPrint('Error generating device fingerprint: $e');
|
||||
throw Exception('Failed to generate device fingerprint: $e');
|
||||
throw Exception('Failed to generate device fingerprint');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,44 +38,9 @@ class AppInitializer {
|
||||
List<Map<String, dynamic>> links = [];
|
||||
|
||||
Future<void> initializeApp() async {
|
||||
if (box.read(BoxName.jwt) == null) {
|
||||
String? secureJwt = await storage.read(key: BoxName.jwt);
|
||||
if (secureJwt != null) {
|
||||
box.write(BoxName.jwt, secureJwt);
|
||||
}
|
||||
}
|
||||
|
||||
if (box.read(BoxName.jwt) == null) {
|
||||
await LoginDriverController().getJWT();
|
||||
} else {
|
||||
String token =
|
||||
r(box.read(BoxName.jwt)).toString().split(AppInformation.addd)[0];
|
||||
bool isTokenValid = false;
|
||||
try {
|
||||
final parts = token.split('.');
|
||||
if (parts.length == 3) {
|
||||
String payload = parts[1];
|
||||
switch (payload.length % 4) {
|
||||
case 2:
|
||||
payload += '==';
|
||||
break;
|
||||
case 3:
|
||||
payload += '=';
|
||||
break;
|
||||
}
|
||||
final decoded = jsonDecode(utf8.decode(base64Url.decode(payload)));
|
||||
final exp = decoded['exp'];
|
||||
if (exp != null) {
|
||||
isTokenValid =
|
||||
DateTime.now().millisecondsSinceEpoch < (exp * 1000 - 30000);
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
if (!isTokenValid) {
|
||||
await LoginDriverController().getJWT();
|
||||
}
|
||||
}
|
||||
|
||||
// getJWT() now internally checks flutter_secure_storage and validates the token
|
||||
// before making any network requests.
|
||||
await LoginDriverController().getJWT();
|
||||
// await getKey();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user