Update: 2026-08-03 11:52:03
This commit is contained in:
@@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
require_once __DIR__ . '/../../connect.php'; // تأكد أن هذا الملف يحتوي على $con_tracking
|
require_once __DIR__ . '/../../connect.php'; // تأكد أن هذا الملف يحتوي على $con_tracking
|
||||||
|
|
||||||
|
// $con_tracking لا يعرّفه connect.php — كان الاستدعاء يسقط بـ
|
||||||
|
// "Call to a member function prepare() on null".
|
||||||
|
$con_tracking = Database::get('tracking');
|
||||||
|
|
||||||
|
|
||||||
header("Content-Type: application/json; charset=UTF-8");
|
header("Content-Type: application/json; charset=UTF-8");
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../../connect.php';
|
require_once __DIR__ . '/../../connect.php';
|
||||||
|
|
||||||
|
// $con_tracking لا يعرّفه connect.php — كان الاستدعاء يسقط بـ
|
||||||
|
// "Call to a member function prepare() on null".
|
||||||
|
$con_tracking = Database::get('tracking');
|
||||||
|
|
||||||
if ($role !== 'admin' && $role !== 'super_admin') {
|
if ($role !== 'admin' && $role !== 'super_admin') {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
echo json_encode(['error' => 'Unauthorized: Admin access required']);
|
echo json_encode(['error' => 'Unauthorized: Admin access required']);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../../../connect.php';
|
// المسار كان '/../../../connect.php' فيُحلّ من backend/auth/driver إلى
|
||||||
|
// /var/www/connect.php — مستوى واحد فوق جذر الباك إند — فتسقط الشاشة بخطأ
|
||||||
|
// قاتل رغم أن الاستجابة تُرجع 200.
|
||||||
|
require_once __DIR__ . '/../../connect.php';
|
||||||
|
|
||||||
// 🔥 [Fix Broken Access Control] كان يتحقق من صلاحية التوكن فقط بدون التحقق
|
// 🔥 [Fix Broken Access Control] كان يتحقق من صلاحية التوكن فقط بدون التحقق
|
||||||
// من الدور — أي توكن صالح (حتى راكب) كان يقدر يسحب قائمة السائقين المعلّقين
|
// من الدور — أي توكن صالح (حتى راكب) كان يقدر يسحب قائمة السائقين المعلّقين
|
||||||
|
|||||||
@@ -79,7 +79,15 @@ class RateLimiter
|
|||||||
// ── بناء معرّف المستخدم ────────────────────────────────────
|
// ── بناء معرّف المستخدم ────────────────────────────────────
|
||||||
public static function identifier(?string $userId = null): string
|
public static function identifier(?string $userId = null): string
|
||||||
{
|
{
|
||||||
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
|
// كان REMOTE_ADDR وحده، وهو خلف دوكر دائماً عنوان الوسيط نفسه
|
||||||
|
// (172.24.0.1) — فيتشارك كل المستخدمين دلواً واحداً، ومحاولات دخول
|
||||||
|
// فاشلة من عميل واحد تحجب الجميع بـ 429 (ظهر ذلك في سجل الإنتاج).
|
||||||
|
// clientIp() تستخرج العميل الحقيقي من X-Forwarded-For ولا تثق به
|
||||||
|
// إلا إذا كان REMOTE_ADDR وسيطاً موثوقاً.
|
||||||
|
$ip = function_exists('clientIp')
|
||||||
|
? clientIp()
|
||||||
|
: ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
|
||||||
|
|
||||||
return $userId ? "{$ip}:{$userId}" : $ip;
|
return $userId ? "{$ip}:{$userId}" : $ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,15 @@ class Database
|
|||||||
PDO::ATTR_EMULATE_PREPARES => false,
|
PDO::ATTR_EMULATE_PREPARES => false,
|
||||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
PDO::ATTR_PERSISTENT => true,
|
// الاتصالات الدائمة أُوقفت: كانت تُنتج "Packets out of order.
|
||||||
|
// Expected 1 received 0" بتكرار في الإنتاج — وهو عرض معروف لإعادة
|
||||||
|
// استخدام اتصال دائم تُرك في حالة غير نظيفة، أو أغلقه MySQL عند
|
||||||
|
// wait_timeout بينما ما زال PHP يعدّه حياً. والأسوأ أن التحذير
|
||||||
|
// يُطبع قبل الرؤوس فيُفسد الاستجابة بـ "headers already sent".
|
||||||
|
//
|
||||||
|
// الكلفة اتصال جديد لكل طلب، وهي مقبولة هنا: MySQL على شبكة دوكر
|
||||||
|
// نفسها و max-connections=300. الاستقرار أولى من هذا التوفير.
|
||||||
|
PDO::ATTR_PERSISTENT => false,
|
||||||
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci",
|
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci",
|
||||||
PDO::ATTR_TIMEOUT => 10,
|
PDO::ATTR_TIMEOUT => 10,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -310,3 +310,52 @@ function maskPhone(?string $phone): ?string
|
|||||||
$tail = substr($d, -2);
|
$tail = substr($d, -2);
|
||||||
return $head . str_repeat('*', $len - 5) . $tail;
|
return $head . str_repeat('*', $len - 5) . $tail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* عنوان العميل الحقيقي خلف الوسطاء.
|
||||||
|
*
|
||||||
|
* سلسلة الطلب: العميل ← nginx المضيف ← nginx الحاوية ← php-fpm، فيصل
|
||||||
|
* REMOTE_ADDR دائماً كعنوان الوسيط. الاعتماد عليه وحده جعل RateLimiter يضع
|
||||||
|
* كل المستخدمين في دلو واحد، فمحاولات دخول فاشلة من عميل واحد تحجب الجميع.
|
||||||
|
*
|
||||||
|
* X-Forwarded-For قابل للتزوير من العميل، لذا لا نثق به إلا إذا كان
|
||||||
|
* REMOTE_ADDR نفسه وسيطاً موثوقاً (شبكات خاصة/لوكال). خلاف ذلك نعيد
|
||||||
|
* REMOTE_ADDR كما هو.
|
||||||
|
*/
|
||||||
|
function clientIp(): string
|
||||||
|
{
|
||||||
|
$remote = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
|
||||||
|
|
||||||
|
$isTrustedProxy = $remote !== 'unknown' && (
|
||||||
|
filter_var(
|
||||||
|
$remote,
|
||||||
|
FILTER_VALIDATE_IP,
|
||||||
|
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
|
||||||
|
) === false
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$isTrustedProxy) {
|
||||||
|
return $remote;
|
||||||
|
}
|
||||||
|
|
||||||
|
$forwarded = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
|
||||||
|
if ($forwarded === '') {
|
||||||
|
return $remote;
|
||||||
|
}
|
||||||
|
|
||||||
|
// أول عنوان عام في السلسلة هو العميل؛ نتخطّى العناوين الخاصة التي
|
||||||
|
// أضافها الوسطاء.
|
||||||
|
foreach (explode(',', $forwarded) as $candidate) {
|
||||||
|
$candidate = trim($candidate);
|
||||||
|
if ($candidate === '') continue;
|
||||||
|
if (filter_var(
|
||||||
|
$candidate,
|
||||||
|
FILTER_VALIDATE_IP,
|
||||||
|
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
|
||||||
|
) !== false) {
|
||||||
|
return $candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $remote;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
//encrypt_decrypt.php
|
//encrypt_decrypt.php
|
||||||
// ⚠️ هذا الملف للتوافقية فقط. استخدم core/Security/EncryptionHelper.php للتشفير الجديد
|
// ⚠️ هذا الملف للتوافقية فقط. استخدم core/Security/EncryptionHelper.php للتشفير الجديد
|
||||||
require_once realpath(__DIR__ . '/../vendor/autoload.php');
|
// vendor يقع داخل backend/ لا فوقه. المسار السابق '/../vendor/autoload.php'
|
||||||
|
// يشير إلى /var/www/vendor غير الموجود، و realpath() تُعيد false عندئذٍ،
|
||||||
|
// فيصبح الاستدعاء require_once('') ويسقط بخطأ قاتل يُحلّ من مجلد العمل
|
||||||
|
// الحالي — وهو ما ظهر في السجل كـ "Failed opening required ''".
|
||||||
|
$autoloadPath = __DIR__ . '/vendor/autoload.php';
|
||||||
|
if (!file_exists($autoloadPath)) {
|
||||||
|
$autoloadPath = __DIR__ . '/../vendor/autoload.php';
|
||||||
|
}
|
||||||
|
if (file_exists($autoloadPath)) {
|
||||||
|
require_once $autoloadPath;
|
||||||
|
}
|
||||||
|
|
||||||
require_once 'load_env.php';
|
require_once 'load_env.php';
|
||||||
$siteUser = get_current_user();
|
$siteUser = get_current_user();
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../../connect.php'; // يفترض أن هذا الملف ينشئ $con و $con_tracking
|
require_once __DIR__ . '/../../connect.php'; // يفترض أن هذا الملف ينشئ $con و $con_tracking
|
||||||
|
|
||||||
|
// $con_tracking لا يعرّفه connect.php — كان الاستدعاء يسقط بـ
|
||||||
|
// "Call to a member function prepare() on null".
|
||||||
|
$con_tracking = Database::get('tracking');
|
||||||
//getBalash.php
|
//getBalash.php
|
||||||
try {
|
try {
|
||||||
// 1) قراءة والتحقق من الإحداثيات
|
// 1) قراءة والتحقق من الإحداثيات
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../../connect.php'; // يفترض أن هذا الملف ينشئ $con و $con_tracking
|
require_once __DIR__ . '/../../connect.php'; // يفترض أن هذا الملف ينشئ $con و $con_tracking
|
||||||
|
|
||||||
|
// $con_tracking لا يعرّفه connect.php — كان الاستدعاء يسقط بـ
|
||||||
|
// "Call to a member function prepare() on null".
|
||||||
|
$con_tracking = Database::get('tracking');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1) قراءة والتحقق من الإحداثيات
|
// 1) قراءة والتحقق من الإحداثيات
|
||||||
$southwestLat = filterRequest("southwestLat");
|
$southwestLat = filterRequest("southwestLat");
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../../connect.php'; // يفترض أن هذا الملف ينشئ $con و $con_tracking
|
require_once __DIR__ . '/../../connect.php'; // يفترض أن هذا الملف ينشئ $con و $con_tracking
|
||||||
|
|
||||||
|
// $con_tracking لا يعرّفه connect.php — كان الاستدعاء يسقط بـ
|
||||||
|
// "Call to a member function prepare() on null".
|
||||||
|
$con_tracking = Database::get('tracking');
|
||||||
//getComfort.php
|
//getComfort.php
|
||||||
try {
|
try {
|
||||||
// 1) قراءة والتحقق من الإحداثيات
|
// 1) قراءة والتحقق من الإحداثيات
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../../connect.php'; // يفترض أن هذا الملف ينشئ $con و $con_tracking
|
require_once __DIR__ . '/../../connect.php'; // يفترض أن هذا الملف ينشئ $con و $con_tracking
|
||||||
|
|
||||||
|
// $con_tracking لا يعرّفه connect.php — كان الاستدعاء يسقط بـ
|
||||||
|
// "Call to a member function prepare() on null".
|
||||||
|
$con_tracking = Database::get('tracking');
|
||||||
//getDelivery.php
|
//getDelivery.php
|
||||||
try {
|
try {
|
||||||
// 1) قراءة والتحقق من الإحداثيات
|
// 1) قراءة والتحقق من الإحداثيات
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
require_once __DIR__ . '/../../get_connect.php';
|
require_once __DIR__ . '/../../get_connect.php';
|
||||||
|
|
||||||
|
// $con_tracking لا يعرّفه connect.php — كان الاستدعاء يسقط بـ
|
||||||
|
// "Call to a member function prepare() on null".
|
||||||
|
$con_tracking = Database::get('tracking');
|
||||||
|
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
// إعدادات التقرير
|
// إعدادات التقرير
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../../connect.php'; // يفترض أن هذا الملف ينشئ $con و $con_tracking
|
require_once __DIR__ . '/../../connect.php'; // يفترض أن هذا الملف ينشئ $con و $con_tracking
|
||||||
|
|
||||||
|
// $con_tracking لا يعرّفه connect.php — كان الاستدعاء يسقط بـ
|
||||||
|
// "Call to a member function prepare() on null".
|
||||||
|
$con_tracking = Database::get('tracking');
|
||||||
//getElectric.php
|
//getElectric.php
|
||||||
try {
|
try {
|
||||||
// 1) قراءة والتحقق من الإحداثيات
|
// 1) قراءة والتحقق من الإحداثيات
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
//getFemalDriver.php
|
//getFemalDriver.php
|
||||||
require_once __DIR__ . '/../../connect.php'; // يفترض أن هذا الملف ينشئ $con و $con_tracking
|
require_once __DIR__ . '/../../connect.php'; // يفترض أن هذا الملف ينشئ $con و $con_tracking
|
||||||
|
|
||||||
|
// $con_tracking لا يعرّفه connect.php — كان الاستدعاء يسقط بـ
|
||||||
|
// "Call to a member function prepare() on null".
|
||||||
|
$con_tracking = Database::get('tracking');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1) قراءة والتحقق من الإحداثيات
|
// 1) قراءة والتحقق من الإحداثيات
|
||||||
$southwestLat = filterRequest("southwestLat");
|
$southwestLat = filterRequest("southwestLat");
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
// $con_tracking -> يتصل بقاعدة بيانات التتبع (car_locations)
|
// $con_tracking -> يتصل بقاعدة بيانات التتبع (car_locations)
|
||||||
require_once __DIR__ . '/../../connect.php';
|
require_once __DIR__ . '/../../connect.php';
|
||||||
|
|
||||||
|
// $con_tracking لا يعرّفه connect.php — كان الاستدعاء يسقط بـ
|
||||||
|
// "Call to a member function prepare() on null".
|
||||||
|
$con_tracking = Database::get('tracking');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$driver_id = filterRequest("driver_id");
|
$driver_id = filterRequest("driver_id");
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
// $con_tracking -> يتصل بقاعدة بيانات التتبع (driver_behavior, car_locations)
|
// $con_tracking -> يتصل بقاعدة بيانات التتبع (driver_behavior, car_locations)
|
||||||
require_once __DIR__ . '/../../connect.php';
|
require_once __DIR__ . '/../../connect.php';
|
||||||
|
|
||||||
|
// $con_tracking لا يعرّفه connect.php — كان الاستدعاء يسقط بـ
|
||||||
|
// "Call to a member function prepare() on null".
|
||||||
|
$con_tracking = Database::get('tracking');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// استلام البيانات من Flutter
|
// استلام البيانات من Flutter
|
||||||
$driver_id = filterRequest("driver_id");
|
$driver_id = filterRequest("driver_id");
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
// وكائن التشفير $encryptionHelper
|
// وكائن التشفير $encryptionHelper
|
||||||
require_once __DIR__ . '/../../get_connect.php';
|
require_once __DIR__ . '/../../get_connect.php';
|
||||||
|
|
||||||
|
// $con_tracking لا يعرّفه connect.php — كان الاستدعاء يسقط بـ
|
||||||
|
// "Call to a member function prepare() on null".
|
||||||
|
$con_tracking = Database::get('tracking');
|
||||||
|
|
||||||
// السماح بالوصول من أي دومين (لأن الرابط سيفتح في متصفح العميل)
|
// السماح بالوصول من أي دومين (لأن الرابط سيفتح في متصفح العميل)
|
||||||
|
|
||||||
header("Content-Type: application/json; charset=UTF-8");
|
header("Content-Type: application/json; charset=UTF-8");
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ ob_start();
|
|||||||
|
|
||||||
require_once __DIR__ . '/../../get_connect.php';
|
require_once __DIR__ . '/../../get_connect.php';
|
||||||
|
|
||||||
|
// $con_tracking لا يعرّفه connect.php — كان الاستدعاء يسقط بـ
|
||||||
|
// "Call to a member function prepare() on null".
|
||||||
|
$con_tracking = Database::get('tracking');
|
||||||
|
|
||||||
// تنظيف *جميع* مستويات التخزين المؤقت (Loop)
|
// تنظيف *جميع* مستويات التخزين المؤقت (Loop)
|
||||||
// هذا يضمن التخلص من أي مسافات أو أخطاء ظهرت من ملفات الـ include
|
// هذا يضمن التخلص من أي مسافات أو أخطاء ظهرت من ملفات الـ include
|
||||||
while (ob_get_level()) {
|
while (ob_get_level()) {
|
||||||
|
|||||||
+5
-1
@@ -7,7 +7,11 @@ PHP_VERSION=8.2
|
|||||||
HTTP_PORT=8181
|
HTTP_PORT=8181
|
||||||
APP_DOMAIN=jordan-siro.intaleqapp.com
|
APP_DOMAIN=jordan-siro.intaleqapp.com
|
||||||
APP_ISSUER=SiroApp
|
APP_ISSUER=SiroApp
|
||||||
APP_DEBUG=true
|
# ⚠️ يجب أن تبقى false في الإنتاج.
|
||||||
|
# bootstrap.php يفعّل display_errors عندما تكون true، فتُطبع تحذيرات PHP
|
||||||
|
# داخل جسم الاستجابة: تكشف مسارات الملفات للعملاء، وتُفسد الردود بـ
|
||||||
|
# "headers already sent" لأن الإخراج يسبق الرؤوس (رُصد فعلاً في الإنتاج).
|
||||||
|
APP_DEBUG=false
|
||||||
APP_COUNTRY=Jordan
|
APP_COUNTRY=Jordan
|
||||||
APP_CURRENCY=JOD
|
APP_CURRENCY=JOD
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,19 @@ server {
|
|||||||
index index.php index.html;
|
index index.php index.html;
|
||||||
client_max_body_size 25m;
|
client_max_body_size 25m;
|
||||||
|
|
||||||
|
# عنوان العميل الحقيقي.
|
||||||
|
# السلسلة هي: العميل ← nginx المضيف (CloudPanel) ← nginx الحاوية ← php-fpm،
|
||||||
|
# فكان REMOTE_ADDR يصل إلى PHP دائماً كعنوان الوسيط (172.24.0.1). والنتيجة
|
||||||
|
# أن RateLimiter يضع كل المستخدمين في دلو واحد: محاولات دخول فاشلة من عميل
|
||||||
|
# واحد كانت تحجب الجميع بـ 429 (ظهر ذلك في السجل فعلاً).
|
||||||
|
# نمرّر السلسلة إلى PHP ليقرأها عبر clientIp() في core/helpers.php.
|
||||||
|
set $siro_fwd $http_x_forwarded_for;
|
||||||
|
if ($siro_fwd = "") {
|
||||||
|
set $siro_fwd $remote_addr;
|
||||||
|
}
|
||||||
|
fastcgi_param HTTP_X_FORWARDED_FOR $siro_fwd;
|
||||||
|
fastcgi_param HTTP_X_REAL_IP $remote_addr;
|
||||||
|
|
||||||
# كان access_log off من اختبار ضغط قديم ونُسي مُطفأً، فصار تشخيص أي عطل
|
# كان access_log off من اختبار ضغط قديم ونُسي مُطفأً، فصار تشخيص أي عطل
|
||||||
# مستحيلاً: البحث في سجلّ الوصول يرجع صفراً دائماً فيبدو أن التطبيق لا
|
# مستحيلاً: البحث في سجلّ الوصول يرجع صفراً دائماً فيبدو أن التطبيق لا
|
||||||
# يستدعي المسار أصلاً، والحقيقة أن السجلّ فارغ لا الطلبات. أعِده مطفأً
|
# يستدعي المسار أصلاً، والحقيقة أن السجلّ فارغ لا الطلبات. أعِده مطفأً
|
||||||
|
|||||||
Reference in New Issue
Block a user