RideFlow is a ride-hailing style app with:
- Users: register/login, choose pickup & destination, select vehicle type, request rides.
- Captains: register/login, stay online, receive nearby ride requests in real-time, accept/arrive, start/end rides.
- Maps & Routing: address geocoding, distance/time calculation, route geometry.
- Real-time updates: uses Socket.IO to notify users/captains of ride lifecycle events.
- Server:
server/src- Routes:
server/src/routes/* - Controllers:
server/src/controllers/* - Services:
server/src/services/* - Middleware:
server/src/middleware/* - Socket:
server/src/socket/socket.js - DB:
server/src/database/*
- Routes:
- Client:
client/src- Routing:
client/src/App.jsx - Pages:
client/src/pages/* - Components:
client/src/components/* - Context:
client/src/Context/*
- Routing:
Base URL mounting (from server/src/app.js):
/api/v1/auth→ user auth routes/api/v1/captain→ captain auth routes/api/v1/maps→ map/routing routes/api/v1/ride→ ride routes
- User JWT:
server/src/middleware/userAuth.middleware.js- Reads token from
req.cookies.tokenorAuthorization: Bearer <token> - Loads user from
userstable and setsreq.user
- Reads token from
- Captain JWT:
server/src/middleware/captainAuth.middleware.js- Reads token similarly
- Loads captain from
captainstable and setsreq.captain
server/src/middleware/validate.middleware.jsuses Zod schemas and returns the first validation error.
File: server/src/routes/user.routes.js
- Body: validated by
registerSchema(seeserver/src/validators/user.validator.js) - Creates user, returns token (set in HTTP-only cookie) and user data.
- Body: validated by
loginSchema - Logs in user, returns token (set in cookie).
- Auth: User JWT (
verifyUserJWT) - Response: user profile from
userstable.
- Clears cookie:
token.
File: server/src/routes/captain.routes.js
- Body validated by
registerCaptainSchema - Creates captain and sets cookie token.
- Body validated by
loginCaptainSchema - Logs in captain and sets cookie token.
- Auth: Captain JWT (
verifyCaptainJWT) - Returns captain profile from
captainstable.
- Clears cookie:
token.
File: server/src/routes/map.routes.js
- Auth: User JWT
- Body:
{ address } - Output:
{ lat, lng } - Service:
server/src/services/map.service.js(Nominatim)
- Auth: User JWT
- Output:
{ distance, duration, distanceText, durationText } - Service: OSRM (
router.project-osrm.org)
- Auth: User JWT
- Output: route geometry coordinates and distance/duration
- Uses OSRM with
geometries=geojson
- No auth
- Output: list of suggestions from Photon Komoot API
File: server/src/routes/ride.routes.js
- Auth: User JWT
- Output:
{ Price: <calculated object> } - Service:
RideService.calculateFare(distance, duration)
- Auth: User JWT
- Body:
{ pickup, destination, vehicleType } - Creates a ride and:
- Geocodes pickup
- Finds nearby captains within 15km (from
getNearbyCaptains(lat, lng, 15)) - Filters captains by
vehicle_type === vehicleType - Sends Socket.IO event to each matched captain:
new-ride- Payload includes ride + user details.
- Auth: User JWT
- Body/Query: pickup
- Finds nearby captains, returns unique available
vehicle_typevalues.
- Auth: Captain JWT
- Body:
{ rideId, status, captainId } - Updates ride status via
RideService.changeStatus(...) - Emits Socket.IO events to user depending on status:
status === 'accepted'→ emitride-confirmedstatus === 'arrived'→ emitride-arrivedwith{ rideId: updatedRide.id }
- Auth: User JWT
- Returns ride details (
RideService.getRideDetails(rideId))
- Auth: Captain JWT
- Body:
{ rideId, captainId, otp } - Updates ride to started (
RideService.startRide(...)) - Emits Socket.IO event to user:
ride-startedwith{ rideId }
- Auth: Captain JWT
- Body:
{ rideId, captainId } - Updates ride to completed (
RideService.endRide(...)) - Emits Socket.IO event to user:
ride-completedwith a payment/order payload
Server file: server/src/socket/socket.js
- Exports
initSocket(server)andsendMessageToSocketId(socketId, event, data)
join:{ userId, userType }whereuserTypeis"user"or"captain"- Server updates
users.socket_idorcaptains.socket_id.
update-captain-location:{ captainId, latitude, longitude }- Persists captain coordinates using
updateCaptainLocation.
new-ride(sent when a user creates a ride)ride-confirmed(sent when captain accepts)ride-arrived(sent when captain arrives)ride-started(sent when captain starts ride)ride-completed(sent when captain ends ride)
Client socket entry: client/src/socket/socket.js
- Connects to
http://localhost:7000withwithCredentials: true
File: client/src/App.jsx
/login→UserLogin/register→UserRegister/captain-login→CaptainLogin/captain-register→CaptainRegister/about→About/help→ placeholderHelp Page
Wrapped by UserProtectedWrapper:
/→Home(home/search)/select-vehicle→VehicleSelect(fare + vehicle options)/shareOtp/:rideId→ShareOtp(shows OTP/pickup code)
Wrapped by CaptainProtectedWrapper:
/captain-home→CaptainHome(online + accept ride)/captain-riding/:rideId→Arrived(captain marks arrived)/otp-verify→OtpVerify(captain starts ride using OTP)/end-ride→EndRide
/request-ride→RequestRide(user waiting for captain acceptance)/Ride/:rideId→Ride(shows ride details and route map)
UserRegisterposts to:POST /api/v1/auth/registerUserLoginposts to:POST /api/v1/auth/login- JWT is stored in
localStorage(token) and also set as cookie by the server.
CaptainRegisterposts to:POST /api/v1/auth/registerin the provided code (likely intended to be/api/v1/captain/register).CaptainLoginposts to:POST /api/v1/captain/login
- Home (
/): user selects pickup/destination; socket joins room:joinwith{ userId, userType:'user' }. - VehicleSelect (
/select-vehicle):- Fetches fare:
GET /api/v1/ride/fare?pickup&destination - Fetches available vehicle types:
GET /api/v1/ride/available-vehicles?pickup - User selects a vehicle type and requests ride:
POST /api/v1/ride/create
- Fetches fare:
- RequestRide (
/request-ride): listens on socket for:ride-confirmedto show captain info.ride-arrivedto navigate to/shareOtp/:rideId.
- CaptainHome (
/captain-home):- Watches geolocation and emits
update-captain-location. - Joins socket room with
{ userId: captain.id, userType:'captain' }. - Listens for
new-rideand shows accept/ignore.
- Watches geolocation and emits
- When captain clicks Accept:
- Calls
PATCH /api/v1/ride/update-statuswithstatus:'accepted'. - Triggers server emit
ride-confirmedto the user.
- Calls
- Arrived (
/captain-riding/:rideId):- Calls
PATCH /api/v1/ride/update-statuswithstatus:'arrived'. - User receives
ride-arrivedand is navigated to/shareOtp/:rideId.
- Calls
- OtpVerify (
/otp-verify):- Calls
POST /api/v1/ride/start-ridewith{ rideId, captainId, otp }. - Server emits
ride-startedto the user. - User navigates to
/ride/:rideId.
- Calls
- EndRide (
/end-ride): UI placeholder; server supportsPOST /api/v1/ride/end-ridewhich emitsride-completed.
Referenced in code:
JWT_SECRETCORS(server usescors({ origin: process.env.CORS }))NOMINATIM_USER_AGENTNOMINATIM_LANGUAGE
Maps/geocoding uses external services:
- Nominatim (geocoding)
- OSRM (distance, duration, route)
- Photon Komoot (suggestions)
- Socket server sets
ioCORS origin tohttp://localhost:5173. - User and captain auth middleware look for JWT in cookie
tokenorAuthorizationheader. - In the provided client code, some flows store tokens in
localStorage(e.g.token,Captaintoken). - There is a likely bug in
CaptainRegister.jsx: it posts to/auth/register(user auth) instead of the captain route.