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
1 change: 1 addition & 0 deletions changmin-todolist/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"body-parser": "^1.19.1",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"crypto": "^1.0.1",
"dotenv": "^10.0.0",
"express": "^4.17.2",
"jsonwebtoken": "^8.5.1",
Expand Down
2 changes: 1 addition & 1 deletion changmin-todolist/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Changmin TodoList</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
28 changes: 19 additions & 9 deletions changmin-todolist/server/controllers/todoController.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ function connectDB() {
}

exports.getTodoList = async (req, res) => {
const username = req.body.username;

const pool = connectDB();
const conn = await pool.getConnection();
try {
const [rows] = await conn.query("SELECT * FROM todo");
const [rows] = await conn.query("SELECT * FROM todo WHERE username = ?", [
req.body.username,
]);
console.log(rows);
res.send(rows);
} catch (err) {
Expand All @@ -31,32 +35,38 @@ exports.getTodoList = async (req, res) => {
}
};

exports.postTodoList = async (req, res) => {
exports.editTodoList = async (req, res) => {
const username = req.body.username;
const action = req.body.action;

console.log(action);

const pool = connectDB();
const conn = await pool.getConnection();
try {
let sql;
let sql, values;
switch (action.type) {
case "CREATE":
sql = `INSERT INTO todo (id, text, done) VALUES (${action.todo.id}, '${action.todo.text}', ${action.todo.done})`;
sql = "INSERT INTO todo (username, id, text, done) VALUES (?, ?, ?, ?)";
values = [username, action.todo.id, action.todo.text, action.todo.done];
break;
case "TOGGLE":
sql = `UPDATE todo SET done = 1 - done WHERE id = ${action.id}`;
sql = "UPDATE todo SET done = 1 - done WHERE username = ? AND id = ?";
values = [username, action.id];
break;
case "REMOVE":
sql = `DELETE FROM todo WHERE id = ${action.id}`;
sql = "DELETE FROM todo WHERE username = ? AND id = ?";
values = [username, action.id];
break;
case "EDIT":
sql = `UPDATE todo SET text = '${action.editText}' WHERE id = ${action.id}`;
sql = "UPDATE todo SET text = ? WHERE username = ? AND id = ?";
values = [action.editText, username, action.id];
break;
default:
throw new Error(`Undefined Action: ${action.type}`);
}
console.log(sql);
await conn.query(sql);
console.log(`${sql} / ${values}`);
await conn.query(sql, values);
res.sendStatus(200);
} catch (err) {
console.log(err);
Expand Down
67 changes: 42 additions & 25 deletions changmin-todolist/server/controllers/userController.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const jwt = require("jsonwebtoken");
const mysql = require("mysql2/promise");
const dotenv = require("dotenv").config();
const { randomBytes, scrypt } = require("crypto");
const options = {
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
Expand All @@ -25,25 +26,32 @@ exports.createToken = async (req, res) => {
const conn = await pool.getConnection();

try {
const [rows] = await conn.query(
`SELECT * FROM user WHERE username = '${req.body.username}' AND password = '${req.body.password}'`
);
const [rows] = await conn.query("SELECT * FROM user WHERE username = ?", [
req.body.username,
]);

if (rows.length) {
const token = jwt.sign(
{
username: rows[0].username,
},
SECRET_KEY,
{
expiresIn: "1h",
}
);
res.cookie("user", token, {
path: "/",
maxAge: 60 * 60 * 1000,
const [password, salt] = rows[0].password.split("$");

scrypt(req.body.password, salt, 64, (err, derivedKey) => {
if (err) throw err;
if (derivedKey.toString("base64") == password) {
const token = jwt.sign(
{
username: rows[0].username,
},
SECRET_KEY,
{
expiresIn: "1h",
}
);
res.cookie("user", token, {
path: "/",
maxAge: 60 * 60 * 1000,
});
res.send("OK");
} else res.send("USER_NOT_FOUND");
});
res.send("OK");
} else {
res.send("USER_NOT_FOUND");
}
Expand All @@ -62,14 +70,20 @@ exports.createNewUser = async (req, res) => {
const conn = await pool.getConnection();

try {
const [rows] = await conn.query(
`SELECT * FROM user WHERE username = '${req.body.username}'`
);
const [rows] = await conn.query("SELECT * FROM user WHERE username = ?", [
req.body.username,
]);

if (!rows.length) {
await conn.query(
`INSERT INTO user (username, password) VALUES ('${req.body.username}', '${req.body.password}')`
);
const salt = randomBytes(32).toString("base64");

scrypt(req.body.password, salt, 64, async (err, derivedKey) => {
if (err) throw err;
await conn.query(
"INSERT INTO user (username, password) VALUES (?, ?)",
[req.body.username, `${derivedKey.toString("base64")}$${salt}`]
);
});
res.send("OK");
} else {
res.send("USER_EXISTS");
Expand All @@ -89,9 +103,12 @@ exports.removeUser = async (req, res) => {
const conn = await pool.getConnection();

try {
await conn.query(
`DELETE FROM user WHERE username = '${req.body.username}'`
);
await conn.query("DELETE FROM user WHERE username = ?", [
req.body.username,
]);
await conn.query("DELETE FROM todo WHERE username = ?", [
req.body.username,
]);
res.send("OK");
} catch (err) {
console.log(err);
Expand Down
6 changes: 2 additions & 4 deletions changmin-todolist/server/routes/todo.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ const express = require("express");
const router = express.Router();
const todoController = require("../controllers/todoController");

router
.route("/")
.get(todoController.getTodoList)
.post(todoController.postTodoList);
router.post("/get", todoController.getTodoList);
router.post("/edit", todoController.editTodoList);

module.exports = router;
4 changes: 2 additions & 2 deletions changmin-todolist/server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ app.use(cors({ origin: true, credentials: true }));
app.use(bodyParser.json());
app.use(cookieParser());

app.use("/todo", todoRouter);
app.use("/user", userRouter);
app.use("/api/todo", todoRouter);
app.use("/api/user", userRouter);

app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
Expand Down
51 changes: 35 additions & 16 deletions changmin-todolist/src/App.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import Main from "./Main";
import styled, { createGlobalStyle } from "styled-components";
import { Routes, Route, Link } from "react-router-dom";
import { Routes, Route, Link, Navigate } from "react-router-dom";
import TodoTemplate from "./components/TodoTemplate";
import LoginForm from "./components/LoginForm";
import MenuTemplate from "./components/MenuTemplate";
import { TodoProvider } from "./components/TodoContext";
import { useEffect, useState } from "react";
import axios from "axios";
import { useContext, useEffect } from "react";
import Cookies from "universal-cookie";
import UserContext from "./contexts/UserContext";
import { TodoAPI } from "./utils/axios";
import Page from "./Page";

const cookies = new Cookies();

Expand All @@ -34,26 +36,27 @@ const MenuStyle = {
};

const App = () => {
const [currentUsername, setCurrentUsername] = useState();
const { state, actions } = useContext(UserContext);

async function verifyToken() {
await axios
.post("http://localhost:3001/user/auth", null, {
withCredentials: true,
})
await TodoAPI.post("/user/auth", null, {
withCredentials: true,
})
.then((res) => {
setCurrentUsername(res.data);
actions.setUsername(res.data);
})
.catch((err) => {
console.log("Invalid token, removing the cookie");
actions.setUsername(null);
cookies.remove("user");
});
}

useEffect(() => {
if (cookies.get("user") && cookies.get("user") !== "undefined")
verifyToken();
}, []);
else actions.setUsername(null);
}, [state]);

return (
<TodoProvider>
Expand All @@ -66,20 +69,36 @@ const App = () => {
</Menu>
<Menu>
<Link to="/login" style={MenuStyle}>
{!currentUsername ? "Login" : currentUsername}
{!state.username ? "Login" : state.username}
</Link>
</Menu>
</MenuTemplate>
<TodoTemplate>
<Routes>
<Route path="/" element={<Main />} />
<Route
path="/"
element={
state.username !== null ? (
<Page>
<Main />
</Page>
) : (
<Navigate to="/login" />
)
}
/>
<Route
path="/login"
element={
<LoginForm
currentUsername={currentUsername}
setCurrentUsername={setCurrentUsername}
/>
state.username !== null ? (
<Page title="User Info">
<LoginForm />
</Page>
) : (
<Page title="Login">
<LoginForm />
</Page>
)
}
/>
</Routes>
Expand Down
12 changes: 12 additions & 0 deletions changmin-todolist/src/Page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useEffect } from "react";

const Page = (props) => {
useEffect(() => {
document.title = `${
props.title ? `${props.title} - ` : ""
}Changmin TodoList`;
}, [props.title]);
return props.children;
};

export default Page;
Loading