Commit Graph
11 Commits
Author SHA1 Message Date
KrikoriosandClaude f50ff27e11 Build driver app, Uber-style dispatch, POI suggestions; fix map tiles
Driver side (was a stub):
- In-app driver onboarding: a driver-role user creates their own linked
  drivers profile (driver/profile+api GET/POST/PATCH).
- Driver dashboard: online/offline toggle, today's earnings, incoming
  request cards (accept/decline), active ride panel (start/complete trip).
  Polls /driver/rides every 4s while online.
- Location heartbeat (use-driver-location): watchPositionAsync pings
  /driver/location every ~5s; restarts the watch on app foreground so a
  backgrounded driver doesn't go permanently stale and miss requests.

Dispatch (auto-match nearest, Uber-style):
- Ride state machine: requested -> accepted -> en_route -> completed/cancelled
  with a nullable driver_id until matched (lib/dispatch.matchNextDriver).
- matchNextDriver locks the ride (SELECT FOR UPDATE), expires 15s-stale
  offers, picks the nearest eligible driver of the matching service by
  haversine, offers one at a time. Called from ride/create, ride/[id] GET
  (lazy match on the rider's poll), and ride/[id]/respond (on decline).
- ride/create is now a request endpoint (driver_id NULL, status=requested,
  service); drops the pre-match driver_id payment reconciliation.
- ride/[id] GET returns status/service/nullable driver; PATCH handles rider
  cancel + driver en_route/completed. ride/list backs the history tabs.

Rider flow (best experience):
- confirm-ride is now a request screen: single trip fare + nearest-driver
  ETA + cash/card + Request Ride -> live status. Periodically polls online
  drivers of the selected service and disables Request when none are
  online (prevents the "stuck searching forever" state).
- book-ride is the live ride-status screen (searching -> accepted ->
  en_route -> completed/cancelled + Cancel), polling every 3s.
- lib/request-ride unifies the Areeba card flow + cash path.
- Map reads /driver/nearby (real positions, service-filtered); lib/map
  adds calculateTripFare + service-aware fares.

POI suggestions:
- lib/places (Google Nearby Search) + nearby-suggestions chips for
  mall/hospital/pharmacy/restaurant on the home screen.

Service categories now drive both matching and a per-service fare
multiplier (car 1.0 / moto 0.7 / courier 0.85 / chauffeur 1.5).

Map tiles: react-native-maps rendered blank on Android because no Google
Maps key was set. Switched app.json -> app.config.js so
android.config.googleMaps.apiKey is injected from
EXPO_PUBLIC_GOOGLE_API_KEY at build time (keeps the key out of git).
Requires a native rebuild (expo run:android) to take effect.

Also includes the prior payment/auth hardening (server-authoritative
payment_orders ledger with double-spend guards, peppered OTP, register
TOCTOU fix, stats cents fix) that was left uncommitted.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 13:57:21 +03:00
KrikoriosandClaude Opus 5 bc23c94ea2 Add remember-me and OTP autofill, fix session persistence
Sign-in gains a "Keep me signed in" checkbox: checked issues a 30-day
token and prefills the address next launch, unchecked drops the session
to 12 hours and forgets the address. The TTL is chosen server-side in
the login route.

Emailed codes are now reachable without retyping. OtpField opts into the
iOS one-time-code keyboard suggestion and raises a paste chip when the
user returns from Gmail with a code on the clipboard. The mails put the
code first in the subject and body, which is what makes Gmail render its
"Copy code" notification action at all.

Fixes found along the way:

- Session was wiped on every launch. decodeJwtExp used atob, which
  neither RN 0.74 nor Expo SDK 51 defines, so it threw, returned null,
  and the caller read that as "expired" and deleted the token. Replaced
  with a dependency-free base64url decoder, and restore now only
  discards a session it can prove is expired.
- Verification and reset codes counted attempts but never enforced them,
  leaving a 6-digit code open to unlimited guessing. Both routes now
  charge the attempt before comparing so concurrent guesses can't race
  past the cap of five, and compare in constant time.
- A wrong verification code showed the "Verified" success screen:
  onModalHide fired unconditionally, so the failure state advanced the
  flow. Only an explicit "verified" state does that now.
- fetchAPI discarded the server's error body, so the UI substring-matched
  synthetic status strings and showed "Could not sign in" for everything.
  It now throws ApiError carrying status and the server's message.
- Login answered a missing account faster than a wrong password; it now
  runs the same scrypt work either way.
- Blank email or password is caught client-side instead of surfacing as
  an opaque 400, and a failed attempt only clears the password on a 401.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 23:33:51 +03:00
KrikoriosandClaude Fable 5 eceb6b45d5 Fix SMTP delivery, add password reset and user deletion
SMTP:
- Add connection/greeting/socket timeouts so a stalled Gmail
  connection no longer hangs sign-up
- Wrap sendMail in try/catch and fall back to logging the code
- Derive secure from port (465 implicit TLS vs 587 STARTTLS)
- Strip whitespace from the Gmail app password
- Document SMTP_HOST/SMTP_PORT in .env.example and environment.d.ts

Password reset (new):
- POST /(api)/auth/forgot-password emails a 6-digit code and does
  not reveal whether the address is registered
- POST /(api)/auth/reset-password validates the code, sets the new
  password, verifies the email, and signs the user in
- password_reset_codes table added to seed-db.mjs
- "Forgot password?" flow on the mobile sign-in screen

User deletion (new):
- DELETE /(api)/admin/users/[id], owner-only, blocks self-deletion
- Delete button with confirmation on the dashboard Users page

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 22:41:41 +03:00
Krikorios a0b297285a Add self-hosted auth, admin API, and owner web dashboard
- Replace Clerk with self-hosted JWT auth (register/login/verify, bcrypt
  passwords, Gmail OTP with console fallback)
- Add lib/db.ts pg pool + transaction helpers; seed script migrates legacy
  Clerk-era schema (drop clerk_id, enforce UUID ids and unique email)
- Add owner-gated admin API: stats, users, drivers CRUD, rides
- Add dashboard/ Vite React owner dashboard (login, overview, users,
  fleet, rides) with dev-server proxy to avoid Expo CORS middleware
- Add scripts/set-owner.mjs for role management
2026-08-23 16:38:41 +03:00
Krikorios fbe92c9d16 Add rider/driver role selection after sign-up
- users.role column (+ migration in seed script)
- /(api)/user: GET role by clerkId, PATCH to set role
- Role selection screen; drivers get placeholder driver-home
- Root index routes by role; hardened location handling on home
2026-08-22 16:13:59 +03:00
Krikorios 62dc5e9d53 Rebrand to Waseel, swap Stripe for Areeba, add phone-ready auth and DB seed
- Rename app: Waseel (name, slug, scheme waseel://, com.waseel.app ids, splash)
- Payments: replace Stripe with Areeba hosted checkout (create/verify API routes,
  lib/areeba.ts, WebBrowser-based payment flow)
- Maps: migrate address autocomplete to Places API (New), drop legacy library
- Web support: map stub for web (native maps are iOS/Android only)
- Auth: keep email + Google OAuth; fix OAuth redirect for Expo Go
- Add scripts/seed-db.mjs (schema + Lebanese driver seed)
- Pin Expo SDK 51 compatible package versions
2026-08-22 15:53:41 +03:00
Sanidhya Kumar Verma 02dbd6c7af stripe payment integrated 2024-09-04 11:05:39 +00:00
Sanidhya Kumar Verma eb7e052d75 stripe payment endpoints created 2024-09-04 10:17:45 +00:00
Sanidhya Kumar Verma c5c50e75db drivers api endpoint created 2024-09-04 10:17:34 +00:00
Sanidhya Kumar Verma b313417609 home page initial ui implemented 2024-08-30 12:18:27 +00:00
Sanidhya Kumar Verma 7f0d38eb3c user api fetch request created 2024-08-30 07:27:34 +00:00