Skip to content

Commit 50a9b8b

Browse files
committed
feat: foss weekened init
0 parents  commit 50a9b8b

37 files changed

+32052
-0
lines changed

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
## Description
2+
3+
This is MERN auth application
4+
5+
6+
It includes the following:
7+
8+
- Backend API with Express & MongoDB
9+
- Routes for auth, logout, register.
10+
- JWT authentication stored in HTTP-only cookie
11+
- Protected routes and endpoints
12+
- Custom middleware to check JSON web token and store in cookie
13+
- React frontend to register, login, logout.
14+
15+
## Usage
16+
17+
- Create a MongoDB database and obtain your `MongoDB URI` - [MongoDB Atlas](https://www.mongodb.com/cloud/atlas/register)
18+
19+
### Env Variables
20+
21+
Rename the `.env.example` file to `.env` and add the following
22+
23+
```
24+
PORT = 4000
25+
MONGO_URI = your mongodb uri
26+
JWT_SECRET = 'abc123'
27+
```
28+
29+
Change the JWT_SECRET to what you want
30+
31+
### Install Dependencies (frontend & backend)
32+
33+
```
34+
cd backend
35+
npm install
36+
cd frontend
37+
npm install
38+
```
39+
40+
### Run
41+
42+
```
43+
44+
# Run frontend (:3000) & backend (:4000)
45+
npm start
46+
47+
```
48+
49+
## Build & Deploy
50+
51+
```
52+
# Create frontend prod build
53+
cd frontend
54+
npm run build
55+
```

backend/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/node_modules
2+
.env
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
const Task = require('../models/taskModel')
2+
const mongoose = require('mongoose')
3+
4+
// get all tasks
5+
const getTasks = async (req, res) => {
6+
const user_id = req.user._id
7+
8+
const tasks = await Task.find({user_id})
9+
10+
res.status(200).json(tasks)
11+
}
12+
13+
// get a single task
14+
const getTask = async (req, res) => {
15+
const { id } = req.params
16+
17+
if (!mongoose.Types.ObjectId.isValid(id)) {
18+
return res.status(404).json({error: 'No such task'})
19+
}
20+
21+
const task = await Task.findById(id)
22+
23+
if (!task) {
24+
return res.status(404).json({error: 'No such task'})
25+
}
26+
27+
res.status(200).json(task)
28+
}
29+
30+
31+
// create new task
32+
const createTask = async (req, res) => {
33+
const {title, description, progress} = req.body
34+
35+
let emptyFields = []
36+
37+
if(!title) {
38+
emptyFields.push('title')
39+
}
40+
if(!description) {
41+
emptyFields.push('description')
42+
}
43+
if(!progress) {
44+
emptyFields.push('progress')
45+
}
46+
if(emptyFields.length > 0) {
47+
return res.status(400).json({ error: 'Please fill in all the fields', emptyFields })
48+
}
49+
50+
// add doc to db
51+
try {
52+
const user_id = req.user._id
53+
const task = await Task.create({title, description, progress, user_id})
54+
res.status(200).json(task)
55+
} catch (error) {
56+
res.status(400).json({error: error.message})
57+
}
58+
}
59+
60+
// delete a task
61+
const deleteTask = async (req, res) => {
62+
const { id } = req.params
63+
64+
if (!mongoose.Types.ObjectId.isValid(id)) {
65+
return res.status(404).json({error: 'No such task'})
66+
}
67+
68+
const task = await Task.findOneAndDelete({_id: id})
69+
70+
if (!task) {
71+
return res.status(400).json({error: 'No such task'})
72+
}
73+
74+
res.status(200).json(task)
75+
}
76+
77+
// update a task
78+
const updateTask = async (req, res) => {
79+
const { id } = req.params
80+
81+
if (!mongoose.Types.ObjectId.isValid(id)) {
82+
return res.status(404).json({error: 'No such task'})
83+
}
84+
85+
const task = await Task.findOneAndUpdate({_id: id}, {
86+
...req.body
87+
})
88+
89+
if (!task) {
90+
return res.status(400).json({error: 'No such task'})
91+
}
92+
93+
res.status(200).json(task)
94+
}
95+
96+
97+
module.exports = {
98+
getTasks,
99+
getTask,
100+
createTask,
101+
deleteTask,
102+
updateTask
103+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const User = require('../models/userModel')
2+
const jwt = require('jsonwebtoken')
3+
4+
const createToken = (_id) => {
5+
return jwt.sign({_id}, process.env.SECRET, { expiresIn: '3d' })
6+
}
7+
8+
// login a user
9+
const loginUser = async (req, res) => {
10+
const {email, password} = req.body
11+
12+
try {
13+
const user = await User.login(email, password)
14+
15+
// create a token
16+
const token = createToken(user._id)
17+
18+
res.status(200).json({email, token})
19+
} catch (error) {
20+
res.status(400).json({error: error.message})
21+
}
22+
}
23+
24+
// signup a user
25+
const signupUser = async (req, res) => {
26+
const {email, password} = req.body
27+
28+
try {
29+
const user = await User.signup(email, password)
30+
31+
// create a token
32+
const token = createToken(user._id)
33+
34+
res.status(200).json({email, token})
35+
} catch (error) {
36+
res.status(400).json({error: error.message})
37+
}
38+
}
39+
40+
module.exports = { signupUser, loginUser }

backend/middleware/requireAuth.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const jwt = require('jsonwebtoken')
2+
const User = require('../models/userModel')
3+
4+
const requireAuth = async (req, res, next) => {
5+
// verify user is authenticated
6+
const { authorization } = req.headers
7+
8+
if (!authorization) {
9+
return res.status(401).json({error: 'Authorization token required'})
10+
}
11+
12+
const token = authorization.split(' ')[1]
13+
14+
try {
15+
const { _id } = jwt.verify(token, process.env.SECRET)
16+
17+
req.user = await User.findOne({ _id }).select('_id')
18+
next()
19+
20+
} catch (error) {
21+
console.log(error)
22+
res.status(401).json({error: 'Request is not authorized'})
23+
}
24+
}
25+
26+
module.exports = requireAuth

backend/models/taskModel.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const mongoose = require('mongoose')
2+
3+
const Schema = mongoose.Schema
4+
5+
const taskSchema = new Schema({
6+
title: {
7+
type: String,
8+
required: true
9+
},
10+
description: {
11+
type: String,
12+
required: true
13+
},
14+
progress: {
15+
type: Number,
16+
required: true
17+
},
18+
user_id: {
19+
type: String,
20+
required: true
21+
}
22+
}, { timestamps: true })
23+
24+
module.exports = mongoose.model('Task', taskSchema)

backend/models/userModel.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
const mongoose = require('mongoose')
2+
const bcrypt = require('bcrypt')
3+
const validator = require('validator')
4+
5+
const Schema = mongoose.Schema
6+
7+
const userSchema = new Schema({
8+
email: {
9+
type: String,
10+
required: true,
11+
unique: true
12+
},
13+
password: {
14+
type: String,
15+
required: true
16+
}
17+
})
18+
19+
// static signup method
20+
userSchema.statics.signup = async function(email, password) {
21+
22+
// validation
23+
if (!email || !password) {
24+
throw Error('All fields must be filled')
25+
}
26+
if (!validator.isEmail(email)) {
27+
throw Error('Email not valid')
28+
}
29+
if (!validator.isStrongPassword(password)) {
30+
throw Error('Password not strong enough')
31+
}
32+
33+
const exists = await this.findOne({ email })
34+
35+
if (exists) {
36+
throw Error('Email already in use')
37+
}
38+
39+
const salt = await bcrypt.genSalt(10)
40+
const hash = await bcrypt.hash(password, salt)
41+
42+
const user = await this.create({ email, password: hash })
43+
44+
return user
45+
}
46+
47+
// static login method
48+
userSchema.statics.login = async function(email, password) {
49+
50+
if (!email || !password) {
51+
throw Error('All fields must be filled')
52+
}
53+
54+
const user = await this.findOne({ email })
55+
if (!user) {
56+
throw Error('Incorrect email')
57+
}
58+
59+
const match = await bcrypt.compare(password, user.password)
60+
if (!match) {
61+
throw Error('Incorrect password')
62+
}
63+
64+
return user
65+
}
66+
67+
module.exports = mongoose.model('User', userSchema)

0 commit comments

Comments
 (0)