A full-stack travel & tourism platform for vehicle bookings, rental accommodations, and destination discovery.
Features β’ Architecture β’ Getting Started β’ API Routes β’ Database β’ Contributing
- About the Project
- Features
- Architecture
- Tech Stack
- Getting Started
- User Roles & Permissions
- API Routes Reference
- Business Logic Deep Dive
- Database Schemas
- Middleware
- Session Management
- Image Uploads
- Error Handling
- Project Structure
- Screenshots
- Roadmap
- Contributing
- License
- Acknowledgements
QuickExplore is a full-stack web application that connects travelers with local vehicle rental services and accommodation providers. Users can browse tourist destinations, book vehicles with assigned drivers, and rent accommodations β all through a unified platform.
The platform supports four distinct user roles (User, Admin, Driver, Rental Owner), each with its own dashboard, authentication system, and feature set.
- For Travelers β Discover places, book vehicles and rooms in one platform
- For Vehicle Owners β Register vehicles and earn through the platform
- For Rental Owners β List properties and manage tenant bookings
- For Admins β Approve/reject requests, assign drivers, manage the ecosystem
- β User registration & login with hashed passwords (bcrypt)
- β Browse tourist places across India
- β Book vehicles at destinations with date selection
- β Book rental accommodations (Houses, PGs, Apartments, Villas)
- β View booking history (active, completed, cancelled)
- β Renew/extend bookings (vehicles & rentals)
- β Cancel bookings with dynamic cancellation fees
- β View cancellation charges history
- β Profile management with password change
- β Secure admin login (env-based credentials)
- β Review & approve/reject vehicle registration requests
- β Review & approve/reject rental property requests
- β Set rental pricing
- β Add new drivers to the system
- β Assign/reassign drivers to vehicles
- β View system architecture docs (DFD)
- β Driver login with phone, email & password
- β View assigned customer booking details
- β Mark rides as completed
- β Cancel rides (with auto fee calculation)
- β Change password from profile
- β Owner login with auto-generated credentials
- β View tenant/customer details
- β Mark stays as completed
- β Cancel bookings (with auto fee calculation)
- β Change password from profile
- β Server-side sessions stored in MongoDB (connect-mongo)
- β Image uploads to Cloudinary
- β Splash screen on first visit
- β Request status tracking (vehicle & rental)
- β 404 catch-all error page
- β Dynamic cancellation fee engine
QuickExplore follows the Model-View-Controller (MVC) pattern:
βββββββββββββββ ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β Browser ββββββΆβ Routes ββββββΆβ Controllers ββββββΆβ Models β
β (EJS) βββββββ (Express) βββββββ (Logic) βββββββ (Mongoose) β
βββββββββββββββ ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β β
ββββββββ΄βββββββ βββββββ΄βββββββ
β Middleware β β Cloudinary β
β (Auth) β β (Images) β
βββββββββββββββ ββββββββββββββ
QuickExplore/
βββ app.js # Express setup, middleware, route mounting (~90 lines)
βββ config/
β βββ db.js # MongoDB connection via Mongoose
β βββ cloudinary.js # Cloudinary + Multer storage config
βββ middleware/
β βββ auth.js # 4 auth middlewares (User, Admin, Driver, Owner)
βββ controllers/
β βββ userController.js # User registration, login, logout, profile
β βββ adminController.js # Admin auth, panel, documentation routes
β βββ vehicleController.js# Vehicle request CRUD, status checks
β βββ rentalController.js # Rental request CRUD, status checks
β βββ bookingController.js# Vehicle & rental bookings, charges history
β βββ driverController.js # Driver auth, rides, assignment, profile
β βββ ownerController.js # Owner auth, tenant management, profile
βββ routes/
β βββ users.js # /register, /login, /logout, /user/profile
β βββ admin.js # /adminLogin, /admin, /Adminlogout
β βββ vehicles.js # /vehicleRform, /requestForms/vehicleForms
β βββ rentals.js # /rentalRform, /requestForms/rentalForms
β βββ bookings.js # /places2, /book-vehicle, /book-rooms, /my-bookings
β βββ drivers.js # /driver/* (mounted at /driver prefix)
β βββ owners.js # /owner/* (mounted at /owner prefix)
βββ models/ # 10 Mongoose schemas
βββ views/ # EJS templates organized by feature
βββ public/ # Static assets (CSS, images)
βββ init/ # Database seeding scripts
ββββββββββββββββββββββββ
β QuickExplore β
β (Express) β
ββββββββββββ¬ββββββββββββ
β
βββββββββββββββββββββββββββΌββββββββββββββββββββββββββ
β β β
ββββββββ΄βββββββ βββββββββ΄ββββββββ ββββββββ΄βββββββ
β Users β β Admin β β Drivers β
β Register β β Approve/ β β Login β
β Login β β Reject β β Rides β
β Book β β Assign β β Cancel β
β Cancel β β Price Set β β Complete β
ββββββββ¬βββββββ βββββββββ¬ββββββββ ββββββββ¬βββββββ
β β β
β ββββββββ΄βββββββ β
β β Rental β β
β β Owners β β
β β Login β β
β β Manage β β
β ββββββββ¬βββββββ β
β β β
ββββββββ΄ββββββββββββββββββββββββββ΄ββββββββββββββββββββββββββ΄βββββββ
β MongoDB Atlas β
β Users | Drivers | Listings | Vehicles | Rentals | Bookings β
β Transactions | ChargesHistory | RentalChargesHistory β
ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
ββββββββ΄βββββββ
β Cloudinary β
β (Images) β
βββββββββββββββ
| Layer | Technology |
|---|---|
| Runtime | Node.js |
| Framework | Express.js v4 |
| Database | MongoDB (Mongoose ODM) |
| Template Engine | EJS (with ejs-mate layouts) |
| Authentication | bcrypt (password hashing) + express-session |
| Session Store | connect-mongo (MongoDB-backed sessions) |
| Image Storage | Cloudinary (via multer-storage-cloudinary) |
| File Upload | Multer |
| Environment | dotenv |
| HTTP Method Override | method-override (PUT/DELETE via forms) |
Make sure you have the following installed:
- Node.js (v16 or higher) β Download
- MongoDB (local) or MongoDB Atlas account β Atlas
- Cloudinary account β Sign up
- Git β Download
# 1. Clone the repository
git clone https://github.com/your-username/QuickExplore.git
# 2. Navigate to the project directory
cd QuickExplore
# 3. Install dependencies
npm installCreate a .env file in the root directory:
# MongoDB Connection
MONGO_ATLAS_URL=mongodb+srv://<username>:<password>@cluster.mongodb.net/quickexplore
# Session Secret
SESSION_SECRET=your_super_secret_session_key_here
# Cloudinary Configuration
CLOUD_NAME=your_cloud_name
CLOUD_API_KEY=your_api_key
CLOUD_API_SECRET=your_api_secret
# Admin Credentials
QuickExploreTeamAdmin_USERNAME=admin
QuickExploreTeamAdmin_EMAIL=admin@quickexplore.com
QuickExploreTeamAdmin_PASSWORD=your_admin_password
# Driver Default Password (assigned when admin creates a new driver)
DRIVER_PASS=default_driver_password
β οΈ Never commit your.envfile. It's already in.gitignore.
The init/ folder contains seed scripts for initial data:
# Seed tourist places
node init/listingP.js
# Seed sample vehicles
node init/vehicleRequestData.js
# Seed sample drivers
node init/driver.js
# Seed sample bookings
node init/bookingVehicle.js
# Seed sample rental requests
node init/rentalRequest.js
# Seed sample users
node init/userinit.js# Start the server
node app.js
# Output: app is listening on port : 8080
# Output: DB CONNECTED TO ATLAS MONGODB!Open your browser and navigate to: http://localhost:8080
| Feature | User | Admin | Driver | Rental Owner |
|---|---|---|---|---|
| Browse Places | β | β | β | β |
| Book Vehicle | β | β | β | β |
| Book Rental | β | β | β | β |
| Cancel Own Booking | β | β | β | β |
| Renew Booking | β | β | β | β |
| View Charges History | β | β | β | β |
| Approve/Reject Requests | β | β | β | β |
| Add Drivers | β | β | β | β |
| Assign Drivers to Vehicles | β | β | β | β |
| Set Rental Pricing | β | β | β | β |
| View Customer Details | β | β | β | β |
| Complete Ride/Stay | β | β | β | β |
| Cancel Ride (Driver-side) | β | β | β | β |
| Cancel Booking (Owner-side) | β | β | β | β |
| Change Password | β | β | β | β |
| Role | Login Method | Session Variable | Middleware |
|---|---|---|---|
| User | Email + Password | req.session.userId |
requireLogin |
| Admin | Username + Email + Password (from .env) |
req.session.authenticated |
isAuthenticated |
| Driver | Phone + Email + Password | req.session.driver_id |
requireDriverLogin |
| Rental Owner | Phone + Email + Password | req.session.owner_id |
requireOwnerLogin |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/ |
β | Redirects to /flash |
GET |
/flash |
β | Splash screen |
GET |
/register |
β | Registration form |
POST |
/register |
β | Create new user account |
GET |
/login |
β | Login form |
POST |
/login |
β | Authenticate user |
GET |
/logout |
β | Destroy session & logout |
GET |
/user/profile |
requireLogin |
View user profile |
POST |
/user/profile |
requireLogin |
Change user password |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/adminLogin |
β | Admin login form |
POST |
/adminLogin |
β | Authenticate admin |
GET |
/Adminlogout |
isAuthenticated |
Admin logout |
GET |
/admin |
isAuthenticated |
Admin dashboard |
GET |
/quickexploreDFDd1 |
isAuthenticated |
DFD Document 1 |
GET |
/quickexploreDFDd2 |
isAuthenticated |
DFD Document 2 |
GET |
/quickexploreSA |
isAuthenticated |
System Architecture doc |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/vehicleRform |
requireLogin |
Vehicle registration form |
GET |
/requestForms/vehicleForms |
isAuthenticated |
Admin: view all vehicle requests |
POST |
/requestForms/vehicleForms |
β | Submit vehicle request (with image upload) |
GET |
/usercredential |
requireLogin |
Show user credentials after vehicle request |
POST |
/updateVehicleStatus/:id |
β | Admin: approve/reject vehicle |
GET |
/Vstatus |
requireLogin |
Vehicle request status check form |
POST |
/CheckVehicleStatus |
β | Fetch vehicle request status |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/rentalRform |
requireLogin |
Rental property registration form |
GET |
/requestForms/rentalForms |
isAuthenticated |
Admin: view all rental requests |
POST |
/requestForms/rentalForms |
β | Submit rental request (with image upload) |
GET |
/usercredentialR |
requireLogin |
Show credentials after rental request |
POST |
/requestForms/updatePrice/:id |
β | Admin: set rental price |
POST |
/requestForms/updateStatus/:id |
β | Admin: approve/reject rental |
GET |
/Rstatus |
requireLogin |
Rental request status check form |
POST |
/CheckRentalStatus |
β | Fetch rental request status |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/places2 |
β | Browse all tourist places |
GET |
/listings/:id/viewP |
requireLogin |
View single place details |
GET |
/book-vehicle |
requireLogin |
Available vehicles at a place |
POST |
/book-vehicle |
β | Book a vehicle |
GET |
/my-bookings |
requireLogin |
User's vehicle bookings |
POST |
/renew-booking/:bookingId |
β | Extend vehicle booking |
POST |
/cancel-booking/:bookingId |
β | Cancel vehicle booking |
GET |
/my-charges |
requireLogin |
Vehicle cancellation charges history |
GET |
/book-rooms |
requireLogin |
Available rentals at a place |
POST |
/bookRental/:rentalId |
requireLogin |
Book a rental accommodation |
GET |
/my-Rbookings |
requireLogin |
User's rental bookings |
POST |
/renew-rental/:rentalBookingId |
requireLogin |
Extend rental booking |
POST |
/cancel-rental/:bookingId |
β | Cancel rental booking |
GET |
/my-rentalcharges |
requireLogin |
Rental cancellation charges history |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/driver/login |
β | Driver login form |
POST |
/driver/login |
β | Authenticate driver |
GET |
/driver/logout |
β | Driver logout |
GET |
/driver/home |
requireDriverLogin |
Driver dashboard |
GET |
/driver/customer-details |
requireDriverLogin |
View assigned customers |
POST |
/driver/customer-details |
β | Fetch customer details by driver ID |
POST |
/driver/complete-ride/:bookingId |
requireDriverLogin |
Mark ride as completed |
POST |
/driver/cancel-ride/:bookingId |
β | Cancel ride (driver-initiated) |
GET |
/driver/profile |
requireDriverLogin |
Driver profile page |
POST |
/driver/profile |
requireDriverLogin |
Change driver password |
GET |
/driver/assign-vehicle |
isAuthenticated |
Admin: assign vehicle to driver |
POST |
/driver/assign-vehicle |
β | Admin: execute assignment |
GET |
/driver/assign-vehicleNewDriver |
isAuthenticated |
Admin: reassign driver |
POST |
/driver/assign-vehicleNewDriver |
β | Admin: execute reassignment |
GET |
/driver/add |
isAuthenticated |
Admin: add driver form |
POST |
/driver/add |
isAuthenticated |
Admin: create new driver |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/owner/login |
β | Owner login form |
POST |
/owner/login |
β | Authenticate owner |
GET |
/owner/logout |
β | Owner logout |
GET |
/owner/home |
requireOwnerLogin |
Owner dashboard |
GET |
/owner/customer-details |
requireOwnerLogin |
View tenants |
POST |
/owner/complete-booking/:bookingId |
requireOwnerLogin |
Mark stay as completed |
POST |
/owner/cancel-booking/:bookingId |
requireOwnerLogin |
Cancel tenant booking |
GET |
/owner/profile |
requireOwnerLogin |
Owner profile page |
POST |
/owner/profile |
requireOwnerLogin |
Change owner password |
User selects a place
β
βΌ
GET /book-vehicle?place=XYZ
β Query: VehicleRequest { adminStatus: "Approved", bookingStatus: "Available", place }
β
βΌ
User picks vehicle & dates β POST /book-vehicle
β
βββ Verify vehicle exists & is approved + available
βββ Check no existing "Booked" booking for this vehicle
βββ Auto-find assigned driver: Driver.findOne({ currentVehicle: vehicleId })
βββ Calculate: rentalDays = ceil((dropoff - pickup) / 1 day)
βββ Calculate: totalAmount = rentalDays Γ rentalPricePerDay
βββ Create Booking record (vehicleStatus: "Booked", paymentStatus: "Pending")
βββ Update VehicleRequest.bookingStatus β "Booked"
βββ Redirect β /my-bookings
Key Points:
- Only admin-approved vehicles with status "Available" are shown
- Driver is auto-assigned based on the
currentVehiclefield - Vehicle gets locked (
bookingStatus: "Booked") immediately upon booking - Total amount is calculated server-side to prevent manipulation
User selects a place
β
βΌ
GET /book-rooms?place=XYZ
β Query: rentalRequest { place: /XYZ/i, adminStatus: "Approved", bookingStatus: "Available" }
β
βΌ
User picks rental & dates β POST /bookRental/:rentalId
β
βββ Verify rental exists & is approved + available
βββ Check for overlapping bookings:
β RentalBooking { rentalId, status: ["Booked","Ongoing"],
β checkInDate β€ checkOutDate AND checkOutDate β₯ checkInDate }
βββ Create RentalBooking record (status: "Booked")
βββ Update rentalRequest.bookingStatus β "Booked"
βββ Redirect β /my-Rbookings
Key Points:
- Place search is case-insensitive using regex
- Overlap detection prevents double bookings
totalAmountis sent from the form (calculated client-side from daily rate Γ days)
The system implements a tiered cancellation fee structure based on time difference:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CANCELLATION FEE TIERS β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ€
β Time to Pickup β Fee β
ββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββ€
β β€ 20 minutes β βΉ0 (free cancellation) β
β 20 min β 24 hrs β 10% of total booking amount β
β > 24 hours β 1 day's rental price β
ββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββ
For Vehicle Cancellation (POST /cancel-booking/:bookingId):
timeDifference (from request body, in minutes)
β
βββ > 20 AND β€ 1440 β fee = totalAmount Γ 0.10
βββ > 1440 β fee = vehicle.rentalPricePerDay
βββ β€ 20 β fee = 0
β
βββ booking.vehicleStatus β "Cancelled"
βββ booking.cancelledBy β "driver" (indicates who initiated)
βββ vehicle.bookingStatus β "Available" (freed up)
βββ Create Transaction record (if fee > 0)
βββ Create UserChargesHistory record (if fee β₯ 0)
βββ Render cancel-summary page
For Rental Cancellation (POST /cancel-rental/:bookingId):
- Same tiered logic, but uses
rental.priceinstead ofrentalPricePerDay - Records in
UserRentalChargesHistoryinstead - Renders
rental-cancel-summarypage
For Driver-Initiated Cancellation (POST /driver/cancel-ride/:bookingId):
- Calculates fee based on actual pickup date vs current time (server-side)
- Sets
booking.cancelledBy = "driver" - Creates Transaction record but does NOT create UserChargesHistory
For Owner-Initiated Cancellation (POST /owner/cancel-booking/:bookingId):
- Calculates fee based on checkInDate vs current time (server-side)
- Sets
booking.cancelledBy = "owner" - Records in UserRentalChargesHistory (if fee > 0)
Vehicle Renewal (POST /renew-booking/:bookingId):
additionalDays (from form)
β
βββ New dropoff = original dropoff + additionalDays
βββ Total days = ceil((newDropoff - pickupDate) / 1 day)
βββ Updated total = totalDays Γ rentalPricePerDay
βββ Save & redirect to /my-bookings
Rental Renewal (POST /renew-rental/:rentalBookingId):
additionalDays (from form)
β
βββ Must be the booking owner (userId check)
βββ Must be currently "Booked" status
βββ New checkout = original checkout + additionalDays
βββ Total days = ceil((newCheckout - checkInDate) / 1 day)
βββ Updated total = totalDays Γ dailyRate (rental.price)
βββ Save & redirect to /my-Rbookings
Admin assigns driver β POST /driver/assign-vehicle
β
βββ Clear any existing vehicle assignment for this vehicle:
β Driver.updateMany({ currentVehicle: vehicleId }, { currentVehicle: null })
βββ Assign driver:
β Driver.findByIdAndUpdate(driverId, { currentVehicle: vehicleId })
βββ Update vehicle:
β VehicleRequest.findByIdAndUpdate(vehicleId, { currentDriver: driverId })
βββ Redirect back
Admin reassigns driver β POST /driver/assign-vehicleNewDriver
β
βββ Same as above PLUS:
βββ Update ALL existing bookings for this vehicle:
Booking.updateMany({ vehicleId }, { driverId: newDriverId })
Key Points:
- One vehicle can only have one driver at a time
- When reassigning, ALL bookings (even past ones) get the new driver ID
- Driver auto-assignment happens at booking time:
Driver.findOne({ currentVehicle: vehicleId })
User Passwords:
- Hashed with
bcrypt(12 salt rounds) via a Mongoose pre-save hook - Password comparison via
user.comparePassword(candidate)instance method - On profile update, setting
user.password = newPlaintriggers the pre-save hook again
Driver Passwords:
- Same bcrypt pre-save hook and
comparePasswordmethod - Default password set from
process.env.DRIVER_PASSwhen admin creates driver - Drivers can change their password from
/driver/profile
Owner Passwords:
- No pre-save hook β hashing is done manually in the controller:
const hashedPassword = await bcrypt.hash(newPassword, 12); owner.password = hashedPassword;
comparePasswordinstance method exists for login verification
Admin Auth:
- No database record β credentials are read directly from environment variables
- Session flag
req.session.authenticated = trueupon successful login
When a rental property request is submitted:
POST /requestForms/rentalForms
β
βββ Save rental request with password: "placeholder"
βββ Generate unique password: "QuE9@" + last 5 chars of MongoDB _id
β Example: QuE9@a3f8c
βββ Hash generated password with bcrypt (12 rounds)
βββ Update rental record with hashed password
βββ Redirect to /usercredentialR showing:
β - Request ID
β - Contact
β - Email
β - Plain-text generated password (shown ONCE)
βββ Owner uses these credentials to login at /owner/login
User βββ¬βββΆ Booking (Vehicle) βββΆ VehicleRequest βββ Driver
β ββββΆ Driver
ββββΆ RentalBooking βββΆ RentalRequest
ββββΆ Transaction βββΆ VehicleRequest
ββββΆ UserChargesHistory βββΆ VehicleRequest + Booking
ββββΆ UserRentalChargesHistory βββΆ RentalRequest + RentalBooking
Driver.currentVehicle βββΆ VehicleRequest
VehicleRequest.currentDriver βββΆ Driver
| Model | Collection | Key Fields | Purpose |
|---|---|---|---|
| User | users |
username, email, password, phone | Platform users |
| Driver | drivers |
username, phone, email, currentVehicle, password | Vehicle drivers |
| ListingP | listingps |
title, location, type, images, entryFee | Tourist destinations |
| VehicleRequest | vehiclerequests |
ownerName, vehicleType, brand, model, registrationNumber, rentalPricePerDay, adminStatus, bookingStatus, currentDriver | Registered vehicles |
| RentalRequest | rentalrequests |
ownerName, propertyType, location, price, adminStatus, bookingStatus, password | Rental properties |
| Booking | bookings |
userId, vehicleId, driverId, pickupDate, dropoffDate, totalAmount, vehicleStatus | Vehicle bookings |
| RentalBooking | rentalbookings |
userId, rentalId, checkInDate, checkOutDate, totalAmount, status, cancellationFee | Rental bookings |
| Transaction | transactions |
userId, vehicleId, amount, type | Financial transactions |
| UserChargesHistory | userchargeshistories |
userId, vehicleId, bookingId, destination, cancellationFee | Vehicle cancellation history |
| UserRentalChargesHistory | userrentalchargeshistories |
userId, rentalId, bookingId, destination, cancellationFee | Rental cancellation history |
Admin Status (vehicles & rentals):
| Status | Meaning |
|---|---|
Pending |
Awaiting admin review (default) |
Approved |
Admin accepted the request |
Rejected |
Admin denied the request |
Flagged |
Marked for further review |
Booking Status (vehicles & rentals):
| Status | Meaning |
|---|---|
Available |
Ready for booking (default) |
Booked |
Currently booked by a user |
Ongoing |
Trip/stay in progress |
Completed |
Trip/stay finished |
Cancelled |
Booking was cancelled |
Payment Status:
| Status | Meaning |
|---|---|
Pending |
Payment not yet collected (default) |
Completed |
Payment received |
Failed |
Payment attempt failed |
Located in middleware/auth.js:
// Protects user-only routes
requireLogin(req, res, next)
β Checks: req.session.userId
β Redirects to: /login
// Protects admin-only routes
isAuthenticated(req, res, next)
β Checks: req.session.authenticated
β Redirects to: /adminLogin
// Protects driver-only routes
requireDriverLogin(req, res, next)
β Checks: req.session.driver_id
β Redirects to: /driver/login
// Protects owner-only routes
requireOwnerLogin(req, res, next)
β Checks: req.session.owner_id
β Redirects to: /owner/login// Makes currentUser available to ALL EJS templates
app.use((req, res, next) => {
res.locals.currentUser = req.session.user || null;
next();
});| Config | Value | Purpose |
|---|---|---|
| Store | connect-mongo (MongoDB) |
Sessions persist across server restarts |
| Secret | process.env.SESSION_SECRET |
Signs session cookies |
| Max Age | 24 hours (86400000 ms) |
Auto-logout after 1 day |
| httpOnly | true |
Prevents client-side JS access to cookies |
| resave | false |
Don't save session if unmodified |
| saveUninitialized | false |
Don't create session until data is stored |
// User login
req.session.userId = user._id;
req.session.user = { _id, username, email };
// Admin login
req.session.authenticated = true;
// Driver login
req.session.driver_id = driver._id;
// Owner login
req.session.owner_id = rental._id;
// Splash screen
req.session.splashShown = true/false;Configuration in config/cloudinary.js:
// Storage Settings
Folder: 'quickexplore_DEV'
Allowed Formats: jpg, jpeg, png
Public ID: Original filename (without extension)Used in routes:
POST /requestForms/vehicleFormsβ Vehicle registration imagesPOST /requestForms/rentalFormsβ Rental property images
Upload middleware: multer with CloudinaryStorage adapter
The app uses a two-tier error handling system:
-
Operational Errors β
views/errors/error.ejs- User-friendly messages (invalid credentials, not found, etc.)
- Rendered with:
res.render("errors/error", { error: "message" })
-
Application Errors β
views/errors/appError.ejs- System-level errors (DB failures, unexpected crashes)
- Rendered with:
res.render("errors/appError.ejs", { error })
-
404 Not Found β Catch-all route
app.get('*', (req, res) => { res.status(404).render("errors/appError", { error: 'Page not found', statusCode: 404 }); });
Every route handler is wrapped in try/catch blocks with appropriate error logging via console.error.
Quick-Explore/
β
βββ app.js # Express app entry point (~90 lines)
β
βββ config/
β βββ db.js # MongoDB connection
β βββ cloudinary.js # Cloudinary + Multer storage
β
βββ controllers/
β βββ userController.js # User auth + profile
β βββ adminController.js # Admin auth + dashboard
β βββ vehicleController.js # Vehicle request management
β βββ rentalController.js # Rental request management
β βββ bookingController.js # All booking operations
β βββ driverController.js # Driver operations
β βββ ownerController.js # Rental owner operations
β
βββ middleware/
β βββ auth.js # 4 authentication middlewares
β
βββ models/
β βββ User.js # User schema
β βββ Driver.js # Driver schema
β βββ listingP.js # Tourist place schema
β βββ vehicleRequest.js # Vehicle registration schema
β βββ rentalRequest.js # Rental property schema
β βββ bookingVehicle.js # Vehicle booking schema
β βββ bookingRental.js # Rental booking schema
β βββ Transaction.js # Transaction schema
β βββ UserChargesHistory.js # Vehicle charges history
β βββ UserRentalChargesHistory.js # Rental charges history
β
βββ routes/
β βββ users.js # User routes
β βββ admin.js # Admin routes
β βββ vehicles.js # Vehicle routes
β βββ rentals.js # Rental routes
β βββ bookings.js # Booking routes
β βββ drivers.js # Driver routes (prefix: /driver)
β βββ owners.js # Owner routes (prefix: /owner)
β
βββ views/
β βββ flash.ejs # Splash screen
β βββ BookingR/ # Rental booking views
β β βββ bookRental.ejs
β β βββ myRbookings.ejs
β βββ BookingV/ # Vehicle booking views
β β βββ bookVehicle.ejs
β β βββ myBookings.ejs
β βββ checkStatus/ # Status check views
β β βββ checkRstatusF.ejs
β β βββ checkVstatusF.ejs
β β βββ showRstatus.ejs
β β βββ showVstatus.ejs
β βββ docsqe/ # Documentation views
β β βββ Physical_DFD_QuickExplore.ejs
β β βββ Physical_DFD_QuickExplore2.ejs
β β βββ QuickExplore_Architecture.ejs
β βββ driver/ # Driver views
β β βββ addDriver.ejs
β β βββ assignNewDriverToVehicle.ejs
β β βββ assignVehicle.ejs
β β βββ customerList.ejs
β β βββ driverLogin.ejs
β β βββ driverPage.ejs
β β βββ driverProfile.ejs
β β βββ enterDriverId.ejs
β βββ errors/ # Error pages
β β βββ appError.ejs
β β βββ error.ejs
β βββ includes/ # Partial templates
β β βββ basicCode.ejs
β β βββ navbar.ejs
β βββ layouts/ # Layout templates
β β βββ boilerplate.ejs
β βββ listing/ # Place & form views
β β βββ places2.ejs
β β βββ rentalRform.ejs
β β βββ usercredential.ejs
β β βββ usercredentialR.ejs
β β βββ vehicleRform.ejs
β β βββ viewP.ejs
β βββ rentalOwner/ # Owner views
β β βββ customerList.ejs
β β βββ ownerLogin.ejs
β β βββ ownerPage.ejs
β β βββ ownerProfile.ejs
β βββ requestForms/ # Admin request views
β β βββ admin.ejs
β β βββ adminLogin.ejs
β β βββ rentalForms.ejs
β β βββ vehicleForms.ejs
β βββ users/ # User views
β βββ cancel-summary.ejs
β βββ login.ejs
β βββ my-charges.ejs
β βββ my-rentalcharges.ejs
β βββ register.ejs
β βββ rental-cancel-summary.ejs
β βββ userProfile.ejs
β
βββ public/
β βββ images/ # Static images
β
βββ init/ # Database seed scripts
β βββ bookingVehicle.js
β βββ driver.js
β βββ listingP.js
β βββ rentalRequest.js
β βββ userinit.js
β βββ vehicleRequestData.js
β
βββ .env # Environment variables (not committed)
βββ .gitignore
βββ cloudConfig.js # Legacy cloud config (kept for compatibility)
βββ package.json
βββ package-lock.json
screenshots of my application are:
I designed the system flow and database schema on paper before implementation.
The first version was built in a monolithic Express app to understand the data flow clearly.
Later I refactored the project into MVC architecture for better maintainability.
- Payment gateway integration (Razorpay / Stripe)
- Real-time notifications (Socket.io)
- Email notifications for booking confirmations
- Google Maps integration for place locations
- Rating & review system for vehicles and rentals
- Multi-language support
- Mobile-responsive PWA
- REST API for mobile app integration
- Advanced search filters (price range, vehicle type, property type)
- Dashboard analytics for admin
Contributions make the open-source community an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
- Fork the repository
- Clone your fork
git clone https://github.com/your-username/QuickExplore.git
- Create a feature branch
git checkout -b feature/your-feature-name
- Make your changes
- Test thoroughly β make sure
node app.jsruns without errors - Commit with a meaningful message
git commit -m "feat: add payment gateway integration" - Push to your branch
git push origin feature/your-feature-name
- Open a Pull Request
Follow the Conventional Commits format:
| Prefix | Purpose |
|---|---|
feat: |
New feature |
fix: |
Bug fix |
docs: |
Documentation changes |
style: |
Code formatting (no logic change) |
refactor: |
Code restructuring |
test: |
Adding or updating tests |
chore: |
Build/tooling changes |
- Do NOT change existing route paths or session variable names
- Do NOT modify database schemas without discussion
- Do NOT remove existing features
- Do add proper error handling with try/catch
- Do follow the existing MVC pattern (routes β controllers β models)
- Do test with
node app.jsbefore submitting - Do update this README if adding new routes or features
| Area | Description | Difficulty |
|---|---|---|
| π³ Payment Integration | Add Razorpay/Stripe for actual payments | Medium |
| π§ Email Service | Send booking confirmations via email | Easy |
| πΊοΈ Maps Integration | Show places on Google Maps | Medium |
| β Review System | Let users rate vehicles & rentals | Medium |
| π§ͺ Testing | Add unit & integration tests (Jest/Mocha) | Medium |
| π± Responsive UI | Improve mobile experience | Easy |
| π Notifications | Real-time updates with Socket.io | Hard |
| π Admin Analytics | Charts & stats for admin dashboard | Medium |
| π i18n | Multi-language support | Medium |
| π³ Docker | Add Dockerfile & docker-compose | Easy |
Found a bug? Please open an issue with:
- Clear description of the problem
- Steps to reproduce
- Expected vs actual behavior
- Screenshots (if applicable)
- Browser / Node.js version
This project is licensed under the MIT License β see the LICENSE file for details.
MIT License
Copyright (c) 2025 QuickExplore Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- Express.js β Fast, unopinionated web framework
- MongoDB β NoSQL database
- Mongoose β Elegant MongoDB ODM
- EJS β Embedded JavaScript templating
- Cloudinary β Image management platform
- bcrypt β Password hashing library
- connect-mongo β MongoDB session store
Made with β€οΈ by the QuickExplore Team
β Star this repo if you found it helpful!














