Update: 2026-06-12 22:40:40

This commit is contained in:
Hamza-Ayed
2026-06-12 22:40:40 +03:00
parent f907212c57
commit 0ae368dbc8
24 changed files with 1197 additions and 303 deletions

View File

@@ -398,6 +398,7 @@ static String get updateTesterApp => "$auth/Tester/updateTesterApp.php";
static String get signUp => "$auth/signup.php";
static String get sendVerifyEmail => "$auth/sendVerifyEmail.php";
static String get loginFromGooglePassenger => "$auth/loginFromGooglePassenger.php";
static String get loginUsingCredentialsWithoutGooglePassenger => "$auth/loginUsingCredentialsWithoutGooglePassenger.php";
static String get checkPhoneNumberISVerfiedPassenger =>
"$auth/checkPhoneNumberISVerfiedPassenger.php";

View File

@@ -25,6 +25,7 @@ import 'package:location/location.dart';
import '../../env/env.dart';
import '../../print.dart';
import '../../views/auth/otp_token_page.dart';
import '../functions/country_logic.dart';
import '../functions/encrypt_decrypt.dart';
import '../functions/package_info.dart';
@@ -48,7 +49,8 @@ class LoginController extends GetxController {
@override
void onInit() async {
// Log.print('box.read(BoxName.isTest): ${box.read(BoxName.isTest)}');
box.write(BoxName.countryCode, 'Syria');
// كشف الدولة تلقائياً عبر الموقع الجغرافي
await CountryLogic.initializeCountry();
FirebaseMessagesController().getToken();
super.onInit();
}
@@ -271,8 +273,6 @@ class LoginController extends GetxController {
final res = await CRUD().get(
link: AppLink.loginFromGooglePassenger,
payload: {
'email': email,
'id': passengerID, // استخدم المعامل مباشرة
'platform': Platform.isAndroid ? 'android' : 'ios',
'appName': AppInformation.appName,
},
@@ -385,7 +385,7 @@ class LoginController extends GetxController {
"inviteCode": invite,
"passengerID": passengerID,
});
// سجل الدعوة أيضاً في النظام الموحد الجديد
await CRUD().post(link: AppLink.addUnifiedInvite, payload: {
"inviter_code": invite,
@@ -445,46 +445,48 @@ class LoginController extends GetxController {
// );
// }
void login() async {
void loginTester() async {
isloading = true;
update();
var res =
await CRUD().get(link: AppLink.loginFromGooglePassenger, payload: {
'email': (emailController.text),
'id': passwordController.text,
"platform": Platform.isAndroid ? 'android' : 'ios',
"appName": AppInformation.appName,
});
try {
var fingerPrint = await DeviceHelper.getDeviceFingerprint();
var payload = {
'email': emailController.text.trim(),
'password': passwordController.text.trim(),
'fingerPrint': fingerPrint,
'aud': '${AK.allowed}${Platform.isAndroid ? 'android' : 'ios'}',
};
var response = await http.post(
Uri.parse(AppLink.loginUsingCredentialsWithoutGooglePassenger),
body: payload,
);
if (response.statusCode == 200 || response.statusCode == 201) {
var jsonDecoeded = jsonDecode(response.body);
if (jsonDecoeded['status'] == 'success' && jsonDecoeded['data'][0]['verified'].toString() == '1') {
var d = jsonDecoeded['data'][0];
var jwt = jsonDecoeded['jwt'];
// حفظ التوكن
if (jwt != null) {
box.write(BoxName.jwt, c(jwt));
await storage.write(key: BoxName.jwt, value: c(jwt));
}
isloading = false;
update();
if (res == 'Failure') {
//Failure
Get.offAll(() => LoginPage());
isloading = false;
update();
// Get.snackbar("User does not exist.".tr, '', backgroundColor: Colors.red);
} else {
var jsonDecoeded = jsonDecode(res);
if (jsonDecoeded.isNotEmpty) {
if (jsonDecoeded['status'] == 'success' &&
jsonDecoeded['data'][0]['verified'].toString() == '1') {
//
box.write(BoxName.isVerified, '1');
box.write(BoxName.email, jsonDecoeded['data'][0]['email']);
box.write(BoxName.name, jsonDecoeded['data'][0]['first_name']);
box.write(BoxName.phone, jsonDecoeded['data'][0]['phone']);
box.write(BoxName.passengerID, passwordController.text);
// var token = await CRUD().get(link: AppLink.getTokens, payload: {
// 'passengerID': box.read(BoxName.passengerID).toString()
// });
// await updateAppTester(AppInformation.appName);
box.write(BoxName.email, d['email']);
box.write(BoxName.name, d['first_name']);
box.write(BoxName.phone, d['phone']);
box.write(BoxName.passengerID, d['id']);
box.write(BoxName.isTest, '1');
await storage.write(key: BoxName.fingerPrint, value: fingerPrint);
Get.offAll(() => const MapPagePassenger());
} else {
// Get.offAll(() => SmsSignupEgypt());
// Get.snackbar(jsonDecoeded['status'], jsonDecoeded['data'],
// backgroundColor: Colors.redAccent);
Get.offAll(() => LoginPage());
isloading = false;
update();
}
@@ -492,6 +494,10 @@ class LoginController extends GetxController {
isloading = false;
update();
}
} catch (e) {
Log.print("Tester Login Error: $e");
isloading = false;
update();
}
}

View File

@@ -1,5 +1,9 @@
import 'package:geolocator/geolocator.dart';
import 'package:intaleq_maps/intaleq_maps.dart';
import 'package:siro_rider/constant/box_name.dart';
import 'package:siro_rider/constant/country_polygons.dart';
import 'package:siro_rider/main.dart';
import '../../print.dart';
class CountryLogic {
/// Formats the phone number based on the country's dialing rules.
@@ -88,7 +92,77 @@ class CountryLogic {
return formatPhone(cleanPhone, 'Jordan');
}
final country = box.read(BoxName.countryCode) ?? 'Syria';
final country = box.read(BoxName.countryCode) ?? 'Jordan';
return formatPhone(cleanPhone, country);
}
/// التحقق مما إذا كانت نقطة داخل مضلع (Ray Casting Algorithm)
static bool _isPointInPolygon(LatLng point, List<LatLng> polygon) {
int intersections = 0;
for (int i = 0; i < polygon.length; i++) {
final v1 = polygon[i];
final v2 = polygon[(i + 1) % polygon.length];
if ((v1.latitude <= point.latitude && v2.latitude > point.latitude) ||
(v2.latitude <= point.latitude && v1.latitude > point.latitude)) {
final t = (point.latitude - v1.latitude) / (v2.latitude - v1.latitude);
if (point.longitude <
v1.longitude + t * (v2.longitude - v1.longitude)) {
intersections++;
}
}
}
return intersections % 2 != 0;
}
/// كشف الدولة تلقائياً عبر الموقع الجغرافي باستخدام مضلعات CountryPolygons
static Future<String?> detectByLocation() async {
try {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) return null;
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) return null;
}
if (permission == LocationPermission.deniedForever) return null;
final pos = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.low,
timeLimit: const Duration(seconds: 8),
);
final point = LatLng(pos.latitude, pos.longitude);
if (_isPointInPolygon(point, CountryPolygons.jordanBoundary)) {
return 'Jordan';
}
if (_isPointInPolygon(point, CountryPolygons.syriaBoundary)) {
return 'Syria';
}
if (_isPointInPolygon(point, CountryPolygons.egyptBoundary)) {
return 'Egypt';
}
} catch (e) {
Log.print('[CountryLogic] detectByLocation error: $e');
}
return null;
}
/// تهيئة الدولة عند بدء التطبيق
static Future<void> initializeCountry() async {
final existing = box.read(BoxName.countryCode);
if (existing != null && existing.toString().isNotEmpty) {
Log.print('[CountryLogic] الدولة موجودة مسبقاً: $existing');
return;
}
Log.print('[CountryLogic] كشف الدولة عبر الموقع...');
final country = await detectByLocation();
if (country != null) {
box.write(BoxName.countryCode, country);
Log.print('[CountryLogic] تم الكشف: $country');
} else {
box.write(BoxName.countryCode, 'Jordan');
Log.print('[CountryLogic] افتراضي: Jordan');
}
}
}

View File

@@ -75,14 +75,39 @@ class CRUD {
}
// ─────────────────────────────────────────────────────────────
// دالة مساعدة خاصة: يجيب البصمة المشفرة من GetStorage
// هي نفس القيمة المرسلة في login وعُملها hash في JWT payload
// السيرفر يعمل: sha256(X-Device-FP + FP_PEPPER) == JWT.fingerPrint
// دالة مساعدة خاصة: تجيب البصمة المشفرة من GetStorage
// ─────────────────────────────────────────────────────────────
String _getFpHeader() {
return box.read(BoxName.deviceFpEncrypted)?.toString() ?? '';
}
// ─────────────────────────────────────────────────────────────
// دالة مساعدة خاصة: تقرأ JWT من FlutterSecureStorage (آمن)
// بدلاً من GetStorage (غير مشفر)
// ─────────────────────────────────────────────────────────────
Future<String> _getJwt() async {
try {
final String? encryptedJwt = await storage.read(key: BoxName.jwt);
if (encryptedJwt == null || encryptedJwt.isEmpty) {
// Fallback إلى GetStorage للتوافقية
final String? fallback = box.read(BoxName.jwt);
if (fallback != null) {
return r(fallback).toString().split(Env.addd)[0];
}
return '';
}
return r(encryptedJwt).toString().split(Env.addd)[0];
} catch (e) {
Log.print('Error reading JWT from SecureStorage: $e');
// Fallback
final String? fallback = box.read(BoxName.jwt);
if (fallback != null) {
return r(fallback).toString().split(Env.addd)[0];
}
return '';
}
}
/// Centralized private method to handle all API requests.
/// Includes retry logic, network checking, and standardized error handling.
Future<dynamic> _makeRequest({
@@ -169,7 +194,7 @@ class CRUD {
required String link,
Map<String, dynamic>? payload,
}) async {
String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
final token = await _getJwt();
final headers = {
'Content-Type': 'application/x-www-form-urlencoded',
@@ -193,14 +218,14 @@ class CRUD {
required String link,
Map<String, dynamic>? payload,
}) async {
final token = await _getJwt();
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]}',
'Authorization': 'Bearer $token',
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
},
);
@@ -548,7 +573,6 @@ class CRUD {
return response.statusCode;
}
Future<dynamic> postPayMob({
required String link,
Map<String, dynamic>? payload,

View File

@@ -4,12 +4,9 @@ import 'package:siro_rider/constant/box_name.dart';
import 'package:siro_rider/constant/info.dart';
import 'package:siro_rider/controller/auth/login_controller.dart';
import 'package:jwt_decoder/jwt_decoder.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import '../../constant/char_map.dart';
import '../../constant/links.dart';
import '../../main.dart';
import '../../print.dart';
import 'crud.dart';
import 'encrypt_decrypt.dart';
@@ -24,23 +21,25 @@ class SecureStorage {
}
}
const List<String> keysToFetch = [
'serverPHP',
// 'seferAlexandriaServer',
// 'seferPaymentServer',
// 'seferCairoServer',
// 'seferGizaServer',
];
class AppInitializer {
List<Map<String, dynamic>> links = [];
/// تقرأ JWT من FlutterSecureStorage أولاً، مع fallback إلى GetStorage
Future<String?> _readJwt() async {
try {
final secureJwt = await storage.read(key: 'jwt');
if (secureJwt != null && secureJwt.isNotEmpty) return secureJwt;
} catch (_) {}
return box.read(BoxName.jwt);
}
Future<void> initializeApp() async {
if (box.read(BoxName.jwt) == null) {
final jwt = await _readJwt();
if (jwt == null) {
await LoginController().getJWT();
} else {
bool isTokenExpired = JwtDecoder.isExpired(
r(box.read(BoxName.jwt)).toString().split(AppInformation.addd)[0]);
bool isTokenExpired =
JwtDecoder.isExpired(r(jwt).toString().split(AppInformation.addd)[0]);
if (isTokenExpired) {
await LoginController().getJWT();

View File

@@ -419,7 +419,7 @@ class LoginPage extends StatelessWidget {
onPressed: () {
if (controller.formKey.currentState!
.validate()) {
controller.login();
controller.loginTester();
}
},
),

View File

@@ -160,7 +160,7 @@ class AuthScreen extends StatelessWidget {
testerEmailController.text;
controller.passwordController.text =
testerPasswordController.text;
controller.login();
controller.loginTester();
Navigator.of(dialogContext).pop();
}
},