A full-stack car rental application with two roles:
- User: discover cars, book rentals, manage bookings, pay online
- Owner: onboard a rental business, add/manage cars, view bookings, create offline bookings
- Session 0 — Overview, Roles, Features, Tech Stack
- Session 1 — Frontend (Client) Setup + Structure + Routes
- Session 2 — Backend (Server) Setup + Structure + API Endpoints
- Session 3 — Auth, Storage, Payments, Email, Jobs (How it works)
- Session 4 — Troubleshooting (CORS/Cookies/Local Dev)
-
User
- Signs up/logs in (supports Google sign-in)
- Email verification
- Optional 2FA via email OTP
- Browse available cars (filter by
state+city) - Book cars for a date range and pickup time
- View/cancel bookings
- Pay using Razorpay
-
Owner
- Signs up with business details + document uploads
- Email verification
- Optional 2FA via email OTP
- Adds cars to fleet
- Updates car status/price
- Views bookings
- Creates offline bookings
- Cancels affected bookings automatically when car becomes unavailable and notifies users by email
- Cookie-based JWT auth (
authTokenhttpOnly cookie) - Role-based routing (separate
/user/*and/owner/*) - PostgreSQL schema for users/owners/cars/bookings
- File uploads:
- Owner uploads:
id_proof,ownership_proof - Uploaded to Supabase Storage (public URLs stored)
- Owner uploads:
- Payments: Razorpay order creation + signature validation
- Email: Nodemailer (Gmail SMTP) for verification, reset, 2FA, cancellations
- Daily job: auto-mark completed bookings at midnight
Frontend
- React + Vite
- React Router
- Redux Toolkit + redux-persist
- Tailwind CSS + shadcn/ui style components (Radix UI)
Backend
- Node.js + Express
- PostgreSQL (
pg) - JWT (
jsonwebtoken) - Password hashing (
bcryptjs) - Email (
nodemailer) - Payments (
razorpay) - File upload (
multer) - Storage (
@supabase/supabase-js)
- App root: Client
- Entry: Client/src/main.jsx
- Router: Client/src/App/App.jsx
From the repo root:
-
Install:
cd Client && npm install
-
Configure env:
- Create/update: Client/.env
Required keys:
VITE_API_BASE_URL(example:http://localhost:3000)VITE_SUPABASE_PROJECT_URLVITE_SUPABASE_API_KEYVITE_RAZORPAY_API_KEY_IDVITE_RAZORPAY_API_SCRIPT_URL(example:https://checkout.razorpay.com/v1/checkout.js)
-
Start dev server:
npm run frontend
Vite default is typically http://localhost:5173.
Defined in Client/src/App/App.jsx.
Public
/- If user authenticated →
/user/dashboard - Else if owner authenticated →
/owner/dashboard - Else → landing page
- If user authenticated →
User (/user/*)
/user/log-in/user/sign-up/user/forgot-password/user/password-reset/:id/user/two-factor-auth/user/google/callback/user/dashboard(protected)/user/car-details/:car_id(protected)/user/owner-rental-cars(protected)
Owner (/owner/*)
/owner/log-in/owner/sign-up/owner/forgot-password/owner/password-reset/:id/owner/two-factor-auth/owner/dashboard(protected)/owner/offline-booking(protected)
- Redux store: Client/src/Redux/redux-store.js
- Slices:
- Persistence: uses
redux-persist(see Client/src/main.jsx)
Services live in:
Key idea:
- Axios uses
withCredentials: trueso the backend can set/read theauthTokencookie. - A response interceptor logs out and redirects on
401(except for auth routes).- Interceptor: Client/src/Services/auth-interceptor.js
Main service clients:
- User auth: Client/src/Services/user-auth-service.js
- Owner auth: Client/src/Services/owner-auth-service.js
- User booking/payment: Client/src/Services/user-car-rental-service.js
- Owner fleet/booking: Client/src/Services/owner-car-rental-service.js
- Server root: Server
- Entry: Server/src/index.js
- Express app: Server/src/app.js
From the repo root:
-
Install:
cd Server && npm install
-
Configure env:
- Create/update: Server/.env
Recommended keys (use your own values):
SERVER_PORT(example:3000)CORS_ORIGIN(example:http://localhost:5173)DATABASE_USERDATABASE_HOSTDATABASE_PASSWORDDATABASE_NAMEDATABASE_PORTDATABASE_SSLMODE(example:require)DATABASE_CHANNELBINDING(example:require)EMAIL_ADDRESSEMAIL_APP_KEYACCESS_TOKEN_SECRETACCESS_TOKEN_EXPIRY(example:1h)SALT_ROUNDS(example:10)SUPABASE_PROJECT_URLSUPABASE_API_KEYRAZORPAY_API_KEY_IDRAZORPAY_API_KEY_SECRET
-
Start backend:
npm run backend
Default server base URL (per env): http://localhost:3000
Schema file:
Core tables:
usersownerscar_rentalscarscar_bookings
Connection:
Mounted in Server/src/app.js:
/user(auth + rentals + payment)/owner(auth + rentals)
Auth middleware (cookie JWT):
All endpoints are defined under:
- User routes: Server/src/Routes/User
- Owner routes: Server/src/Routes/Owner
Routes: Server/src/Routes/User/auth-routes.js
POST /user/auth/sign-upPOST /user/auth/log-inGET /user/auth/verification-mail(protected)PATCH /user/auth/verification(protected)GET /user/auth/password-reset-mailPATCH /user/auth/password-resetPATCH /user/auth/activate-twofa(protected)GET /user/auth/twofa-mailPATCH /user/auth/twofa-verificationPOST /user/auth/twofa-log-inPOST /user/auth/log-out(protected)POST /user/auth/google/sign-upPOST /user/auth/google/log-inGET /user/auth/session(protected)
Routes: Server/src/Routes/Owner/auth-routes.js
POST /owner/auth/sign-up(multipart upload:id_proof,ownership_proof)POST /owner/auth/log-inPOST /owner/auth/log-out(protected)GET /owner/auth/verification-mail(protected)PATCH /owner/auth/verification(protected)GET /owner/auth/password-reset-mailPATCH /owner/auth/password-resetPATCH /owner/auth/activate-twofa(protected)GET /owner/auth/twofa-mailPATCH /owner/auth/twofa-verificationPOST /owner/auth/twofa-log-inGET /owner/auth/session(protected)
Routes: Server/src/Routes/User/car-rental-routes.js
GET /user/available-cars(protected, query:state,city)POST /user/car-booking(protected)GET /user/car/:car_id/booked-dates(protected)GET /user/bookings/:user_id(protected)GET /user/:car_rental_id/rental-cars(protected)PATCH /user/cancel-booking/:booking_id(protected)
Routes: Server/src/Routes/Owner/car-rental-routes.js
POST /owner/add-car(protected)GET /owner/:car_rental_id/rental-cars(protected)PATCH /owner/update-car-status(protected)PATCH /owner/update-car-price(protected)GET /owner/bookings/:car_rental_id(protected)GET /owner/car/:car_id/booked-dates(protected)POST /owner/offline-car-booking(protected)GET /owner/confirmed-car-bookings/:car_id(protected)
Routes: Server/src/Routes/User/payment-routes.js
POST /user/create-booking-order(protected)POST /user/validate-booking-order(protected)
- Token utilities: Server/src/Utils/jwt-utils.js
- Middleware reads
req.cookies.authToken: Server/src/Middleware/auth-middleware.js - Client sends cookies via Axios
withCredentials: truein:
- 2FA code generated/stored in DB
- Email sent via Nodemailer
- Service: Server/src/Services/mail-service.js
- Sends:
- verification code
- password reset link
- 2FA code
- booking cancellation notification
- Upload middleware: Server/src/Middleware/multer-middleware.js
- Files uploaded to Supabase Storage:
- Service: Server/src/Services/razorpay-service.js
- Flow:
- Client requests order creation (
/user/create-booking-order) - Client completes Razorpay checkout
- Client sends signature payload to backend (
/user/validate-booking-order) - After validation, client creates booking (
/user/car-booking) withorder_id+payment_id
- Client requests order creation (
- Job scheduler: Server/src/Jobs/booking-status-job.js
- Started on server boot: Server/src/index.js