Authorisation: driver/deleteCaptain.php permanently deletes a captain and had
no role check at all — any valid token, including a driver's or passenger's,
could delete captains. It now requires super_admin. The two unblacklist
endpoints had no role check either and now require an admin role.
Console:
- Blacklist & removal screen (super admin only): current blocks, permanent
removal, and lifting a block. Deletion additionally requires the phone
number to be retyped, because the row is deleted outright.
- Arabic interface with full RTL layout and a language toggle. Sidebar,
content offset, input affordances, toasts and directional icons all mirror;
numbers, identifiers and the diagnostics output stay LTR.
- Sign-in failures now render in the form and stay there. They were only
shown as a toast, which disappears before it can be read — a locked
account or a rejected device looked like the page simply returning to
the login screen with no explanation.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
serviceapp/updatePackages.php built its UPDATE by interpolating the request
values straight into the SQL string, so any caller with a valid token could
execute arbitrary SQL through the version field. It now uses bound
parameters, requires an admin role, validates the version format, and writes
an audit entry.
trigger_campaign.php gains dry_run=1: it performs the same Gemini analysis
and target selection but returns before creating the promo code and before
dispatching any notification. Launching without previewing was the only
option before, and a launch writes a seven-day discount and pushes to every
passenger in the country.
Console:
- Campaign launcher with a mandatory preview. Launching stays disabled until
the current parameters have been previewed, and re-locks if any parameter
changes afterwards or once a launch completes.
- App version manager with the same version-format check as the server and a
confirmation naming the old and new values.
Cache busting: assets are served straight off a bind mount with no version,
so browsers kept running the previously cached build after a deploy. Both
asset links now carry ?v=, and the build id is shown in Session & Security
and printed in the diagnostics report.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The ride table holds two generations of status values: the legacy CamelCase
set ('Finished', 'CancelFromPassenger') and the lowercase set written by
backend/ride/rides/* today ('completed', 'cancelled_by_passenger'). Admin
queries only matched the legacy set, so on live data:
- get_rides_by_status.php returned nothing meaningful for every filter, and
the "in progress" default masked it.
- dashbord.php reported total_driver_earnings as NULL, completed_rides as a
fraction of the real count, and cancelled_rides as 0.
- driver_avg_duration averaged in negative durations, yielding "-00h 22m".
All three now match on LOWER(status) across both families.
Staff/pending.php ran with no authentication at all, exposing pending
admins' names and phone numbers to any caller; it now goes through
connect.php with a role check. It also returned HTTP 400 for everything when
the `users` table was absent — each source is queried independently and
reports its own availability.
Console:
- Render rides from either schema generation (price/date/time and
start_location coordinates, or the older address/created_at columns).
- Null aggregates render as "—" rather than a measured 0.00.
- Add tariff/promo, WhatsApp send and encryption modules, all super-admin
gated; pricing remains read-only.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>