This project is for submission of final project for WAP 472 and is developed using NodeJS, ExpressJS, EJS, Mongoose
- Setup Express app
- Create folder structure for MVC
- Create and setup server.js
- Create Views
- create partial layouts
- implement bootstrap
- create individual layouts for cars
- create individual layouts for makes
- create individual layouts for login and registration
- Create Controllers
- Setup project for using Mongoose
- Create Models
- Write Schemas for User
- Seed User data
- Write Schemas for Cars
- Seed Car data
- Write Schemas for makes
- Seed Make data
- Add filter with
- New and Used
- Model
- Price
- Make
- Distance
- Add Sort by price
- Implement Authentication
- Implement Remember Me feature during Login
- (Optional) Add image upload feature when adding cars
- (Optional) Validate Inputs
- (Optional) Setup unit testing for Controllers
.
└── Project/
├── controllers/
│ ├── carController.js
│ ├── loginController.js
│ └── makeController.js
├── middlewares/
│ └── authTokens.js
├── models/
│ ├── carSchema.js
│ ├── makeSchema.js
│ └── userSchama.js
├── node_modules/
│ └── ...
├── public/
│ ├── img/
│ └── uploads/
├── routes/
│ ├── cars.js
│ ├── index.js
│ └── makes.js
├── seeds/
│ ├── carSeeds.js
│ └── makeSeeds.js
├── views/
│ ├── cars/
│ │ ├── edit.ejs
│ │ ├── index.ejs
│ │ └── new.ejs
│ ├── makes/
│ │ ├── edit.ejs
│ │ ├── index.ejs
│ │ ├── new.ejs
│ │ └── show.ejs
│ ├── partials/
│ │ ├── footer.ejs
│ │ ├── head.ejs
│ │ ├── header.ejs
│ │ └── select.ejs
│ ├── index.ejs
│ └── register.ejs
├── .env
├── .gitignore
├── package-lock.json
├── package.json
├── Project.pdf
├── readme.me
└── server.js
Name | Path | Verb | Purpose |
---|---|---|---|
Index | / | GET | Display Login Page |
Login | / | POST | Login post Request |
Create | /register | GET | Display registration page |
Register | /register | POST | Register user on server |
Logout | /logout | GET | Logout user from the application |
Name | Path | Verb | Purpose |
---|---|---|---|
Index | /cars | GET | Display all cars |
New | /cars/new | GET | From to create new car |
Create | /cars | POST | Create new car on server |
Show(skip) | /cars/:id | GET | Details for one specific car |
Edit | /cars/:id/edit | GET | From to edit specific car |
Update | /cars/:id | PATCH | Update specific car on server |
Destroy | /cars/:id | DELETE | Delete specific car on server |
Filter | /cars/filter | Get | Filter cars data based on inputs |
Name | Path | Verb | Purpose |
---|---|---|---|
Index | /makes | GET | Display all makes |
New | /makes/new | GET | From to create new make |
Create | /makes | POST | Create new make on server |
Show | /makes/:id | GET | Details for one specific make |
Edit | /makes/:id/edit | GET | From to edit specific make |
Update | /makes/:id | PATCH | Update specific make on server |
Destroy | /makes/:id | DELETE | Delete specific make on server |
Filter | /makes/filter | Get | Filter makes data based on inputs |
Created a query based on what's selected from the Filters form. Cars are fetched using that query from Car model.
You may see sorting order done in the same controller it is because to sort the data that are already filtered using the filter.
const filterCars = async (req, res) => {
let { status, price, make, model, distance, order } = req.query;
let query = {};
let cars = {};
if (status != null) status != 'newandused' ? query.status = status : null;
if (price != null) price != 'all' ? (parseInt(price) > 50000) ? query.price = { $gt: parseInt(price) } : query.price = { $lt: parseInt(price) } : null;
if (make != null) make != 'all' ? query.make = make : null;
if (model != null) model != 'all' ? query.model = model : null;
if (distance != null) distance != 'all' ? (parseInt(distance) > 100000) ? query.distance = { $gt: parseInt(distance) } : query.distance = { $lt: parseInt(distance) } : null;
if (order != null) order == 'asc' ? cars = await Car.find(query).sort({ price: 1 }) : cars = await Car.find(query).sort({ price: -1 });
else cars = await Car.find(query);
let html = getArticleHTML(cars);
res.send({ html })
}
Created different middleware to generate AuthToken, AuthToken is stored in the cookies.
module.exports = {
authTokens: {},
generateAuthToken: () => {
return crypto.randomBytes(30).toString('hex');
},
requireAuth : (req, res, next) => {
if (req.user) {
next();
} else {
res.render('index', {
message: 'Please login to continue',
messageClass: 'alert-danger'
});
}
},
isLoggedIn: false
}
When storing AuthToken in the cookies, if user has selected remember me then expiring time of AuthToken is set to 2 minutes (which is custom and can me modified as required, e.g. for a day). And if user didn't check remember me then AuthToken is set to expire after the session is over (or browser is closed).
const minute = process.env.MAXSESSION * 60 * 1000;
const authToken = generateAuthToken();
authTokens[authToken] = username;
remember? res.cookie('AuthToken', authToken, { maxAge: minute }) : res.cookie('AuthToken', authToken);