Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"env": {
"commonjs": true,
"es6": true,
"node": true
},
"extends": [
"airbnb-base"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
}
}
25 changes: 9 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
# Unit 7 Problem Set #4
## Full Stack To-Do w/ Authentication

## Project Overview
The purpose of this Problem Set is to merge your work from Problem Set 7.1 and Problem Set 7.3. You will rebuild your MVC To-Do list from 7.1 using the RESTful API that you created in 7.3. Instead of sending rendered templates in response to an incoming request, you will make AJAX requests to your backend, handle JSON responses, and render to-do's to the DOM.
## The app.js file contains all the main logic that this to-do app uses
### With this App you can:
1. Create a user account
2. Login and out of the account
3. Create tasks
4. Delete tasks
5. View tasks
6. And update the completion status

### Project Requirements
* Your To-Do list app must handle multiple users.
* Users must register with a username and a password.
* Users should be able to login with a username and password.
* Each user has their own To-Do List, to which they can create, update, view, and delete To-Do's.
* User must be able to logout and be redirected back to the login screen.

## Submission Directions
1. Fork, clone, and create your project in this repo.
2. Update this README so that instead of project directions, it houses the documentation for your project. It can be very simple. [Here's an example](https://github.com/emtes/assignment-todo-list) from Enmanuel!
3. Deploy to Heroku and be sure to include the project URL in your README.

### Due Date
Tuesday, April 7 at 9AM
[Heroku App](https://secure-savannah-81963.herokuapp.com/)
59 changes: 59 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const express = require('express');

const app = express();
const port = process.env.PORT || 8080;

const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');

const path = require('path');
const userController = require('./controllers/user');
const tasksController = require('./controllers/task');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(express.static('public'));

// render home page
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '/views/index.html'));
});

// render sign up page
app.get('/signUp', (req, res) => {
res.sendFile(path.join(__dirname, '/views/sign-up.html'));
});
app.post('/signUp', userController.signUp);

// render the login page
app.get('/login', (req, res) => {
res.sendFile(path.join(__dirname, '/views/login.html'));
});
app.post('/login', userController.login);

//logoutUser
app.get('/logout', userController.logout);

//verifies that the user is logged in
app.use(userController.authenticate);

// creating task
app.get('/createTask', (req, res) => {
res.sendFile(path.join(__dirname, '/views/add.html'));
});
app.post('/createTask', tasksController.createTask);


app.put('/tasks/:taskId/:completed', tasksController.updateTask);

// render the tasks page for the user
app.get('/tasks', (req, res) => {
res.sendFile(path.join(__dirname, '/views/tasks.html'));
});
app.get('/userTasks', tasksController.getAllTasks);

//deleteTask
app.delete('/tasks/:taskId', tasksController.deleteTask);

app.listen(port, () => console.log(`Now listening on port ${port}`));
72 changes: 72 additions & 0 deletions controllers/task.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const Task = require('../models/Task');
const jwt = require('jsonwebtoken');

// usersTasks
const getAllTasks = (req, res) => {
const { userToken } = req.cookies;
const payload= jwt.decode(userToken);
const { id } = payload;

Task.getTasks(id)
.then((data) => res.json(data.rows))
.catch((err) => {
console.log(err);
res.status(500).send(err);
});
};

// createTask
const createTask = (req, res) => {
const { userToken } = req.cookies;
const payload= jwt.decode(userToken);
const { id } = payload;
const { dateDue, title, description } = req.body;

Task.createTask(id, dateDue, title, description)
.then(() => Task.getLastCreated())
.then((user) => res.status(201).json(user.rows[0]))
.catch((err) => {
console.log(err);
res.status(500).send(err);
});

res.redirect('/tasks');
};


// tasks/taskId
const updateTask = (req, res) => {
const { userToken } = req.cookies;
const payload= jwt.decode(userToken);
const { id } = payload;
const { taskId, completed } = req.params;

Task.updateTask(taskId, id, completed)
.then((data) => res.json(data.rows))
.catch((err) => {
console.log(err);
res.status(500).send(err);
});
};

