إصلاح سوكيت الراكب وتوحيد مسارات الإشعارات
سوكيت الراكب (سبب عدم ظهور معلومات السائق عند القبول): - تطبيق الراكب كان يرسل id فقط بلا jwt، و passenger_socket.php يرفض أي اتصال بلا jwt ⇒ الراكب لا ينضم لغرفته أبداً ولا يستلم ride_status_change ولا driver_location_update. تظهر حالة القبول عبر الـ polling فقط بينما driver_info يصل بالسوكيت وحده. (سوكيت السائق يعتبر الـ jwt اختيارياً، ومن هنا جاء التباين بين التطبيقين.) - cancelled_by_driver كان يسقط من switch حالات الراكب فيبقى معلّقاً بعد إلغاء السائق. - حماية socket (late) من القراءة قبل التهيئة عند الانسحاب بلا jwt. الإشعارات والرسائل (سبب "مرات توصل ومرات لا"): - جدول tokens يخزّن توكن الراكب مشفّراً، و getRideWaiting.php كان يرجعه بلا فك تشفير ⇒ من يقبل من قائمة السوق يحمل blob مشفّراً يستخدمه كـ FCM target فيرفضه FCM بـ 400: لا إشعار قبول ولا رسائل. ومن يقبل من الـ dispatch/FCM يحمل نصاً صريحاً فتعمل. الفرق كان في طريقة القبول. - acceptRide.php يحلّ التوكن من القاعدة دائماً ولا يثق بالعميل (أصحّ أمنياً). - market_new_ride كان لا يحمل passengerId ولا الإحداثيات فتصل "null"؛ أُضيفت بلا أي PII لأن الحمولة تُبَثّ لكل سائق قريب لا للفائز فقط. - send_fcm.php: مهلة على OAuth (كان يعلّق حتى مهلة PHP فتُسقط الرسالة بصمت)، توحيد ding→default لأندرويد، وحقن title/body/tone في data مطابقةً لـ FcmService. - تطبيق السائق يقرأ title/body من data أولاً مثل الراكب، ولا يعرض فقاعة فارغة للرسائل الصامتة. السوكيت والإعدادات: - forwardLocationToPassengerSocket كان يقرأ lat/lng والحمولة فيها latitude/longitude ⇒ المسافة تخرج ضخمة والـ throttle معطّل تماماً فيُعاد التوجيه مع كل نبضة GPS. - notifyPassengerOnRideServer كان يرجع null بصمت مطلق عند حجب العنوان. - العنوان الافتراضي لسيرفر الموقع كان nginx/loction_server/driver_socket.php وهو ديمون Workerman لا يُخدَم عبر nginx ⇒ صار socket_driver:2021. (LOCATION_API_URL بقي على nginx لأن api_get_nearby.php سكربت عادي.) - ride_server/passenger_socket.php (النسخة التي يشغّلها Docker) كانت ناقصة كل كود مواصلاتي الموجود في passenger_server/ ⇒ نُقل مع REDIS_HOST. - .env.example: ALLOWED_SOCKET_URLS يغطّي أسماء حاويات Docker، وإضافة PASSENGER_SOCKET_INTERNAL_URL. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e269cb9b70
commit
143146c1b4
@@ -6,8 +6,10 @@ import 'package:intaleq_maps/intaleq_maps.dart';
|
||||
|
||||
import '../../../constant/box_name.dart';
|
||||
import '../../../constant/links.dart';
|
||||
import '../../../main.dart'; // contains global 'box'
|
||||
import '../../../env/env.dart';
|
||||
import '../../../main.dart'; // contains global 'box' + 'storage'
|
||||
import '../../../print.dart';
|
||||
import '../../functions/encrypt_decrypt.dart'; // r()
|
||||
import 'ride_lifecycle_controller.dart';
|
||||
import 'nearby_drivers_controller.dart';
|
||||
import 'map_engine_controller.dart';
|
||||
@@ -29,10 +31,38 @@ class MapSocketController extends GetxController {
|
||||
DateTime? get lastDriverLocationTime => _lastSocketLocationTime;
|
||||
int get socketLocationUpdatesCount => _socketLocationUpdatesCount;
|
||||
|
||||
void initConnectionWithSocket() {
|
||||
/// يقرأ الـ JWT بنفس آلية CRUD._getJwt (SecureStorage أولاً ثم GetStorage).
|
||||
/// passenger_socket.php يرفض أي اتصال بلا jwt، فبدونه لا تصل أي أحداث للراكب.
|
||||
Future<String> _getJwtForSocket() async {
|
||||
try {
|
||||
final String? encryptedJwt = await storage.read(key: BoxName.jwt);
|
||||
if (encryptedJwt != null && encryptedJwt.isNotEmpty) {
|
||||
return r(encryptedJwt).toString().split(Env.addd)[0];
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print('Error reading JWT from SecureStorage for socket: $e');
|
||||
}
|
||||
final String? fallback = box.read(BoxName.jwt);
|
||||
if (fallback != null && fallback.toString().isNotEmpty) {
|
||||
try {
|
||||
return r(fallback).toString().split(Env.addd)[0];
|
||||
} catch (e) {
|
||||
Log.print('Error decrypting fallback JWT for socket: $e');
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
Future<void> initConnectionWithSocket() async {
|
||||
if (isSocketConnected) return;
|
||||
|
||||
String passengerId = box.read(BoxName.passengerID).toString();
|
||||
final String jwt = await _getJwtForSocket();
|
||||
if (jwt.isEmpty) {
|
||||
Log.print(
|
||||
"⚠️ No JWT available — passenger socket would be rejected by the server. Aborting connect.");
|
||||
return;
|
||||
}
|
||||
Log.print("🔌 Initializing Socket for Passenger: $passengerId");
|
||||
|
||||
socket = io_client.io(
|
||||
@@ -40,7 +70,7 @@ class MapSocketController extends GetxController {
|
||||
io_client.OptionBuilder()
|
||||
.setTransports(['websocket'])
|
||||
.disableAutoConnect()
|
||||
.setQuery({'id': passengerId})
|
||||
.setQuery({'id': passengerId, 'jwt': jwt})
|
||||
.setReconnectionAttempts(20)
|
||||
.setReconnectionDelay(2000)
|
||||
.setReconnectionDelayMax(10000)
|
||||
@@ -171,13 +201,15 @@ class MapSocketController extends GetxController {
|
||||
|
||||
// ── مواصلاتي: اشتراك/إلغاء اشتراك ببثّ موقع خط ─────────────
|
||||
// يُستدعى عند فتح/إغلاق شاشة تتبع الباص الحي. يضمن السوكيت متصلاً أولاً.
|
||||
void subscribeToTransitRoute(int routeId) {
|
||||
Future<void> subscribeToTransitRoute(int routeId) async {
|
||||
_subscribedTransitRouteId = routeId;
|
||||
if (!isSocketConnected) {
|
||||
initConnectionWithSocket();
|
||||
await initConnectionWithSocket();
|
||||
// سيُعاد الاشتراك تلقائياً من onConnect إن أضفنا ذلك، لكن نحاول فوراً أيضاً
|
||||
}
|
||||
if (socket.connected) {
|
||||
// socket هو late — لو انسحب initConnectionWithSocket (بلا JWT مثلاً) يبقى
|
||||
// غير مُهيّأ، فقراءته مباشرة ترمي LateInitializationError.
|
||||
if (_isSocketInitialized && socket.connected) {
|
||||
socket.emit('subscribe_transit_route', {'route_id': routeId});
|
||||
Log.print('🚌 Subscribed to transit route #$routeId');
|
||||
}
|
||||
@@ -185,7 +217,7 @@ class MapSocketController extends GetxController {
|
||||
|
||||
void unsubscribeFromTransitRoute(int routeId) {
|
||||
if (_subscribedTransitRouteId == routeId) _subscribedTransitRouteId = null;
|
||||
if (isSocketConnected && socket.connected) {
|
||||
if (_isSocketInitialized && isSocketConnected && socket.connected) {
|
||||
socket.emit('unsubscribe_transit_route', {'route_id': routeId});
|
||||
Log.print('🚌 Unsubscribed from transit route #$routeId');
|
||||
}
|
||||
@@ -243,7 +275,12 @@ class MapSocketController extends GetxController {
|
||||
_onRideFinishedWithSocket(data);
|
||||
break;
|
||||
|
||||
// cancel_ride_by_driver.php يبعث 'cancelled_by_driver' حرفياً،
|
||||
// و cancel_ride_by_passenger.php يبعث 'cancelled_by_passenger'.
|
||||
// كانت الحالتان تسقطان من الـ switch فيبقى الراكب معلّقاً على الشاشة.
|
||||
case 'cancelled':
|
||||
case 'cancelled_by_driver':
|
||||
case 'canceled_by_driver':
|
||||
rideLifecycle.processRideCancelledByDriver(data, source: "Socket");
|
||||
break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user