From cf95455bcf1692e50bbbdb889596804b01a43782 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Wed, 22 Jul 2026 00:27:50 +0300 Subject: [PATCH] Update: 2026-07-22 00:27:49 --- .../lib/controller/auth/otp_controller.dart | 2 +- .../controller/functions/package_info.dart | 9 ++ .../home/map_widget.dart/map_menu_widget.dart | 26 +++--- stress_test/generate_mock_data.php | 52 +++++++----- stress_test/load_test.js | 84 ++++++++++++------- 5 files changed, 108 insertions(+), 65 deletions(-) diff --git a/siro_rider/lib/controller/auth/otp_controller.dart b/siro_rider/lib/controller/auth/otp_controller.dart index eaf22d77..5b5a8753 100644 --- a/siro_rider/lib/controller/auth/otp_controller.dart +++ b/siro_rider/lib/controller/auth/otp_controller.dart @@ -119,7 +119,7 @@ class PhoneAuthHelper { var res = await CRUD() // this for register and login // .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(), "fingerPrint": fingerPrint }); diff --git a/siro_rider/lib/controller/functions/package_info.dart b/siro_rider/lib/controller/functions/package_info.dart index 81db1446..37dcb926 100644 --- a/siro_rider/lib/controller/functions/package_info.dart +++ b/siro_rider/lib/controller/functions/package_info.dart @@ -190,6 +190,15 @@ void showUpdateDialog(BuildContext context) { class DeviceHelper { static Future getDeviceFingerprint() async { + // ── التحقق من وجود بصمة مخزّنة مسبقاً ────────────────────── + // AES-GCM يستخدم IV عشوائي كل مرة، فالتشفير ينتج نتيجة مختلفة + // حتى لو النص الأصلي نفسه. لذلك نخزّن البصمة المشفرة أول مرة + // ونرجعها من التخزين في كل مرة بعدها لضمان الثبات. + final String? cachedFp = box.read(BoxName.deviceFpEncrypted); + if (cachedFp != null && cachedFp.isNotEmpty) { + return cachedFp; + } + final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); var deviceData; diff --git a/siro_rider/lib/views/home/map_widget.dart/map_menu_widget.dart b/siro_rider/lib/views/home/map_widget.dart/map_menu_widget.dart index c35b09ad..5c2129e8 100644 --- a/siro_rider/lib/views/home/map_widget.dart/map_menu_widget.dart +++ b/siro_rider/lib/views/home/map_widget.dart/map_menu_widget.dart @@ -77,7 +77,8 @@ class MapMenuWidget extends StatelessWidget { decoration: BoxDecoration( color: _kBg.withValues(alpha: 0.88), 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( color: _kCyan.withValues(alpha: 0.12), @@ -121,7 +122,8 @@ class MapMenuWidget extends StatelessWidget { decoration: BoxDecoration( color: _kBg.withValues(alpha: 0.97), border: Border( - right: BorderSide(color: _kCyan.withValues(alpha: 0.12), width: 1), + right: + BorderSide(color: _kCyan.withValues(alpha: 0.12), width: 1), ), boxShadow: [ BoxShadow( @@ -149,11 +151,12 @@ class MapMenuWidget extends StatelessWidget { padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 4), children: [ - // MenuListItem( - // title: 'Mawasalati'.tr, - // icon: Icons.directions_bus_filled_rounded, - // onTap: () => Get.to(() => const TransitHomePage()), - // ), + MenuListItem( + title: 'Mawasalati'.tr, + icon: Icons.directions_bus_filled_rounded, + onTap: () => + Get.to(() => const TransitHomePage()), + ), MenuListItem( title: 'My Balance'.tr, icon: Icons.account_balance_wallet_outlined, @@ -263,8 +266,8 @@ class MapMenuWidget extends StatelessWidget { _kAmber.withValues(alpha: 0.12), ], ), - border: - Border.all(color: _kCyan.withValues(alpha: 0.35), width: 1.5), + border: Border.all( + color: _kCyan.withValues(alpha: 0.35), width: 1.5), ), child: Icon(Icons.person_rounded, color: _kCyan, size: 28), ), @@ -281,7 +284,7 @@ class MapMenuWidget extends StatelessWidget { border: Border.all(color: _kBg, width: 2), boxShadow: [ BoxShadow( - color: const Color(0xFF00E676).withValues(alpha: 0.5), + color: const Color(0xFF00E676).withValues(alpha: 0.5), blurRadius: 6, ), ], @@ -383,8 +386,7 @@ class MapMenuWidget extends StatelessWidget { driverAppUrl = 'https://play.google.com/store/apps/details?id=com.siro_driver'; } else if (defaultTargetPlatform == TargetPlatform.iOS) { - driverAppUrl = - 'https://apps.apple.com/st/app/siro-driver/id6482995159'; + driverAppUrl = 'https://apps.apple.com/st/app/siro-driver/id6482995159'; } else { return; } diff --git a/stress_test/generate_mock_data.php b/stress_test/generate_mock_data.php index 6a434797..8cf614a1 100644 --- a/stress_test/generate_mock_data.php +++ b/stress_test/generate_mock_data.php @@ -2,12 +2,11 @@ /** * generate_mock_data.php * 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); -ini_set('display_errors', '1'); +ini_set('display_errors', '0'); // ── Load .env ── $loadedFiles = []; @@ -27,27 +26,21 @@ function loadEnvFile(string $path): void { } } -// Try multiple .env locations loadEnvFile('/home/location/env/.env'); loadEnvFile(dirname(__DIR__) . '/backend/.env'); -// ── Resolve JWT secret ── function getJwtSecret(): string { - // Priority 1: key file $keyPath = getenv('JWT_SECRET_KEY_PATH'); if ($keyPath && file_exists($keyPath)) { return trim(file_get_contents($keyPath)); } - // Priority 2: JWT_SECRET_KEY env var $key = getenv('JWT_SECRET_KEY') ?: ($_ENV['JWT_SECRET_KEY'] ?? ''); if ($key) return $key; - // Priority 3: JWT_SECRET env var (backend .env uses this) $key = getenv('JWT_SECRET') ?: ($_ENV['JWT_SECRET'] ?? ''); if ($key) return $key; return ''; } -// ── Pure PHP HS256 JWT (no libraries needed) ── function base64url_encode(string $data): string { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); } @@ -59,21 +52,40 @@ function generate_jwt(string $secret, array $payload): string { return "$header.$body.$sig"; } -// ── Main ── $secret = getJwtSecret(); if (empty($secret)) { - echo json_encode(['error' => 'JWT secret not found in any .env', 'files_checked' => $loadedFiles, 'jwt' => '']); - // Do not exit with 1 so Node.js can print the JSON error + echo json_encode(['error' => 'JWT secret not found in any .env', 'jwt' => '']); exit(0); } -$driverId = $argv[1] ?? '999999'; +$userId = $argv[1] ?? '999999'; +$role = $argv[2] ?? 'driver'; -$jwt = generate_jwt($secret, [ - 'sub' => (string)$driverId, - 'role' => 'driver', - 'iat' => time(), - 'exp' => time() + 86400, +$aud = ($role === 'driver') ? (getenv('allowedDriver2') ?: 'driver-app:ios') : (getenv('allowed2') ?: 'passenger-app:ios'); +$fpPepper = getenv('FP_PEPPER') ?: ($_ENV['FP_PEPPER'] ?? ''); +$fpHeader = 'mock_device_fp_' . $userId; +$fpInToken = $fpPepper ? hash('sha256', $fpHeader . $fpPepper) : null; + +$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]); diff --git a/stress_test/load_test.js b/stress_test/load_test.js index b0206441..6308f37d 100644 --- a/stress_test/load_test.js +++ b/stress_test/load_test.js @@ -35,23 +35,23 @@ function debug(msg) { if (argv.debug) console.log(` [DEBUG] ${msg}`); } -// Helper to get JWT for mock driver -function getMockDriverJwt(driverId) { +// Helper to get JWT and FP for mock user +function getMockJwt(userId, role = 'driver') { try { - const output = execSync(`php generate_mock_data.php ${driverId}`, { + const output = execSync(`php generate_mock_data.php ${userId} ${role}`, { timeout: 5000 }).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 parsed = JSON.parse(jsonString); if (!parsed.jwt) { - console.error(`[Error] JWT generation returned empty for driver ${driverId}: ${output}`); - return ''; + console.error(`[Error] JWT generation returned empty for ${role} ${userId}: ${output}`); + return { jwt: '', fp: '' }; } - return parsed.jwt; + return { jwt: parsed.jwt, fp: parsed.fp || `mock_device_fp_${userId}` }; } catch (err) { - console.error(`[Error] Failed to generate JWT for driver ${driverId}:`, err.message); - return ''; + console.error(`[Error] Failed to generate JWT for ${role} ${userId}:`, err.message); + return { jwt: '', fp: '' }; } } @@ -67,17 +67,17 @@ let metrics = { async function simulateTrip(tripIndex) { const driverId = 900000 + tripIndex; const passengerId = 800000 + tripIndex; - const driverJwt = getMockDriverJwt(driverId); + const driverAuth = getMockJwt(driverId, 'driver'); + const passengerAuth = getMockJwt(passengerId, 'passenger'); - if (!driverJwt) { - console.error(`[Trip ${tripIndex}] Skipping — JWT generation failed for driver ${driverId}`); + if (!driverAuth.jwt || !passengerAuth.jwt) { + console.error(`[Trip ${tripIndex}] Skipping — JWT generation failed.`); metrics.jwtErrors++; metrics.failed++; return false; } console.log(`[Trip ${tripIndex}] Starting... Driver: ${driverId}, Passenger: ${passengerId}`); - debug(`JWT token (first 30 chars): ${driverJwt.substring(0, 30)}...`); const tripStartTime = Date.now(); // 1. Connect Driver to Socket @@ -88,7 +88,7 @@ async function simulateTrip(tripIndex) { query: { driver_id: String(driverId), platform: 'android', - jwt: driverJwt + jwt: driverAuth.jwt }, reconnection: false, timeout: 10000, @@ -108,7 +108,6 @@ async function simulateTrip(tripIndex) { socket.on('connect_error', (err) => { console.error(`[Trip ${tripIndex}] ❌ Socket Connection Error: ${err.message}`); - debug(`Full error: ${JSON.stringify(err)}`); metrics.connectionErrors++; metrics.failed++; finish(false); @@ -145,7 +144,11 @@ async function simulateTrip(tripIndex) { `${argv.baseUrl}/ride/rides/add_ride.php`, 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 } ); @@ -177,7 +180,11 @@ async function simulateTrip(tripIndex) { `${argv.baseUrl}/ride/rides/acceptRide.php`, 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 } ); @@ -198,7 +205,18 @@ async function simulateTrip(tripIndex) { if (steps === 3) { try { 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}`); } catch (e) { debug(`start_ride error: ${e.message}`); @@ -209,7 +227,18 @@ async function simulateTrip(tripIndex) { clearInterval(driveInterval); try { 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}.`); } catch (e) { debug(`finish_ride error: ${e.message}`); @@ -244,7 +273,6 @@ async function simulateTrip(tripIndex) { } async function runTest() { - // ── Pre-flight checks ── console.log(`\n======================================================`); console.log(`🚀 Siro Stress Test`); console.log(`======================================================`); @@ -254,23 +282,18 @@ async function runTest() { console.log(`🐛 Debug: ${argv.debug ? 'ON' : 'OFF'}`); console.log(`------------------------------------------------------`); - // Test 1: Can we generate JWT? console.log(`\n🔑 Pre-flight: Testing JWT generation...`); - const testJwt = getMockDriverJwt(999999); - if (!testJwt) { - console.error(`❌ FATAL: Cannot generate JWT tokens. Check that:\n` + - ` - 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)`); + const testJwt = getMockJwt(999999, 'driver'); + if (!testJwt.jwt) { + console.error(`❌ FATAL: Cannot generate JWT tokens.`); process.exit(1); } console.log(` ✅ JWT generation works.`); - // Test 2: Can we reach the socket server? console.log(`\n🔌 Pre-flight: Testing socket connection...`); const testResult = await new Promise((resolve) => { const testSocket = io(argv.socketUrl, { - query: { driver_id: '999999', platform: 'android', jwt: testJwt }, + query: { driver_id: '999999', platform: 'android', jwt: testJwt.jwt }, reconnection: false, timeout: 5000, forceNew: true @@ -282,8 +305,6 @@ async function runTest() { }); testSocket.on('connect_error', (err) => { 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(); resolve(false); }); @@ -303,7 +324,6 @@ async function runTest() { process.exit(1); } - // ── Run the actual test ── console.log(`\n------------------------------------------------------`); console.log(`🏁 Starting ${argv.trips} concurrent trips...\n`);