- List all bootcamps in the database
- Pagination
- Select specific fields in result
- Limit number of results
- Filter by fields
- Search bootcamps by radius from zipcode
- Use a geocoder to get exact location and coords from a single address field
- Get single bootcamp
- Create new bootcamp
- Authenticated users only
- Must have the role "publisher" or "admin"
- Only one bootcamp per publisher (admins can create more)
- Field validation via Mongoose
- Upload a photo for bootcamp
- Owner only
- Photo will be uploaded to local filesystem
- Update bootcamps
- Owner only
- Validation on update
- Delete Bootcamp
- Owner only
- Calculate the average cost of all courses for a bootcamp
- Calculate the average rating from the reviews for a bootcamp
- List all courses for bootcamp
- List all courses in general
- Pagination, filtering, etc
- Get single course
- Create new course
- Authenticated users only
- Must have the role "publisher" or "admin"
- Only the owner or an admin can create a course for a bootcamp
- Publishers can create multiple courses
- Update course
- Owner only
- Delete course
- Owner only
- List all reviews for a bootcamp
- List all reviews in general
- Pagination, filtering, etc
- Get a single review
- Create a review
- Authenticated users only
- Must have the role "user" or "admin" (no publishers)
- Update review
- Must have the role "user" or "admin" (no publishers)
- Delete review
- Must have the role "user" or "admin" (no publishers)
- Authentication will be ton using JWT/cookies
- JWT and cookie should expire in 30 days
- User registration
- Register as a "user" or "publisher"
- Once registered, a token will be sent along with a cookie (token = xxx)
- Passwords must be hashed
- User login
- User can login with email and password
- Plain text password will compare with stored hashed password
- Once logged in, a token will be sent along with a cookie (token = xxx)
- User logout
- Cookie will be sent to set token = none
- Get user
- Route to get the currently logged in user (via token)
- Password reset (lost password)
- User can request to reset password
- A hashed token will be emailed to the users registered email address
- A put request can be made to the generated url to reset password
- The token will expire after 10 minutes
- Update user info
- Authenticated user only
- Separate route to update password
- User CRUD
- Admin only
- Users can only be made admin by updating the database field manually
- Encrypt passwords and reset tokens
- Prevent NoSQL injections
- Add headers for security (helmet)
- Prevent cross site scripting - XSS
- Add a rate limit for requests of 100 requests per 10 minutes
- Protect against http param polution
- Use cors to make API public (for now)
- Use Postman to create documentation
- Use docgen to create HTML files from Postman JSON File
- Add html files as the / route for the api
toJSON: {virtuals: true},
toObject: {virtuals: true}
BootcampSchema.virtual('courses', {
ref: 'Course',
localField: '_id',
foreignField: 'bootcamp',
justOne: false
});
query = Bootcamp.find(JSON.parse(queryString)).populate('courses');
BootcampSchema.pre('remove', async function (next) {
console.log(`Course being removed from bootcamp: ${this._id}`);
await this.model('Course').deleteMany({bootcamp: this._id});
next();
})
const bootcamp = await Bootcamp.findById(req.params.id);
bootcamp.remove();
CourseSchema.statics.getAverageCost = async function (bootcampId) {
const obj = await this.aggregate([
{
$match: {bootcamp: bootcampId}
},
{
$group: {
_id: '$bootcamp',
averageCost: {$avg: '$tuition'}
}
}
]);
try {
await this.model('Bootcamp').findByIdAndUpdate(bootcampId, {
averageCost: Math.ceil(obj[0].averageCost / 10) * 10
})
} catch (errors) {
console.log(errors);
}
}
//Call AverageCost After Add Course **********************
CourseSchema.post('save', function () {
this.constructor.getAverageCost(this.bootcamp);
});
//Call AverageCost Before Remove Course ******************
CourseSchema.pre('remove', function () {
this.constructor.getAverageCost(this.bootcamp);
});
UserSchema.pre('save', async function (next) {
if (!this.isModified('password')) {
next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
});
UserSchema.methods.getSignedJwtToken = function () {
return jwt.sign({id: this._id}, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRE
});
};
UserSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
exports.authorize = (...roles) => {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return next(new ErrorResponse(`User Role ${req.user.role} is Not Authorize to access this route`, 403));
}
next();
};
};
req.body.user = req.user.id;
const publishedBootcamp = await Bootcamp.findOne({user: req.user.id});
if (publishedBootcamp && req.user.role !== 'admin') {
return next(new ErrorResponse(`The User with ${req.user.id} Already Published a Bootcamp`, 400));
}
if (bootcamp.user.toString() !== req.user.id && req.user.role !== 'admin') {
return next(new ErrorResponse(`User ${req.user.id} Is Not Authorized to The Bootcamp`, 401));
}
UserSchema.methods.getResetPasswordToken = function () {
const resetToken = crypto.randomBytes(20).toString('hex');
this.resetPasswordToken = crypto
.createHash('sha256')
.update(resetToken)
.digest('hex');
this.resetPasswordExpire = Date.now() + 10 * 60 * 1000;
return resetToken;
};
ReviewSchema.index({bootcamp: 1, user: 1}, {unique: true});
Backend API for the DevCamper application to the manage bootcams
Endpoint:
Method: POST
Type: RAW
URL: {{URL}}/api/v1/auth/forgotpassword
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"email": "admin@gmail.com"
}
Endpoint:
Method: GET
Type:
URL: {{URL}}/api/v1/auth/me
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Endpoint:
Method: POST
Type: RAW
URL: {{URL}}/api/v1/auth/login
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"email": "admin@gmail.com",
"password": "123456"
}
Endpoint:
Method: GET
Type:
URL: {{URL}}/api/v1/auth/logout
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Endpoint:
Method: POST
Type: RAW
URL: {{URL}}/api/v1/auth/register
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"name": "Review3",
"email": "review3@gmail.com",
"role":"user",
"password": "123456"
}
Endpoint:
Method: PUT
Type: RAW
URL: {{URL}}/api/v1/auth/resetpassword/8c7b3dc3892fd8335b9ac57ec4639fc2d1a90fd9
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"password": "123456"
}
Endpoint:
Method: PUT
Type: RAW
URL: {{URL}}/api/v1/auth/updatedetails
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"name": "Admin 2 Update",
"email": "admin2@gmail.com"
}
Endpoint:
Method: PUT
Type: RAW
URL: {{URL}}/api/v1/auth/updatepassword
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"currentPassword":"123456",
"newPassword":"1234567"
}
Bootcamps CRUD functionality
Create New Bootcamp Must be Authenticate By Admin or publishers
Endpoint:
Method: POST
Type: RAW
URL: {{URL}}/api/v1/bootcamps/
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Authorization | Sourav eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVmZmQyY2NmNzY0NzlmMjQ5MDc2NDBjMCIsIm5hbWUiOiJVc2VyIEFjY291bnQiLCJpYXQiOjE2MTA0Mjg5NDAsImV4cCI6MTYxMzAyMDk0MH0.X-Zws9YZiW5f4NKh5_P4HZKiplLSrh4uSuf8TK8nUv4 |
Body:
{
"name": "ModernTech Bootcamp XSS-CLEAN<script>alert(1)</script>",
"description": "Is coding your passion? Codemasters will give you the skills and the tools to become the best developer possible. We specialize in front end and full stack web development",
"website": "https://devcentral.com",
"phone": "(444) 444-4444",
"email": "enroll@devcentral.com",
"address":"Nikunja-2 Dhaka, Dhaka Division 1229, BD",
"careers": [
"Mobile Development",
"Web Development",
"Data Science",
"Business"
],
"housing": false,
"jobAssistance": true,
"jobGuarantee": true,
"acceptGi": true
}
Delete Bootcamp By ID From DB
Endpoint:
Method: DELETE
Type:
URL: {{URL}}/api/v1/bootcamps/6005e12bad4a0a3278ecc8f8
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Fetch all bootcamps from database
Endpoint:
Method: GET
Type: RAW
URL: {{URL}}/api/v1/bootcamps/
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{}
Endpoint:
Method: GET
Type: RAW
URL: {{URL}}/api/v1/bootcamps/radius/02215/10
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{}
Get Single Bootcamp By ID
Endpoint:
Method: GET
Type:
URL: {{URL}}/api/v1/bootcamps/5d713a66ec8f2b88b8f830b8
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Endpoint:
Method: GET
Type: RAW
URL: {{URL}}/api/v1/bootcamps/
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Query params:
Key | Value | Description |
---|---|---|
page | 2 | |
limit | 1 | |
select | name |
Body:
{}
Endpoint:
Method: PUT
Type: FORMDATA
URL: {{URL}}/api/v1/bootcamps/5d725a1b7b292f5f8ceff788/photo
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
Key | Value | Description |
---|---|---|
file |
Endpoint:
Method: GET
Type: RAW
URL: {{URL}}/api/v1/bootcamps/
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Query params:
Key | Value | Description |
---|---|---|
housing | true | |
location.state | MA |
Body:
{}
Endpoint:
Method: GET
Type: RAW
URL: {{URL}}/api/v1/bootcamps/
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Query params:
Key | Value | Description |
---|---|---|
select | name,slug,housing,createdAt | |
sort | name |
Body:
{}
Update Bootcamp By ID from database
Endpoint:
Method: PUT
Type: RAW
URL: {{URL}}/api/v1/bootcamps/5ffe9125e8854807e8960828
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"housing": false
}
CRUD All Courses !!!
Create a course for specific bootcamp
Endpoint:
Method: POST
Type: RAW
URL: {{URL}}/api/v1/bootcamps/5ffea33f8c3f0a2f3853d00b/courses
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"title": "UI/UX By ADMIN",
"description": "In this course you will learn to create beautiful interfaces. It is a mix of design and development to create modern user experiences on both web and mobile",
"weeks": 12,
"tuition": 10000,
"minimumSkill": "intermediate",
"scholarhipsAvailable": true
}
Endpoint:
Method: GET
Type: RAW
URL: {{URL}}/api/v1/bootcamps/5d713a66ec8f2b88b8f830b8/courses
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"title": "UI/UX By SOURAV Update 2",
"description": "In this course you will learn to create beautiful interfaces. It is a mix of design and development to create modern user experiences on both web and mobile",
"weeks": 12,
"tuition": 10000,
"minimumSkill": "intermediate",
"scholarhipsAvailable": true
}
Endpoint:
Method: DELETE
Type: RAW
URL: {{URL}}/api/v1/courses/5ffea3948c3f0a2f3853d00c
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"tuition": 12000,
"minimumSkill": "advanced"
}
Endpoint:
Method: GET
Type: RAW
URL: {{URL}}/api/v1/courses/5d725a4a7b292f5f8ceff789
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{}
Endpoint:
Method: GET
Type: RAW
URL: {{URL}}/api/v1/courses/
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{}
Endpoint:
Method: GET
Type: RAW
URL: {{URL}}/api/v1/bootcamps/5d713a66ec8f2b88b8f830b8/courses
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{}
Endpoint:
Method: PUT
Type: RAW
URL: {{URL}}/api/v1/courses/5ffea3948c3f0a2f3853d00c
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"tuition": 12000,
"minimumSkill": "advanced"
}
Endpoint:
Method: POST
Type: RAW
URL: {{URL}}/api/v1/bootcamps/60054405d6a84136343e2ad8/reviews
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"title":"Review 1",
"text":"This is the description",
"rating": 8
}
Endpoint:
Method: DELETE
Type:
URL: {{URL}}/api/v1/reviews/600547069b7d8a31a0d9c3d3
Endpoint:
Method: PUT
Type: RAW
URL: {{URL}}/api/v1/reviews/5d7a514b5d2c12c7449be020
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"title":"Update Review Title",
"rating":7
}
Endpoint:
Method: GET
Type: RAW
URL: {{URL}}/api/v1/reviews
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{}
Endpoint:
Method: GET
Type: RAW
URL: {{URL}}/api/v1/bootcamps/60054405d6a84136343e2ad8/reviews
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"title":"Best Review Title 2",
"text":"This is the description",
"rating": 8
}
Endpoint:
Method: PUT
Type:
URL: {{URL}}/api/v1/reviews/6005381b224bfa27389ddc0c
Endpoint:
Method: POST
Type: RAW
URL: {{URL}}/api/v1/users/
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"name":"SOURAV ROY NEW USER",
"email":"sourav@gmail.com",
"password":"123456"
}
Endpoint:
Method: DELETE
Type:
URL: {{URL}}/api/v1/users/6001587dc501af1178b073ae
Endpoint:
Method: GET
Type: RAW
URL: {{URL}}/api/v1/users/
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{}
Endpoint:
Method: GET
Type: RAW
URL: {{URL}}/api/v1/users/600543b6d6a84136343e2ad6
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{}
Endpoint:
Method: PUT
Type: RAW
URL: {{URL}}/api/v1/users/6001587dc501af1178b073ae
Headers:
Key | Value | Description |
---|---|---|
Content-Type | application/json | JSON TYPE |
Body:
{
"name":"SOURAV ROY NEW UPDATE"
}