new backend 29-04-2026

This commit is contained in:
Hamza-Ayed
2026-04-30 01:42:57 +03:00
parent b92db3bb39
commit 4385ef5a99
20 changed files with 796 additions and 708 deletions

View File

@@ -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];
}

View File

@@ -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

View File

@@ -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();
}
}