An Airbnb-inspired travel listing web app built with Node.js, Express, MongoDB, EJS, Bootstrap 5, Passport.js, Cloudinary, and Mapbox.
| Feature | Details |
|---|---|
| User Authentication | Signup, login, logout via Passport.js + passport-local-mongoose |
| Authorization | Owners can only edit/delete their own listings and reviews |
| Image Uploads | Real photo uploads via Multer + Cloudinary (cloud storage) |
| Interactive Maps | Mapbox cluster map on index + single pin on every listing |
| Search | Full-text search across title, location, and country |
| Geocoding | Mapbox Geocoding API auto-pins listings on save |
| MVC Structure | Routes split into routes/ directory — listings, reviews, users |
| Environment Variables | .env file for all secrets (Cloudinary, Mapbox, session, MongoDB) |
wanderlust/
├── app.js Main Express server
├── cloudinary.js Cloudinary v2 config + Multer storage
├── middleware.js Joi validation + isLoggedIn, isOwner, isReviewAuthor
├── package.json
├── .env.example Template — copy to .env and fill in your keys
├── .gitignore
├── README.md
├── models/
│ ├── listing.js Schema: owner ref, geometry, reviews with author
│ └── user.js Passport-local-mongoose user model
├── routes/
│ ├── listings.js CRUD + geocoding + image upload
│ ├── reviews.js Add/delete reviews (auth-protected)
│ └── users.js Signup / login / logout
├── views/
│ ├── listings/
│ │ ├── index.ejs Card grid + search + Mapbox cluster map
│ │ ├── show.ejs Detail page + Mapbox pin + auth-gated reviews
│ │ ├── new.ejs Create form with file upload
│ │ ├── edit.ejs Edit form with image preview + replace
│ │ └── 404.ejs Custom 404 page
│ └── users/
│ ├── signup.ejs Sign up form
│ └── login.ejs Log in form
├── public/css/
│ └── style.css Bootstrap companion + map + auth styles
└── init/
├── data.js 30 sample listings ($109–$399/night)
└── index.js Seed script — creates demo user + assigns ownership
git clone https://github.com/your-username/wanderlust-phase2.git
cd wanderlust-phase2
npm installcp .env.example .envThen open .env and fill in:
| Key | Where to get it |
|---|---|
CLOUDINARY_CLOUD_NAME |
cloudinary.com → Dashboard |
CLOUDINARY_API_KEY |
Cloudinary → Dashboard |
CLOUDINARY_API_SECRET |
Cloudinary → Dashboard |
MAPBOX_TOKEN |
mapbox.com → Access Tokens |
SESSION_SECRET |
Any long random string |
MONGO_URL |
Keep default for local MongoDB |
mongodnpm run seedThis creates a demo user (username: demo, password: password123) and inserts 30 sample listings.
npm start # production
npm run dev # with auto-reload (nodemon)http://localhost:8080
Log in with demo / password123 to test listing creation and reviews.
| Method | Route | Auth | Description |
|---|---|---|---|
| GET | /listings |
Public | All listings + search + map |
| GET | /listings/new |
Login required | New listing form |
| POST | /listings |
Login required | Create listing + geocode + upload |
| GET | /listings/:id |
Public | Listing detail + map pin |
| GET | /listings/:id/edit |
Owner only | Edit form |
| PUT | /listings/:id |
Owner only | Update listing |
| DELETE | /listings/:id |
Owner only | Delete listing + Cloudinary image |
| POST | /listings/:id/reviews |
Login required | Add review |
| DELETE | /listings/:id/reviews/:reviewId |
Review author only | Delete review |
| GET | /signup |
Public | Signup form |
| POST | /signup |
Public | Create account |
| GET | /login |
Public | Login form |
| POST | /login |
Public | Authenticate |
| POST | /logout |
Public | Log out |
- Sign up at cloudinary.com
- Go to Dashboard → copy Cloud Name, API Key, API Secret
- Paste into
.env
- Sign up at mapbox.com
- Go to Account → Access Tokens → copy your Default Public Token
- Paste into
.envasMAPBOX_TOKEN
| Layer | Technology |
|---|---|
| Runtime | Node.js |
| Framework | Express.js |
| Database | MongoDB + Mongoose |
| Auth | Passport.js + passport-local-mongoose |
| Image storage | Cloudinary + Multer |
| Maps | Mapbox GL JS v2.15 |
| Geocoding | Mapbox Geocoding API |
| Templates | EJS |
| UI | Bootstrap 5.3 |
| Validation | Joi |
| Config | dotenv |
Mandeep Sharma | © 2025 Wanderlust. All rights reserved.