Skip to content

Repository files navigation

House logo

Commercial home rent

Property listing and rental platform powered by Express.js, MongoDB, EJS, Passport, and Cloudinary.

Version License Type

Node.js Express MongoDB EJS

Passport Cloudinary Mapbox

Table of Contents

🚀 Project intro

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.

🎬 Demonstration

Watch the demo video for a complete walkthrough of the property listing, booking, payment, and review flows:

https://www.youtube.com/watch?v=oaWh6L09oFA

📁 Project structure

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 by routes/user.js
  • /listings — listing routes handled by routes/listing.js
  • /listings/:id/reviews — reviews handled by routes/review.js
  • /cart — cart & payment routes handled by routes/cart.js
  • /payments — payment utilities & email resend handled by routes/payment.js

🔧 Features

Core features

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

Extended / Optional

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

Format selection & upload syntax

  • schema.js defines the expected request payload shapes for listings and reviews.
  • cloudConfig.js contains Cloudinary params; the source includes a probable typo allowerdFormats — correct to allowedFormats if 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)

🌊 Flow diagram

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.live for 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]
Loading

Listing update authorization

Update (PUT/PATCH) is permitted only if all conditions hold:

  1. Listing exists (404 if not).
  2. User is logged in (redirect/login if not).
  3. Authenticated user _id matches listing owner (403 otherwise).

User review option

  • Authenticated users can post one review per listing (enforce uniqueness in controller/model if desired).
  • Conditions:
    1. Listing exists.
    2. User authenticated.
    3. (Optional) Prevent user from reviewing own listing.
  • Delete review allowed only for its author (and optionally admins).

Payment & booking

  • Stripe integration: Supports multi-method payments (card, Affirm, Clearpay, WeChat Pay, Cash App).
  • Checkout flow:
    1. User selects listing and booking dates (check-in/check-out).
    2. Date overlap validation prevents double-booking.
    3. Automatic currency conversion to USD for Stripe processing.
    4. 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 PaymentRecord with status, customer details, and booking info.
  • Ownership guard: Users cannot pay for their own listings.

🧰 Tech stack

  • 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

⚙️ Install methods

This is a Node.js application. Pip/pipx are not applicable.

📦 npm / Node

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.js

🐳 Docker

Example 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:latest

Create 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 key

Note: app.js loads .env automatically when NODE_ENV != "production".

🗄️Database structure

MongoDB collections and representative document shapes.

users collection

{
  "_id": "ObjectId",
  "username": "string",
  "email": "string",
  "hash": "string",       // managed by passport-local-mongoose
  "salt": "string",       // managed internally
  "createdAt": "Date",
  "updatedAt": "Date"
}

listings collection

{
  "_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"
}

reviews collection

{
  "_id": "ObjectId",
  "rating": 1,
  "comment": "string",
  "author": "ObjectId -> users._id",
  "listing": "ObjectId -> listings._id",
  "createdAt": "Date",
  "updatedAt": "Date"
}

Relationships

  • 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).

Index suggestions

  • users: { username: 1 } unique.
  • listings: { owner: 1, createdAt: -1 }.
  • reviews: { listing: 1, author: 1 } unique compound (enforces one review per user per listing).

Default image logic

If no upload provided, set image.url to a constant (e.g. /images/default-listing.jpg) and image.filename to null.

Payment records collection

{
  "_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"
}

🤝 Contributing

  • Fork the repo, create a branch, open a PR.
  • Keep PRs small and include verification steps.
  • Never commit secrets; use .env for local configuration.

📜 License

MIT — add a LICENSE file to make this explicit.

About

Property listing and rental platform

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages