Skip to content

Commit

Permalink
feat: foss weekened init
Browse files Browse the repository at this point in the history
  • Loading branch information
rootCircle committed Mar 14, 2024
0 parents commit 50a9b8b
Show file tree
Hide file tree
Showing 37 changed files with 32,052 additions and 0 deletions.
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
## Description

This is MERN auth application


It includes the following:

- Backend API with Express & MongoDB
- Routes for auth, logout, register.
- JWT authentication stored in HTTP-only cookie
- Protected routes and endpoints
- Custom middleware to check JSON web token and store in cookie
- React frontend to register, login, logout.

## Usage

- Create a MongoDB database and obtain your `MongoDB URI` - [MongoDB Atlas](https://www.mongodb.com/cloud/atlas/register)

### Env Variables

Rename the `.env.example` file to `.env` and add the following

```
PORT = 4000
MONGO_URI = your mongodb uri
JWT_SECRET = 'abc123'
```

Change the JWT_SECRET to what you want

### Install Dependencies (frontend & backend)

```
cd backend
npm install
cd frontend
npm install
```

### Run

```
# Run frontend (:3000) & backend (:4000)
npm start
```

## Build & Deploy

```
# Create frontend prod build
cd frontend
npm run build
```
2 changes: 2 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/node_modules
.env
103 changes: 103 additions & 0 deletions backend/controllers/taskController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
const Task = require('../models/taskModel')
const mongoose = require('mongoose')

// get all tasks
const getTasks = async (req, res) => {
const user_id = req.user._id

const tasks = await Task.find({user_id})

res.status(200).json(tasks)
}

// get a single task
const getTask = async (req, res) => {
const { id } = req.params

if (!mongoose.Types.ObjectId.isValid(id)) {
return res.status(404).json({error: 'No such task'})
}

const task = await Task.findById(id)

if (!task) {
return res.status(404).json({error: 'No such task'})
}

res.status(200).json(task)
}


// create new task
const createTask = async (req, res) => {
const {title, description, progress} = req.body

let emptyFields = []

if(!title) {
emptyFields.push('title')
}
if(!description) {
emptyFields.push('description')
}
if(!progress) {
emptyFields.push('progress')
}
if(emptyFields.length > 0) {
return res.status(400).json({ error: 'Please fill in all the fields', emptyFields })
}

// add doc to db
try {
const user_id = req.user._id
const task = await Task.create({title, description, progress, user_id})
res.status(200).json(task)
} catch (error) {
res.status(400).json({error: error.message})
}
}

// delete a task
const deleteTask = async (req, res) => {
const { id } = req.params

if (!mongoose.Types.ObjectId.isValid(id)) {
return res.status(404).json({error: 'No such task'})
}

const task = await Task.findOneAndDelete({_id: id})

if (!task) {
return res.status(400).json({error: 'No such task'})
}

res.status(200).json(task)
}

// update a task
const updateTask = async (req, res) => {
const { id } = req.params

if (!mongoose.Types.ObjectId.isValid(id)) {
return res.status(404).json({error: 'No such task'})
}

const task = await Task.findOneAndUpdate({_id: id}, {
...req.body
})

if (!task) {
return res.status(400).json({error: 'No such task'})
}

res.status(200).json(task)
}


module.exports = {
getTasks,
getTask,
createTask,
deleteTask,
updateTask
}
40 changes: 40 additions & 0 deletions backend/controllers/userController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const User = require('../models/userModel')
const jwt = require('jsonwebtoken')

const createToken = (_id) => {
return jwt.sign({_id}, process.env.SECRET, { expiresIn: '3d' })
}

// login a user
const loginUser = async (req, res) => {
const {email, password} = req.body

try {
const user = await User.login(email, password)

// create a token
const token = createToken(user._id)

res.status(200).json({email, token})
} catch (error) {
res.status(400).json({error: error.message})
}
}

// signup a user
const signupUser = async (req, res) => {
const {email, password} = req.body

try {
const user = await User.signup(email, password)

// create a token
const token = createToken(user._id)

res.status(200).json({email, token})
} catch (error) {
res.status(400).json({error: error.message})
}
}

module.exports = { signupUser, loginUser }
26 changes: 26 additions & 0 deletions backend/middleware/requireAuth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const jwt = require('jsonwebtoken')
const User = require('../models/userModel')

const requireAuth = async (req, res, next) => {
// verify user is authenticated
const { authorization } = req.headers

if (!authorization) {
return res.status(401).json({error: 'Authorization token required'})
}

const token = authorization.split(' ')[1]

try {
const { _id } = jwt.verify(token, process.env.SECRET)

req.user = await User.findOne({ _id }).select('_id')
next()

} catch (error) {
console.log(error)
res.status(401).json({error: 'Request is not authorized'})
}
}

module.exports = requireAuth
24 changes: 24 additions & 0 deletions backend/models/taskModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const mongoose = require('mongoose')

const Schema = mongoose.Schema

const taskSchema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
progress: {
type: Number,
required: true
},
user_id: {
type: String,
required: true
}
}, { timestamps: true })

module.exports = mongoose.model('Task', taskSchema)
67 changes: 67 additions & 0 deletions backend/models/userModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const mongoose = require('mongoose')
const bcrypt = require('bcrypt')
const validator = require('validator')

const Schema = mongoose.Schema

const userSchema = new Schema({
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
})

// static signup method
userSchema.statics.signup = async function(email, password) {

// validation
if (!email || !password) {
throw Error('All fields must be filled')
}
if (!validator.isEmail(email)) {
throw Error('Email not valid')
}
if (!validator.isStrongPassword(password)) {
throw Error('Password not strong enough')
}

const exists = await this.findOne({ email })

if (exists) {
throw Error('Email already in use')
}

const salt = await bcrypt.genSalt(10)
const hash = await bcrypt.hash(password, salt)

const user = await this.create({ email, password: hash })

return user
}

// static login method
userSchema.statics.login = async function(email, password) {

if (!email || !password) {
throw Error('All fields must be filled')
}

const user = await this.findOne({ email })
if (!user) {
throw Error('Incorrect email')
}

const match = await bcrypt.compare(password, user.password)
if (!match) {
throw Error('Incorrect password')
}

return user
}

module.exports = mongoose.model('User', userSchema)
Loading

0 comments on commit 50a9b8b

Please sign in to comment.