// tasks/taskId
const deleteTask = (req, res) => {
const { userToken } = req.cookies;
const payload= jwt.decode(userToken);
const { id } = payload;
const { taskId } = req.params;

Task.deleteTask(taskId, id)
.then((data) => res.json(data.rows))
.catch((err) => {
console.log(err);
res.status(500).send(err);
});
};

module.exports = {
getAllTasks,
createTask,
updateTask,
deleteTask,
};
109 changes: 109 additions & 0 deletions controllers/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const User = require('../models/User');

// signUp
const signUp = (req, res) => {
const { email, password } = req.body;

const saltRounds = 8;

bcrypt.hash(password, saltRounds)
.then((hashedPassword) => {
console.log('Hashed Password: ', hashedPassword);
User.createUser(email, hashedPassword);
})
.then(() => {
res.status(201).redirect('/login');
})
.catch((err) => {
console.log(err);
res.send(err);
});
};

// refactor login with cookies etc
// login
const login = async (req, res) => {
const { email, password } = req.body;

try {
const user = await User.getUserByEmail(email);

if (!user) {
return res.status(403).send('No User with that Email');
}

const validPassword = await bcrypt.compare(password, user.password);

if (!validPassword) {
return res.status(403).send('Wrong Password');
}

const payload = {
id: user.id,
email: user.email,
password: user.password,
};

const privateKey = 'secret';

jwt.sign(payload, privateKey, (err, hashedPayload) => {
if (err) {
console.log(err);
res.status(500).send(err);
}
console.log('JWT: ', hashedPayload);
res.cookie('userToken', hashedPayload).redirect('/tasks');
});
} catch (err) {
console.log(err);
res.send(err);
}
};

// verfying
const authenticate = async (req, res, next) => {
console.log('verifying user');
console.log(req.cookies);

const { userToken } = req.cookies;

if (!userToken) {
return res.status(401).redirect('/');
}

const user = jwt.verify(req.cookies.userToken, 'secret');

const { email, password } = user;

try {
const userInfo = await User.getUserByEmail(email);

if (!userInfo) {
return res.status(403).send('No User with that Email');
}

if (password === userInfo.password) {
return next();
}
} catch (err) {
console.log(err);
return res.status(500).send(err);
}
};


// logout
const logout = (req, res) => {
res.clearCookie('userToken');
res.redirect('/login');
};


module.exports = {
signUp,
login,
authenticate,
logout,
};
11 changes: 11 additions & 0 deletions db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const { Pool } = require('pg');
const localConnect = 'postgresql://postgres@localhost:5432/to_do';

const pool = new Pool({
connectionString: process.env.DATABASE_URL || localConnect,
});


module.exports = {
query: (text, params) => pool.query(text, params),
};
28 changes: 28 additions & 0 deletions models/Task.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const db = require('../db');

class Task {
static getTasks(userId) {
return db.query('SELECT * FROM tasks WHERE tasks.user_id = $1;', [userId]);
}

static createTask(userId, dateDue, title, description) {
const queryText = 'INSERT INTO tasks (user_id, date_due, completed, title, description) VALUES ($1, $2, $3, $4, $5);';
return db.query(queryText, [userId, dateDue, false, title, description]);
}

static getLastCreated(userId) {
return db.query('SELECT * FROM tasks WHERE tasks.user_id = $1 ORDER BY id DESC LIMIT 1;', [userId]);
}

static updateTask(taskId, userId, completed) {
const queryText = 'UPDATE tasks SET completed = $1 WHERE (tasks.id = $2 AND tasks.user_id = $3);';

return db.query(queryText, [completed, taskId, userId]);
}

static deleteTask(taskId, userId) {
return db.query('DELETE FROM tasks WHERE (tasks.id = $1 AND tasks.user_id = $2);', [taskId, userId]);
}
}

module.exports = Task;
16 changes: 16 additions & 0 deletions models/User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const db = require('../db');

class User {
static createUser(email, password) {
const queryText = 'INSERT INTO users (email, password) VALUES ($1, $2);';
return db.query(queryText, [email, password]);
}

static getUserByEmail(email) {
const queryText = 'SELECT * FROM users WHERE users.email = $1;';
return db.query(queryText, [email])
.then((data) => data.rows[0]);
}
}

module.exports = User;
Loading