Update: 2026-07-22 00:27:49

This commit is contained in:
Hamza-Ayed
2026-07-22 00:27:50 +03:00
parent 699b380f2b
commit cf95455bcf
5 changed files with 108 additions and 65 deletions
@@ -119,7 +119,7 @@ class PhoneAuthHelper {
var res = await CRUD() var res = await CRUD()
// this for register and login // // this for register and login //
.post(link: "${AppLink.server}/ride/firebase/addToken.php", payload: { .post(link: "${AppLink.server}/ride/firebase/addToken.php", payload: {
'token': (box.read(BoxName.tokenFCM.toString())), 'token': (box.read(BoxName.tokenFCM)),
'passengerID': box.read(BoxName.passengerID).toString(), 'passengerID': box.read(BoxName.passengerID).toString(),
"fingerPrint": fingerPrint "fingerPrint": fingerPrint
}); });
@@ -190,6 +190,15 @@ void showUpdateDialog(BuildContext context) {
class DeviceHelper { class DeviceHelper {
static Future<String> getDeviceFingerprint() async { static Future<String> getDeviceFingerprint() async {
// ── التحقق من وجود بصمة مخزّنة مسبقاً ──────────────────────
// AES-GCM يستخدم IV عشوائي كل مرة، فالتشفير ينتج نتيجة مختلفة
// حتى لو النص الأصلي نفسه. لذلك نخزّن البصمة المشفرة أول مرة
// ونرجعها من التخزين في كل مرة بعدها لضمان الثبات.
final String? cachedFp = box.read(BoxName.deviceFpEncrypted);
if (cachedFp != null && cachedFp.isNotEmpty) {
return cachedFp;
}
final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
var deviceData; var deviceData;
@@ -77,7 +77,8 @@ class MapMenuWidget extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: _kBg.withValues(alpha: 0.88), color: _kBg.withValues(alpha: 0.88),
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: _kCyan.withValues(alpha: 0.25), width: 1), border: Border.all(
color: _kCyan.withValues(alpha: 0.25), width: 1),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: _kCyan.withValues(alpha: 0.12), color: _kCyan.withValues(alpha: 0.12),
@@ -121,7 +122,8 @@ class MapMenuWidget extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: _kBg.withValues(alpha: 0.97), color: _kBg.withValues(alpha: 0.97),
border: Border( border: Border(
right: BorderSide(color: _kCyan.withValues(alpha: 0.12), width: 1), right:
BorderSide(color: _kCyan.withValues(alpha: 0.12), width: 1),
), ),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
@@ -149,11 +151,12 @@ class MapMenuWidget extends StatelessWidget {
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 4), horizontal: 12, vertical: 4),
children: [ children: [
// MenuListItem( MenuListItem(
// title: 'Mawasalati'.tr, title: 'Mawasalati'.tr,
// icon: Icons.directions_bus_filled_rounded, icon: Icons.directions_bus_filled_rounded,
// onTap: () => Get.to(() => const TransitHomePage()), onTap: () =>
// ), Get.to(() => const TransitHomePage()),
),
MenuListItem( MenuListItem(
title: 'My Balance'.tr, title: 'My Balance'.tr,
icon: Icons.account_balance_wallet_outlined, icon: Icons.account_balance_wallet_outlined,
@@ -263,8 +266,8 @@ class MapMenuWidget extends StatelessWidget {
_kAmber.withValues(alpha: 0.12), _kAmber.withValues(alpha: 0.12),
], ],
), ),
border: border: Border.all(
Border.all(color: _kCyan.withValues(alpha: 0.35), width: 1.5), color: _kCyan.withValues(alpha: 0.35), width: 1.5),
), ),
child: Icon(Icons.person_rounded, color: _kCyan, size: 28), child: Icon(Icons.person_rounded, color: _kCyan, size: 28),
), ),
@@ -281,7 +284,7 @@ class MapMenuWidget extends StatelessWidget {
border: Border.all(color: _kBg, width: 2), border: Border.all(color: _kBg, width: 2),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: const Color(0xFF00E676).withValues(alpha: 0.5), color: const Color(0xFF00E676).withValues(alpha: 0.5),
blurRadius: 6, blurRadius: 6,
), ),
], ],
@@ -383,8 +386,7 @@ class MapMenuWidget extends StatelessWidget {
driverAppUrl = driverAppUrl =
'https://play.google.com/store/apps/details?id=com.siro_driver'; 'https://play.google.com/store/apps/details?id=com.siro_driver';
} else if (defaultTargetPlatform == TargetPlatform.iOS) { } else if (defaultTargetPlatform == TargetPlatform.iOS) {
driverAppUrl = driverAppUrl = 'https://apps.apple.com/st/app/siro-driver/id6482995159';
'https://apps.apple.com/st/app/siro-driver/id6482995159';
} else { } else {
return; return;
} }
+32 -20
View File
@@ -2,12 +2,11 @@
/** /**
* generate_mock_data.php * generate_mock_data.php
* Standalone JWT generator — ZERO external dependencies. * Standalone JWT generator — ZERO external dependencies.
* Generates HS256 JWT using pure PHP (no composer/vendor needed). * Generates HS256 JWT matching Siro JwtService verification logic.
*/ */
// Show errors so we can debug
error_reporting(E_ALL); error_reporting(E_ALL);
ini_set('display_errors', '1'); ini_set('display_errors', '0');
// ── Load .env ── // ── Load .env ──
$loadedFiles = []; $loadedFiles = [];
@@ -27,27 +26,21 @@ function loadEnvFile(string $path): void {
} }
} }
// Try multiple .env locations
loadEnvFile('/home/location/env/.env'); loadEnvFile('/home/location/env/.env');
loadEnvFile(dirname(__DIR__) . '/backend/.env'); loadEnvFile(dirname(__DIR__) . '/backend/.env');
// ── Resolve JWT secret ──
function getJwtSecret(): string { function getJwtSecret(): string {
// Priority 1: key file
$keyPath = getenv('JWT_SECRET_KEY_PATH'); $keyPath = getenv('JWT_SECRET_KEY_PATH');
if ($keyPath && file_exists($keyPath)) { if ($keyPath && file_exists($keyPath)) {
return trim(file_get_contents($keyPath)); return trim(file_get_contents($keyPath));
} }
// Priority 2: JWT_SECRET_KEY env var
$key = getenv('JWT_SECRET_KEY') ?: ($_ENV['JWT_SECRET_KEY'] ?? ''); $key = getenv('JWT_SECRET_KEY') ?: ($_ENV['JWT_SECRET_KEY'] ?? '');
if ($key) return $key; if ($key) return $key;
// Priority 3: JWT_SECRET env var (backend .env uses this)
$key = getenv('JWT_SECRET') ?: ($_ENV['JWT_SECRET'] ?? ''); $key = getenv('JWT_SECRET') ?: ($_ENV['JWT_SECRET'] ?? '');
if ($key) return $key; if ($key) return $key;
return ''; return '';
} }
// ── Pure PHP HS256 JWT (no libraries needed) ──
function base64url_encode(string $data): string { function base64url_encode(string $data): string {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
} }
@@ -59,21 +52,40 @@ function generate_jwt(string $secret, array $payload): string {
return "$header.$body.$sig"; return "$header.$body.$sig";
} }
// ── Main ──
$secret = getJwtSecret(); $secret = getJwtSecret();
if (empty($secret)) { if (empty($secret)) {
echo json_encode(['error' => 'JWT secret not found in any .env', 'files_checked' => $loadedFiles, 'jwt' => '']); echo json_encode(['error' => 'JWT secret not found in any .env', 'jwt' => '']);
// Do not exit with 1 so Node.js can print the JSON error
exit(0); exit(0);
} }
$driverId = $argv[1] ?? '999999'; $userId = $argv[1] ?? '999999';
$role = $argv[2] ?? 'driver';
$jwt = generate_jwt($secret, [ $aud = ($role === 'driver') ? (getenv('allowedDriver2') ?: 'driver-app:ios') : (getenv('allowed2') ?: 'passenger-app:ios');
'sub' => (string)$driverId, $fpPepper = getenv('FP_PEPPER') ?: ($_ENV['FP_PEPPER'] ?? '');
'role' => 'driver', $fpHeader = 'mock_device_fp_' . $userId;
'iat' => time(), $fpInToken = $fpPepper ? hash('sha256', $fpHeader . $fpPepper) : null;
'exp' => time() + 86400,
$payload = [
'iss' => getenv('APP_ISSUER') ?: 'SiroApp',
'aud' => $aud,
'user_id' => (string)$userId,
'sub' => (string)$userId,
'role' => $role,
'token_type' => 'access',
'jti' => bin2hex(random_bytes(16)),
'iat' => time(),
'exp' => time() + 86400,
];
if ($fpInToken) {
$payload['fingerPrint'] = $fpInToken;
}
$jwt = generate_jwt($secret, $payload);
echo json_encode([
'user_id' => $userId,
'role' => $role,
'jwt' => $jwt,
'fp' => $fpHeader
]); ]);
echo json_encode(['driver_id' => $driverId, 'jwt' => $jwt]);
+52 -32
View File
@@ -35,23 +35,23 @@ function debug(msg) {
if (argv.debug) console.log(` [DEBUG] ${msg}`); if (argv.debug) console.log(` [DEBUG] ${msg}`);
} }
// Helper to get JWT for mock driver // Helper to get JWT and FP for mock user
function getMockDriverJwt(driverId) { function getMockJwt(userId, role = 'driver') {
try { try {
const output = execSync(`php generate_mock_data.php ${driverId}`, { const output = execSync(`php generate_mock_data.php ${userId} ${role}`, {
timeout: 5000 timeout: 5000
}).toString(); }).toString();
debug(`JWT output for driver ${driverId}: ${output.substring(0, 80)}...`); debug(`JWT output for ${role} ${userId}: ${output.substring(0, 80)}...`);
const jsonString = output.substring(output.indexOf('{')); const jsonString = output.substring(output.indexOf('{'));
const parsed = JSON.parse(jsonString); const parsed = JSON.parse(jsonString);
if (!parsed.jwt) { if (!parsed.jwt) {
console.error(`[Error] JWT generation returned empty for driver ${driverId}: ${output}`); console.error(`[Error] JWT generation returned empty for ${role} ${userId}: ${output}`);
return ''; return { jwt: '', fp: '' };
} }
return parsed.jwt; return { jwt: parsed.jwt, fp: parsed.fp || `mock_device_fp_${userId}` };
} catch (err) { } catch (err) {
console.error(`[Error] Failed to generate JWT for driver ${driverId}:`, err.message); console.error(`[Error] Failed to generate JWT for ${role} ${userId}:`, err.message);
return ''; return { jwt: '', fp: '' };
} }
} }
@@ -67,17 +67,17 @@ let metrics = {
async function simulateTrip(tripIndex) { async function simulateTrip(tripIndex) {
const driverId = 900000 + tripIndex; const driverId = 900000 + tripIndex;
const passengerId = 800000 + tripIndex; const passengerId = 800000 + tripIndex;
const driverJwt = getMockDriverJwt(driverId); const driverAuth = getMockJwt(driverId, 'driver');
const passengerAuth = getMockJwt(passengerId, 'passenger');
if (!driverJwt) { if (!driverAuth.jwt || !passengerAuth.jwt) {
console.error(`[Trip ${tripIndex}] Skipping — JWT generation failed for driver ${driverId}`); console.error(`[Trip ${tripIndex}] Skipping — JWT generation failed.`);
metrics.jwtErrors++; metrics.jwtErrors++;
metrics.failed++; metrics.failed++;
return false; return false;
} }
console.log(`[Trip ${tripIndex}] Starting... Driver: ${driverId}, Passenger: ${passengerId}`); console.log(`[Trip ${tripIndex}] Starting... Driver: ${driverId}, Passenger: ${passengerId}`);
debug(`JWT token (first 30 chars): ${driverJwt.substring(0, 30)}...`);
const tripStartTime = Date.now(); const tripStartTime = Date.now();
// 1. Connect Driver to Socket // 1. Connect Driver to Socket
@@ -88,7 +88,7 @@ async function simulateTrip(tripIndex) {
query: { query: {
driver_id: String(driverId), driver_id: String(driverId),
platform: 'android', platform: 'android',
jwt: driverJwt jwt: driverAuth.jwt
}, },
reconnection: false, reconnection: false,
timeout: 10000, timeout: 10000,
@@ -108,7 +108,6 @@ async function simulateTrip(tripIndex) {
socket.on('connect_error', (err) => { socket.on('connect_error', (err) => {
console.error(`[Trip ${tripIndex}] ❌ Socket Connection Error: ${err.message}`); console.error(`[Trip ${tripIndex}] ❌ Socket Connection Error: ${err.message}`);
debug(`Full error: ${JSON.stringify(err)}`);
metrics.connectionErrors++; metrics.connectionErrors++;
metrics.failed++; metrics.failed++;
finish(false); finish(false);
@@ -145,7 +144,11 @@ async function simulateTrip(tripIndex) {
`${argv.baseUrl}/ride/rides/add_ride.php`, `${argv.baseUrl}/ride/rides/add_ride.php`,
addRideFormData.toString(), addRideFormData.toString(),
{ {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Bearer ${passengerAuth.jwt}`,
'X-Device-FP': passengerAuth.fp
},
timeout: 10000 timeout: 10000
} }
); );
@@ -177,7 +180,11 @@ async function simulateTrip(tripIndex) {
`${argv.baseUrl}/ride/rides/acceptRide.php`, `${argv.baseUrl}/ride/rides/acceptRide.php`,
acceptFormData.toString(), acceptFormData.toString(),
{ {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Bearer ${driverAuth.jwt}`,
'X-Device-FP': driverAuth.fp
},
timeout: 10000 timeout: 10000
} }
); );
@@ -198,7 +205,18 @@ async function simulateTrip(tripIndex) {
if (steps === 3) { if (steps === 3) {
try { try {
const startFormData = new URLSearchParams({ id: String(rideId), driver_id: String(driverId), status: 'start' }); const startFormData = new URLSearchParams({ id: String(rideId), driver_id: String(driverId), status: 'start' });
await axios.post(`${argv.baseUrl}/ride/rides/start_ride.php`, startFormData.toString(), { timeout: 10000 }); await axios.post(
`${argv.baseUrl}/ride/rides/start_ride.php`,
startFormData.toString(),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Bearer ${driverAuth.jwt}`,
'X-Device-FP': driverAuth.fp
},
timeout: 10000
}
);
console.log(`[Trip ${tripIndex}] Started ride #${rideId}`); console.log(`[Trip ${tripIndex}] Started ride #${rideId}`);
} catch (e) { } catch (e) {
debug(`start_ride error: ${e.message}`); debug(`start_ride error: ${e.message}`);
@@ -209,7 +227,18 @@ async function simulateTrip(tripIndex) {
clearInterval(driveInterval); clearInterval(driveInterval);
try { try {
const finishFormData = new URLSearchParams({ id: String(rideId), driver_id: String(driverId), status: 'finish' }); const finishFormData = new URLSearchParams({ id: String(rideId), driver_id: String(driverId), status: 'finish' });
await axios.post(`${argv.baseUrl}/ride/rides/finish_ride_updates.php`, finishFormData.toString(), { timeout: 10000 }); await axios.post(
`${argv.baseUrl}/ride/rides/finish_ride_updates.php`,
finishFormData.toString(),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Bearer ${driverAuth.jwt}`,
'X-Device-FP': driverAuth.fp
},
timeout: 10000
}
);
console.log(`[Trip ${tripIndex}] ✅ Finished ride #${rideId}.`); console.log(`[Trip ${tripIndex}] ✅ Finished ride #${rideId}.`);
} catch (e) { } catch (e) {
debug(`finish_ride error: ${e.message}`); debug(`finish_ride error: ${e.message}`);
@@ -244,7 +273,6 @@ async function simulateTrip(tripIndex) {
} }
async function runTest() { async function runTest() {
// ── Pre-flight checks ──
console.log(`\n======================================================`); console.log(`\n======================================================`);
console.log(`🚀 Siro Stress Test`); console.log(`🚀 Siro Stress Test`);
console.log(`======================================================`); console.log(`======================================================`);
@@ -254,23 +282,18 @@ async function runTest() {
console.log(`🐛 Debug: ${argv.debug ? 'ON' : 'OFF'}`); console.log(`🐛 Debug: ${argv.debug ? 'ON' : 'OFF'}`);
console.log(`------------------------------------------------------`); console.log(`------------------------------------------------------`);
// Test 1: Can we generate JWT?
console.log(`\n🔑 Pre-flight: Testing JWT generation...`); console.log(`\n🔑 Pre-flight: Testing JWT generation...`);
const testJwt = getMockDriverJwt(999999); const testJwt = getMockJwt(999999, 'driver');
if (!testJwt) { if (!testJwt.jwt) {
console.error(`❌ FATAL: Cannot generate JWT tokens. Check that:\n` + console.error(`❌ FATAL: Cannot generate JWT tokens.`);
` - PHP is installed on this server\n` +
` - /home/location/env/.env or backend/.env has JWT_SECRET_KEY\n` +
` - loction_server/vendor/autoload.php exists (run composer install)`);
process.exit(1); process.exit(1);
} }
console.log(` ✅ JWT generation works.`); console.log(` ✅ JWT generation works.`);
// Test 2: Can we reach the socket server?
console.log(`\n🔌 Pre-flight: Testing socket connection...`); console.log(`\n🔌 Pre-flight: Testing socket connection...`);
const testResult = await new Promise((resolve) => { const testResult = await new Promise((resolve) => {
const testSocket = io(argv.socketUrl, { const testSocket = io(argv.socketUrl, {
query: { driver_id: '999999', platform: 'android', jwt: testJwt }, query: { driver_id: '999999', platform: 'android', jwt: testJwt.jwt },
reconnection: false, reconnection: false,
timeout: 5000, timeout: 5000,
forceNew: true forceNew: true
@@ -282,8 +305,6 @@ async function runTest() {
}); });
testSocket.on('connect_error', (err) => { testSocket.on('connect_error', (err) => {
console.error(` ❌ Socket connection failed: ${err.message}`); console.error(` ❌ Socket connection failed: ${err.message}`);
console.error(` 💡 Make sure the Location Server (driver_socket.php) is running on ${argv.socketUrl}`);
console.error(` 💡 Check with: ps aux | grep driver_socket`);
testSocket.disconnect(); testSocket.disconnect();
resolve(false); resolve(false);
}); });
@@ -303,7 +324,6 @@ async function runTest() {
process.exit(1); process.exit(1);
} }
// ── Run the actual test ──
console.log(`\n------------------------------------------------------`); console.log(`\n------------------------------------------------------`);
console.log(`🏁 Starting ${argv.trips} concurrent trips...\n`); console.log(`🏁 Starting ${argv.trips} concurrent trips...\n`);