- Project intro
- Project structure
- Features
- Tech stack
- Install methods
- Format selection & upload syntax
- Database structure
- Contributing
- License
Commercial-home-rent is a full-featured property listing and rental platform built with Node.js, Express, and MongoDB. It empowers users to browse, book, and manage residential properties with integrated payment processing and secure authentication. Additionally, the platform features map integration, comprehensive review systems, multi-currency support, and email notifications for booking.
Watch the demo video for a complete walkthrough of the property listing, booking, payment, and review flows:
https://www.youtube.com/watch?v=oaWh6L09oFA
ecommerce-app/
├── models/ # Mongoose models
├── routes/ # Express route handlers
├── views/ # EJS templates
├── public/ # Static assets (CSS, JS, images)
├── controllers/ # Controller logic
├── app.js # Main application entry point
├── .env # Environment variables
├── README.md
└── ...
Key routes (based on app.js):
/— user routes (signup/login) handled byroutes/user.js/listings— listing routes handled byroutes/listing.js/listings/:id/reviews— reviews handled byroutes/review.js/cart— cart & payment routes handled byroutes/cart.js/payments— payment utilities & email resend handled byroutes/payment.js
| Feature | Status | Notes |
|---|---|---|
| User registration & login | ✅ Current | Passport Local (session-based auth) |
| Listings CRUD | ✅ Current | Create, read, update, delete listings |
| Listing update ownership guard | ✅ Current | Update only if: listing exists, user authenticated, user is owner |
| Reviews (create/delete) | ✅ Current | Authenticated users can add/remove their reviews |
| Session storage (MongoDB) | ✅ Current | connect-mongo |
| Flash messages & error handling | ✅ Current | Centralized Express error middleware |
| Cloud image uploads | ✅ Current | Multer + Cloudinary (optional) |
| Stripe payments | ✅ Current | Multi-method checkout (card, Affirm, Clearpay, WeChat Pay, Cash App) |
| Booking reservations | ✅ Current | Date-based booking with overlap detection |
| Multi-currency support | ✅ Current | Automatic USD conversion with real-time exchange rates |
| Email confirmations | ✅ Current | Resend integration for booking confirmations & receipts |
| Repeat booking download | ✅ Current | Download previous bookings & receipts as records |
| Feature | Status | Notes |
|---|---|---|
| Docker support | 🧪 Example | Sample Dockerfile included |
| Payments | ✅ Current | Stripe checkout with multi-currency support & email confirmations |
| Favorites / Wishlists | ⏳ Future | User personalization |
| Reviews & Ratings enhancements | ⏳ Future | Owner / renter feedback workflow |
schema.jsdefines the expected request payload shapes for listings and reviews.cloudConfig.jscontains Cloudinary params; the source includes a probable typoallowerdFormats— correct toallowedFormatsif you depend on that option. [VERIFY]
Listing Joi validation fields:
listing.title(string, required)listing.description(string, required)listing.location(string, required)listing.country(string, required)listing.price(number >= 0, required)listing.image(string | null)
Review fields:
review.rating(number 1..5)review.comment(string)
Mermaid flow (updated: includes listings, reviews, payments, and booking management):
💡 Tip: For wide diagrams, use your mouse wheel or arrow keys to pan horizontally.
If your viewer does not support dragging, open the diagram in
https://mermaid.livefor better zoom and navigation controls.
flowchart TD
A[Client] --> B[Login or Signup]
B --> C[Auth OK]
C --> D{User Action}
D -->|Create Listing| E[POST /listings]
D -->|View Listing| F[GET /listings/:id]
D -->|Book Property| G[POST /cart/create-checkout-session]
D -->|View Bookings| H[GET /bookings]
E --> E1[Parse body]
E1 --> E2[Validate Joi]
E2 --> E3{Image provided?}
E3 -->|Yes| E4[Upload image to Cloudinary]
E4 --> E5[Attach image refs]
E3 -->|No| E6[Use default placeholder]
E5 --> E7[Listing created]
E6 --> E7
F --> F1[Fetch listing details]
F1 --> F2{Owner?}
F2 -->|Yes| F3[Show edit/delete options]
F2 -->|No| F4[Show booking option]
F3 --> F5[PUT /listings/:id or DELETE]
F4 --> G
G --> G1[Check booking dates]
G1 --> G2{Dates overlap?}
G2 -->|Yes| G3[Reject - dates unavailable]
G2 -->|No| G4[Validate products & currency]
G4 --> G5[Convert to USD]
G5 --> G6[Create Stripe checkout session]
G6 --> G7[Redirect to Stripe payment]
G7 --> G8{Payment success?}
G8 -->|Yes| G9[Create PaymentRecord]
G9 --> G10[Send booking confirmation email]
G10 --> G11[Redirect to success page]
G8 -->|No| G12[Redirect to cancel page]
H --> H1[Fetch PaymentRecords for user]
H1 --> H2[Display booking history]
H2 --> H3{Download receipt?}
H3 -->|Yes| H4[Generate & download receipt]
H3 -->|No| H5[View booking details]
E7 --> I[POST /listings/:id/reviews]
I --> I1[Check: auth + not owner]
I1 --> I2[Validate review Joi]
I2 --> I3[Save review]
I3 --> I4[Respond]
Update (PUT/PATCH) is permitted only if all conditions hold:
- Listing exists (404 if not).
- User is logged in (redirect/login if not).
- Authenticated user
_idmatches listingowner(403 otherwise).
- Authenticated users can post one review per listing (enforce uniqueness in controller/model if desired).
- Conditions:
- Listing exists.
- User authenticated.
- (Optional) Prevent user from reviewing own listing.
- Delete review allowed only for its author (and optionally admins).
- Stripe integration: Supports multi-method payments (card, Affirm, Clearpay, WeChat Pay, Cash App).
- Checkout flow:
- User selects listing and booking dates (check-in/check-out).
- Date overlap validation prevents double-booking.
- Automatic currency conversion to USD for Stripe processing.
- Secure checkout session creation with success/cancel URLs.
- Multi-currency support: Accepts bookings in any currency; real-time USD conversion via exchange rate API.
- Booking confirmations: Automated emails sent via Resend on successful payment.
- Payment tracking: All transactions logged in
PaymentRecordwith status, customer details, and booking info. - Ownership guard: Users cannot pay for their own listings.
- Runtime: Node.js
- Framework: Express.js
- Templates: EJS + ejs-mate
- Database: MongoDB + Mongoose
- Authentication: Passport.js + passport-local-mongoose
- Payments: Stripe
- Email: Resend
- Validation: Joi
- Media uploads: Cloudinary + multer-storage-cloudinary
- Maps: Mapbox
This is a Node.js application. Pip/pipx are not applicable.
Install via npm install and start with node app.js. Note: package.json contains other scripts (Vite) but the server runs from app.js.
git clone <repo-url> commercial-home-rent
cd commercial-home-rent
npm install
node app.js
# for development with auto-reload
nodemon app.jsExample Dockerfile:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
ENV NODE_ENV=production
EXPOSE 8080
CMD ["node", "app.js"]Build and run:
docker build -t commercial-home-rent:latest .
docker run -p 8080:8080 -e ATLASDB_URL="..." -e SECRET="..." commercial-home-rent:latestCreate a .env at the project root with these variables (example):
ATLASDB_URL=mongodb+srv://<user>:<pass>@cluster.example.mongodb.net/dbname
SECRET=some-session-secret
CLOUD_NAME=...
CLOUD_API_KEY=...
CLOUD_API_SECRET=...
MAP_TOKEN=... # optional
STRIPE_SECRET_KEY=sk_live_... # Stripe payment key
RESEND_API_KEY=... # Resend email service keyNote: app.js loads .env automatically when NODE_ENV != "production".
MongoDB collections and representative document shapes.
{
"_id": "ObjectId",
"username": "string",
"email": "string",
"hash": "string", // managed by passport-local-mongoose
"salt": "string", // managed internally
"createdAt": "Date",
"updatedAt": "Date"
}{
"_id": "ObjectId",
"title": "string",
"description": "string",
"location": "string",
"country": "string",
"price": 3500,
"image": {
"url": "string", // uploaded or default placeholder
"filename": "string" // Cloudinary public_id (optional)
},
"owner": "ObjectId -> users._id",
"reviews": ["ObjectId -> reviews._id"],
"createdAt": "Date",
"updatedAt": "Date"
}{
"_id": "ObjectId",
"rating": 1,
"comment": "string",
"author": "ObjectId -> users._id",
"listing": "ObjectId -> listings._id",
"createdAt": "Date",
"updatedAt": "Date"
}- listing.owner references users.
- listing.reviews is an array of review ids.
- review.author references users.
- review.listing references listings.
- Delete listing: cascade (manually) delete its reviews.
- Delete user: decide whether to restrict if user owns listings/reviews (not automatic).
- users: { username: 1 } unique.
- listings: { owner: 1, createdAt: -1 }.
- reviews: { listing: 1, author: 1 } unique compound (enforces one review per user per listing).
If no upload provided, set image.url to a constant (e.g. /images/default-listing.jpg) and image.filename to null.
{
"_id": "ObjectId",
"sessionId": "string (unique)", // Stripe checkout session ID
"paymentIntentId": "string", // Stripe payment intent ID
"amountTotal": 9999, // Total in cents
"currency": "USD", // Charged currency
"localCurrency": "GBP", // User's booking currency
"localAmountTotal": 7800, // Amount in local currency
"paymentStatus": "paid", // Stripe payment status
"status": "paid", // Transaction status
"customerEmail": "string", // Buyer email
"customerName": "string", // Buyer name
"userId": "ObjectId -> users._id", // Buyer reference
"listingId": "ObjectId -> listings._id", // Property booked
"bookingStartDate": "Date", // Check-in date
"bookingEndDate": "Date", // Check-out date
"bookingDays": 3, // Number of days (inclusive)
"paymentMethodTypes": ["card"], // Payment method used
"createdAt": "Date",
"updatedAt": "Date"
}- Fork the repo, create a branch, open a PR.
- Keep PRs small and include verification steps.
- Never commit secrets; use
.envfor local configuration.
MIT — add a LICENSE file to make this explicit.
