new backend 29-04-2026
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:jwt_decoder/jwt_decoder.dart';
|
||||
import 'package:sefer_driver/controller/functions/encrypt_decrypt.dart';
|
||||
import 'package:sefer_driver/controller/functions/network/net_guard.dart';
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
@@ -21,10 +20,34 @@ import 'upload_image.dart';
|
||||
class CRUD {
|
||||
final NetGuard _netGuard = NetGuard();
|
||||
|
||||
static bool _isRefreshingJWT = false;
|
||||
static String _lastErrorSignature = '';
|
||||
static DateTime _lastErrorTimestamp = DateTime(2000);
|
||||
static const Duration _errorLogDebounceDuration = Duration(minutes: 1);
|
||||
|
||||
// ── فحص صلاحية JWT بدون مكتبات خارجية ──────────────────────
|
||||
static bool _isJwtValid(String? token) {
|
||||
if (token == null || token.isEmpty) return false;
|
||||
try {
|
||||
final parts = token.split('.');
|
||||
if (parts.length != 3) return false;
|
||||
// فك تشفير الـ payload (الجزء الثاني)
|
||||
String payload = parts[1];
|
||||
// إضافة padding للـ base64
|
||||
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) return false;
|
||||
// نعتبر التوكن منتهي قبل 30 ثانية من انتهاء الصلاحية (buffer)
|
||||
return DateTime.now().millisecondsSinceEpoch < (exp * 1000 - 30000);
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> addError(
|
||||
String error, String details, String where) async {
|
||||
try {
|
||||
@@ -93,6 +116,10 @@ class CRUD {
|
||||
|
||||
http.Response? response;
|
||||
int attempts = 0;
|
||||
final requestId = DateTime.now().millisecondsSinceEpoch.toString().substring(7);
|
||||
|
||||
Log.print('🚀 [REQ-$requestId] $link');
|
||||
if (payload != null) Log.print('📦 [PAYLOAD-$requestId] $payload');
|
||||
|
||||
while (attempts < 3) {
|
||||
try {
|
||||
@@ -129,7 +156,8 @@ class CRUD {
|
||||
final sc = response.statusCode;
|
||||
final body = response.body;
|
||||
|
||||
Log.print('_makeRequest [$sc] $link');
|
||||
Log.print('📥 [RES-$requestId] [$sc] $link');
|
||||
Log.print('📄 [BODY-$requestId] $body');
|
||||
|
||||
// 2xx
|
||||
if (sc >= 200 && sc < 300) {
|
||||
@@ -142,9 +170,18 @@ class CRUD {
|
||||
}
|
||||
}
|
||||
|
||||
// 401 → تجديد التوكن
|
||||
// 401 → تجديد التوكن (مع حماية من الحلقة اللانهائية)
|
||||
if (sc == 401) {
|
||||
await Get.put(LoginDriverController()).getJWT();
|
||||
// تخطي تجديد التوكن لـ endpoints غير حرجة (مثل تسجيل الأخطاء)
|
||||
final isNonCritical = link.contains('errorApp.php');
|
||||
if (!_isRefreshingJWT && !isNonCritical) {
|
||||
_isRefreshingJWT = true;
|
||||
try {
|
||||
await Get.put(LoginDriverController()).getJWT();
|
||||
} finally {
|
||||
_isRefreshingJWT = false;
|
||||
}
|
||||
}
|
||||
return 'token_expired';
|
||||
}
|
||||
|
||||
@@ -167,6 +204,17 @@ class CRUD {
|
||||
}) async {
|
||||
String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
|
||||
|
||||
// فحص صلاحية التوكن قبل الإرسال — تجنب طلب مضمون الرفض
|
||||
if (!_isJwtValid(token) && !_isRefreshingJWT) {
|
||||
_isRefreshingJWT = true;
|
||||
try {
|
||||
await Get.put(LoginDriverController()).getJWT();
|
||||
token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
|
||||
} finally {
|
||||
_isRefreshingJWT = false;
|
||||
}
|
||||
}
|
||||
|
||||
final headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Bearer $token',
|
||||
@@ -185,15 +233,26 @@ class CRUD {
|
||||
Map<String, dynamic>? payload,
|
||||
}) async {
|
||||
try {
|
||||
// فحص صلاحية التوكن قبل الإرسال
|
||||
String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
|
||||
if (!_isJwtValid(token) && !_isRefreshingJWT) {
|
||||
_isRefreshingJWT = true;
|
||||
try {
|
||||
await Get.put(LoginDriverController()).getJWT();
|
||||
token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
|
||||
} finally {
|
||||
_isRefreshingJWT = false;
|
||||
}
|
||||
}
|
||||
|
||||
var url = Uri.parse(link);
|
||||
var response = await http.post(
|
||||
url,
|
||||
body: payload,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization':
|
||||
'Bearer ${r(box.read(BoxName.jwt)).toString().split(Env.addd)[0]}',
|
||||
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
|
||||
'Authorization': 'Bearer $token',
|
||||
'X-Device-FP': _getFpHeader(),
|
||||
},
|
||||
).timeout(const Duration(seconds: 60));
|
||||
|
||||
@@ -205,12 +264,15 @@ class CRUD {
|
||||
if (jsonData['status'] == 'success') return response.body;
|
||||
return jsonData['status'];
|
||||
} else if (response.statusCode == 401) {
|
||||
var jsonData = jsonDecode(response.body);
|
||||
if (jsonData['error'] == 'Token expired') {
|
||||
await Get.put(LoginDriverController()).getJWT();
|
||||
return 'token_expired';
|
||||
if (!_isRefreshingJWT) {
|
||||
_isRefreshingJWT = true;
|
||||
try {
|
||||
await Get.put(LoginDriverController()).getJWT();
|
||||
} finally {
|
||||
_isRefreshingJWT = false;
|
||||
}
|
||||
}
|
||||
return 'failure';
|
||||
return 'token_expired';
|
||||
} else {
|
||||
addError('Non-200: ${response.statusCode}', 'crud().get - Other',
|
||||
url.toString());
|
||||
@@ -505,7 +567,7 @@ class CRUD {
|
||||
// r() هي نفس دالة فك التشفير الثلاثي المختصرة
|
||||
String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
|
||||
|
||||
if (JwtDecoder.isExpired(token)) {
|
||||
if (!_isJwtValid(token)) {
|
||||
await LoginDriverController().getJWT();
|
||||
token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
|
||||
}
|
||||
|
||||
@@ -412,6 +412,9 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
void emitLocationToSocket(LatLng pos, double head, double spd) {
|
||||
String status = box.read(BoxName.statusDriverLocation) ?? 'on';
|
||||
String? currentRideStatus = box.read(BoxName.rideStatus);
|
||||
String? storedPassengerId = box.read(BoxName.passengerID);
|
||||
String? storedRideId = box.read(BoxName.rideId);
|
||||
|
||||
// Basic payload
|
||||
var payload = {
|
||||
@@ -424,16 +427,14 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
'distance': totalDistance,
|
||||
};
|
||||
|
||||
// 🔥 CRITICAL FIX: Inject Passenger ID if a ride is active 🔥
|
||||
if (Get.isRegistered<MapDriverController>()) {
|
||||
final mapCtrl = Get.find<MapDriverController>();
|
||||
// 🔥 القرار الذكي: حقن بيانات الراكب إذا كان هناك رحلة نشطة في الـ Box 🔥
|
||||
bool hasActiveRide = (currentRideStatus == 'Begin' ||
|
||||
currentRideStatus == 'Apply' ||
|
||||
currentRideStatus == 'Arrived');
|
||||
|
||||
// Check if ride is started/active and we have a passenger ID
|
||||
if (mapCtrl.isRideStarted && mapCtrl.passengerId != null) {
|
||||
payload['passenger_id'] =
|
||||
mapCtrl.passengerId; // This triggers the PHP forwarding
|
||||
payload['ride_id'] = mapCtrl.rideId; // Good for debugging
|
||||
}
|
||||
if (hasActiveRide && storedPassengerId != null) {
|
||||
payload['passenger_id'] = storedPassengerId;
|
||||
payload['ride_id'] = storedRideId;
|
||||
}
|
||||
|
||||
// DebugLog.print to verify
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:jwt_decoder/jwt_decoder.dart';
|
||||
import 'package:secure_string_operations/secure_string_operations.dart';
|
||||
import 'package:sefer_driver/controller/auth/captin/login_captin_controller.dart';
|
||||
import 'package:sefer_driver/controller/functions/encrypt_decrypt.dart';
|
||||
@@ -42,11 +41,24 @@ class AppInitializer {
|
||||
if (box.read(BoxName.jwt) == null) {
|
||||
await LoginDriverController().getJWT();
|
||||
} else {
|
||||
bool isTokenExpired = JwtDecoder.isExpired(X
|
||||
.r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs)
|
||||
.toString()
|
||||
.split(AppInformation.addd)[0]);
|
||||
if (isTokenExpired) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user