refactor: upgrade API endpoints to v3 and refactor navigation and response handling across controllers
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
@@ -21,14 +22,12 @@ import 'package:Intaleq/views/home/map_page_passenger.dart';
|
||||
|
||||
import 'package:location/location.dart';
|
||||
|
||||
|
||||
|
||||
import '../../env/env.dart';
|
||||
import '../../print.dart';
|
||||
import '../../views/auth/otp_token_page.dart';
|
||||
import '../functions/encrypt_decrypt.dart';
|
||||
import '../functions/package_info.dart';
|
||||
|
||||
|
||||
class LoginController extends GetxController {
|
||||
final formKey = GlobalKey<FormState>();
|
||||
final formKeyAdmin = GlobalKey<FormState>();
|
||||
@@ -48,35 +47,12 @@ class LoginController extends GetxController {
|
||||
var dev = '';
|
||||
@override
|
||||
void onInit() async {
|
||||
await getJWT();
|
||||
// Log.print('box.read(BoxName.isTest): ${box.read(BoxName.isTest)}');
|
||||
box.write(BoxName.countryCode, 'Syria');
|
||||
FirebaseMessagesController().getToken();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
// getAppTester() async {
|
||||
// var res = await CRUD().get(
|
||||
// link: AppLink.getTesterApp,
|
||||
// payload: {'appPlatform': AppInformation.appName});
|
||||
// if (res != 'failure') {
|
||||
// var d = jsonDecode(res);
|
||||
|
||||
// isTest = int.parse(d['message'][0]['isTest'].toString());
|
||||
// box.write(BoxName.isTest, isTest);
|
||||
// update();
|
||||
// } else {
|
||||
// box.write(BoxName.isTest, '1');
|
||||
// update();
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// updateAppTester(String appPlatform) async {
|
||||
// await CRUD().post(
|
||||
// link: AppLink.updateTesterApp, payload: {'appPlatform': appPlatform});
|
||||
// }
|
||||
|
||||
void saveAgreementTerms() {
|
||||
box.write(BoxName.agreeTerms, 'agreed');
|
||||
update();
|
||||
@@ -87,7 +63,6 @@ class LoginController extends GetxController {
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// LoginController — دوال إدارة الـ JWT
|
||||
// ───────────────────────────────────────────────────────────────
|
||||
@@ -104,70 +79,134 @@ class LoginController extends GetxController {
|
||||
// • firstTimeLoadKey == false ← مستخدم موجود → loginJwtRider
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
Future<void> getJWT() async {
|
||||
dev = Platform.isAndroid ? 'android' : 'ios';
|
||||
// إذا كان التوكن الحالي لا يزال صالحاً، لا داعي لطلب واحد جديد
|
||||
if (isTokenValid()) {
|
||||
Log.print("JWT is still valid. Skipping request.");
|
||||
return;
|
||||
}
|
||||
|
||||
// تأكد إن البصمة محدّثة قبل أي طلب
|
||||
await DeviceHelper.getDeviceFingerprint();
|
||||
final String fp = box.read(BoxName.deviceFpEncrypted) ?? '';
|
||||
try {
|
||||
dev = Platform.isAndroid ? 'android' : 'ios';
|
||||
|
||||
if (box.read(BoxName.firstTimeLoadKey).toString() != 'false') {
|
||||
// ── أول تسجيل ─────────────────────────────────────────
|
||||
// نرسل البصمة المشفرة مع باقي البيانات
|
||||
// السيرفر سيعمل hash لها ويخزنها في JWT payload
|
||||
var payload = {
|
||||
'id': box.read(BoxName.passengerID) ?? AK.newId,
|
||||
'password': AK.passnpassenger,
|
||||
'aud': '${AK.allowed}$dev',
|
||||
'fingerPrint': fp,
|
||||
};
|
||||
// تأكد إن البصمة محدّثة قبل أي طلب
|
||||
await DeviceHelper.getDeviceFingerprint();
|
||||
final String fp = box.read(BoxName.deviceFpEncrypted) ?? '';
|
||||
|
||||
var response = await http.post(
|
||||
Uri.parse(AppLink.loginFirstTime),
|
||||
body: payload,
|
||||
);
|
||||
Log.print('AppLink.loginFirstTime: ${AppLink.loginFirstTime}');
|
||||
if (box.read(BoxName.firstTimeLoadKey).toString() != 'false') {
|
||||
// ── أول تسجيل ─────────────────────────────────────────
|
||||
// نرسل البصمة المشفرة مع باقي البيانات
|
||||
// السيرفر سيعمل hash لها ويخزنها في JWT payload
|
||||
var payload = {
|
||||
'id': box.read(BoxName.passengerID) ?? AK.newId,
|
||||
'password': AK.passnpassenger,
|
||||
'aud': '${AK.allowed}$dev',
|
||||
'fingerPrint': fp,
|
||||
};
|
||||
|
||||
Log.print('payload: $payload');
|
||||
Log.print('response: $response');
|
||||
var response = await http.post(
|
||||
Uri.parse(AppLink.loginFirstTime),
|
||||
body: payload,
|
||||
);
|
||||
Log.print('AppLink.loginFirstTime: ${AppLink.loginFirstTime}');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
final String jwt = decoded['jwt'];
|
||||
Log.print('payload: $payload');
|
||||
Log.print('response: $response');
|
||||
|
||||
// نشفر الـ JWT بالتشفير الثلاثي قبل التخزين في GetStorage
|
||||
box.write(BoxName.jwt, c(jwt));
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
final String? jwt =
|
||||
decoded['data'] != null ? decoded['data']['jwt'] : (decoded['message'] != null ? decoded['message']['jwt'] : decoded['jwt']);
|
||||
|
||||
if (jwt != null) {
|
||||
// نشفر الـ JWT بالتشفير الثلاثي قبل التخزين في GetStorage
|
||||
box.write(BoxName.jwt, c(jwt));
|
||||
}
|
||||
|
||||
await EncryptionHelper.initialize();
|
||||
}
|
||||
} else {
|
||||
// ── مستخدم موجود: تجديد التوكن ────────────────────────
|
||||
await EncryptionHelper.initialize();
|
||||
|
||||
var payload = {
|
||||
'id': box.read(BoxName.passengerID),
|
||||
'fingerPrint': fp,
|
||||
'aud': '${AK.allowed}$dev',
|
||||
};
|
||||
|
||||
var response = await http.post(
|
||||
Uri.parse(AppLink.loginJwtRider),
|
||||
body: payload,
|
||||
);
|
||||
Log.print('AppLink.loginJwtRider: ${AppLink.loginJwtRider}');
|
||||
|
||||
Log.print('payload: $payload');
|
||||
Log.print('response: ${response.body}');
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
final String? jwt =
|
||||
decoded['data'] != null ? decoded['data']['jwt'] : (decoded['message'] != null ? decoded['message']['jwt'] : decoded['jwt']);
|
||||
|
||||
if (jwt != null) {
|
||||
box.write(BoxName.jwt, c(jwt));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ── مستخدم موجود: تجديد التوكن ────────────────────────
|
||||
await EncryptionHelper.initialize();
|
||||
|
||||
var payload = {
|
||||
'id': box.read(BoxName.passengerID),
|
||||
'fingerPrint': fp,
|
||||
'aud': '${AK.allowed}$dev',
|
||||
};
|
||||
|
||||
var response = await http.post(
|
||||
Uri.parse(AppLink.loginJwtRider),
|
||||
body: payload,
|
||||
);
|
||||
Log.print('AppLink.loginJwtRider: ${AppLink.loginJwtRider}');
|
||||
|
||||
Log.print('payload: $payload');
|
||||
Log.print('response: ${response.body}');
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
final String jwt = decoded['jwt'];
|
||||
|
||||
box.write(BoxName.jwt, c(jwt));
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print('Error in getJWT: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// getJwtWallet: الحصول على توكن لسيرفر المدفوعات
|
||||
// التحقق من صلاحية التوكن يدوياً (بدون مكاتب خارجية)
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
bool isTokenValid() {
|
||||
try {
|
||||
final String? encryptedJwt = box.read(BoxName.jwt);
|
||||
if (encryptedJwt == null || encryptedJwt.isEmpty) {
|
||||
Log.print("isTokenValid: No token found in storage.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1. فك تشفير التوكن المخزن
|
||||
final String jwtFull = r(encryptedJwt).toString();
|
||||
final String jwt = jwtFull.split(Env.addd)[0];
|
||||
|
||||
final parts = jwt.split('.');
|
||||
if (parts.length != 3) {
|
||||
Log.print("isTokenValid: Invalid JWT format (parts: ${parts.length})");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. فك Base64 للجزء الأوسط (Payload)
|
||||
String payloadPart = parts[1];
|
||||
while (payloadPart.length % 4 != 0) {
|
||||
payloadPart += '=';
|
||||
}
|
||||
|
||||
final String decodedPayload = utf8.decode(base64Url.decode(payloadPart));
|
||||
final Map<String, dynamic> payload = jsonDecode(decodedPayload);
|
||||
|
||||
if (!payload.containsKey('exp')) {
|
||||
Log.print("isTokenValid: No 'exp' claim in token.");
|
||||
return false;
|
||||
}
|
||||
|
||||
final int exp = payload['exp'];
|
||||
final int now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
|
||||
Log.print("isTokenValid: Now=$now, Exp=$exp, Diff=${exp - now}s");
|
||||
|
||||
// اعتبار التوكن منتهي قبل دقيقة للأمان
|
||||
final bool valid = exp > (now + 60);
|
||||
Log.print("isTokenValid: Result=$valid");
|
||||
return valid;
|
||||
} catch (e) {
|
||||
Log.print("isTokenValid Error: $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// الفرق عن getJWT:
|
||||
// • يستخدم endpoint مختلف (loginWallet)
|
||||
@@ -197,11 +236,15 @@ class LoginController extends GetxController {
|
||||
Log.print('response wallet: ${response.body}');
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body);
|
||||
final String jwt = decoded['jwt'];
|
||||
final String hmac = decoded['hmac'];
|
||||
final String? jwt =
|
||||
decoded['data'] != null ? decoded['data']['jwt'] : decoded['jwt'];
|
||||
final String? hmac =
|
||||
decoded['data'] != null ? decoded['data']['hmac'] : decoded['hmac'];
|
||||
|
||||
// نخزن الـ hmac للاستخدام في X-HMAC-Auth header
|
||||
box.write(BoxName.hmac, hmac);
|
||||
if (hmac != null) {
|
||||
// نخزن الـ hmac للاستخدام في X-HMAC-Auth header
|
||||
box.write(BoxName.hmac, hmac);
|
||||
}
|
||||
|
||||
// wallet JWT يُرجَع مباشرة دون تشفير ثلاثي
|
||||
return jwt;
|
||||
@@ -213,8 +256,8 @@ class LoginController extends GetxController {
|
||||
Future<void> loginUsingCredentials(String passengerID, String email) async {
|
||||
isloading = true;
|
||||
update();
|
||||
await getJWT();
|
||||
|
||||
// await getJWT();
|
||||
Log.print("LoginController.loginUsingCredentials: ");
|
||||
try {
|
||||
// 1) استعلام تسجيل الدخول
|
||||
final res = await CRUD().get(
|
||||
@@ -227,16 +270,16 @@ class LoginController extends GetxController {
|
||||
},
|
||||
);
|
||||
|
||||
if (res == 'Failure') {
|
||||
// 2) فك JSON مرة واحدة والتحقق من النجاح
|
||||
final decoded = jsonDecode(res);
|
||||
if (decoded is! Map || decoded.isEmpty) return;
|
||||
|
||||
if (decoded['status'] == 'failure' || decoded['status'] == 'Failure') {
|
||||
Get.snackbar("User does not exist.".tr, '',
|
||||
backgroundColor: Colors.red);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) فك JSON مرة واحدة
|
||||
final decoded = jsonDecode(res);
|
||||
if (decoded is! Map || decoded.isEmpty) return;
|
||||
|
||||
final status = (decoded['status'] ?? '').toString();
|
||||
final data = (decoded['data'] as List?)?.firstOrNull as Map? ?? {};
|
||||
if (status != 'success' || data['verified'].toString() != '1') {
|
||||
@@ -263,47 +306,62 @@ class LoginController extends GetxController {
|
||||
// مهم: تأكد من passengerID في الـ box
|
||||
box.write(BoxName.passengerID, passengerID);
|
||||
|
||||
// 4) تنفيذ العمليات بالتوازي: getTokens + fingerprint محلي
|
||||
final results = await Future.wait([
|
||||
CRUD().get(
|
||||
link: AppLink.getTokens, payload: {'passengerID': passengerID}),
|
||||
DeviceHelper.getDeviceFingerprint(),
|
||||
]);
|
||||
// 4) فحص ما إذا كان التوكن موجوداً في رد الـ Login المدمج (V3 Optimization)
|
||||
String? serverFCM;
|
||||
String? serverFP;
|
||||
|
||||
final tokenResp = results[0];
|
||||
final localFP = (results[1] ?? '').toString();
|
||||
var userData = decoded['data'] is List
|
||||
? (decoded['data'] as List).firstOrNull
|
||||
: decoded['data'];
|
||||
if (userData is Map && userData['fcm_token'] != null) {
|
||||
serverFCM = userData['fcm_token'].toString();
|
||||
serverFP = userData['fcm_fingerprint']?.toString() ?? '';
|
||||
Log.print(
|
||||
"✅ FCM Token found in Login response. Skipping separate getTokens call.");
|
||||
}
|
||||
|
||||
// إذا لم يكن موجوداً (توافقية قديمة)، نقوم بطلبه
|
||||
String? tokenResp;
|
||||
if (serverFCM == null) {
|
||||
tokenResp = await CRUD().get(
|
||||
link: AppLink.getTokens, payload: {'passengerID': passengerID});
|
||||
}
|
||||
|
||||
final localFP = (await DeviceHelper.getDeviceFingerprint()).toString();
|
||||
await storage.write(key: BoxName.fingerPrint, value: localFP);
|
||||
await box.write(BoxName.firstTimeLoadKey, 'false');
|
||||
|
||||
// ── 5. المقارنة: FCM token + fingerprint ──────────────────────
|
||||
if (email != '962798583052@intaleqapp.com' && tokenResp != 'failure') {
|
||||
final tokenJson = jsonDecode(tokenResp);
|
||||
final serverData = tokenJson['message'] as Map?; // null = أول تسجيل
|
||||
|
||||
if (serverData != null) {
|
||||
final serverFCM = serverData['token']?.toString() ?? '';
|
||||
final serverFP = serverData['fingerPrint']?.toString() ?? '';
|
||||
// ── 5. المقارنة: FCM token + fingerprint ──────────────────────
|
||||
if (email != '962798583052@intaleqapp.com') {
|
||||
if (serverFCM == null &&
|
||||
tokenResp != null &&
|
||||
tokenResp != 'failure' &&
|
||||
tokenResp != 'error') {
|
||||
final tokenJson = jsonDecode(tokenResp);
|
||||
final serverData = tokenJson['data'] ?? tokenJson['message'];
|
||||
if (serverData is Map) {
|
||||
serverFCM = serverData['token']?.toString() ?? '';
|
||||
serverFP = serverData['fingerPrint']?.toString() ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
if (serverFCM != null && serverFCM.isNotEmpty) {
|
||||
final localFCM = (box.read(BoxName.tokenFCM) ?? '').toString();
|
||||
|
||||
// ── اختلاف أي منهما = جهاز مختلف أو تثبيت جديد ─────────
|
||||
final fcmChanged = serverFCM.isNotEmpty && serverFCM != localFCM;
|
||||
final fpChanged = serverFP.isNotEmpty && serverFP != localFP;
|
||||
final fcmChanged = serverFCM != localFCM;
|
||||
final fpChanged =
|
||||
serverFP != null && serverFP.isNotEmpty && serverFP != localFP;
|
||||
|
||||
if (fcmChanged || fpChanged) {
|
||||
// final goVerify = await _confirmDeviceChangeDialog();
|
||||
// if (goVerify == true) {
|
||||
mySnackbarInfo('Device Change Detected'.tr);
|
||||
//
|
||||
await Get.to(() => OtpVerificationPage(
|
||||
phone: data['phone'].toString(),
|
||||
deviceToken: localFP,
|
||||
token: tokenResp,
|
||||
ptoken: serverFCM, // نمرر FCM القديم للـ OTP controller
|
||||
token: tokenResp ?? '',
|
||||
ptoken: serverFCM ?? '', // نمرر FCM القديم للـ OTP controller
|
||||
));
|
||||
return; // لا تكمل — الـ OTP controller يتولى الانتقال
|
||||
// }
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user