diff --git a/client/package.json b/client/package.json index 5ef592a..bee806a 100644 --- a/client/package.json +++ b/client/package.json @@ -12,7 +12,9 @@ "axios": "^1.3.0", "jwt-decode": "^3.1.2", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.8.0", + "uuid": "^9.0.0" }, "devDependencies": { "@types/react": "^18.0.26", diff --git a/client/src/App.jsx b/client/src/App.jsx index de95d3c..403ae94 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -1,75 +1,20 @@ -import { useEffect, useState } from 'react'; -import jwt_decode from "jwt-decode"; -import API from './util/API'; -import Auth from './pages/Auth'; +import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import Home from './pages/Home'; +import SingleItem from './pages/protected/SingleItem'; +import Items from './pages/protected/Items'; import './App.css' function App() { - const [user, setUser] = useState(null); - const [contents, setContents] = useState(<>); - - async function handleLogin(info) { - if (!info.email || !info.password) return; - - const response = await API.login(info); - console.log(response); - - const user = jwt_decode(response.token); - console.log(user); - - localStorage.setItem('user', JSON.stringify(user)); - localStorage.setItem('token', response.token); - } - - async function handleLogout() { - await API.logout(); - localStorage.removeItem("user"); - localStorage.removeItem("token"); - } - - async function handleRegister(register) { - if (!register.username || !register.email || !register.password) return; - await API.register(register); - } - - useEffect(() => { - let item = localStorage.getItem('user'); - if (item) { - item = JSON.parse(item); - setUser(item.user); - } - }, []) - - useEffect(() => { - let protectedData; - - if (user) { - (async() => { - protectedData = await API.getItems(); - console.log(protectedData); - })(); - } - - setContents( - user ? ( -
-

Welcome, {user.username}!

-
- -
- -
- ) : ( - - ) - ) - }, [user]) - return ( -
-

Auth Testing

- { contents } -
+ +
+ + } /> + } /> + } /> + +
+
) } diff --git a/client/src/pages/Home.jsx b/client/src/pages/Home.jsx index 45b31c0..44cf4f9 100644 --- a/client/src/pages/Home.jsx +++ b/client/src/pages/Home.jsx @@ -1,24 +1,67 @@ +import { useNavigate } from "react-router-dom"; import { useEffect, useState } from "react"; +import jwt_decode from "jwt-decode"; import API from "../util/API"; +import Auth from "./Auth"; +import Welcome from "./protected/Welcome"; -function Home({ user, handleLogout }) { - const [items, setItems] = useState(null); - console.log(user); +function Home() { + const [user, setUser] = useState(null); + const [contents, setContents] = useState(<>); + const [update, setUpdate] = useState(false); + const navigate = useNavigate(); - async function getStatus() { - const res = await API.validate(); - console.log(res); + async function handleLogin(info) { + if (!info.email || !info.password) return; + + const response = await API.login(info); + const user = jwt_decode(response.token); + + console.log(user); + localStorage.setItem('user', JSON.stringify(user)); + localStorage.setItem('token', response.token); + + setUpdate(!update); + } + + async function handleLogout() { + await API.logout(); + localStorage.removeItem("user"); + localStorage.removeItem("token"); + setUpdate(!update); + + navigate('/'); + } + + async function handleRegister(register) { + if (!register.username || !register.email || !register.password) return; + const response = await API.register(register); + console.log(response); + + if (response.ok) { + await API.login({ email: register.email, password: register.password }); + } + + setUpdate(!update); } + useEffect(() => { + let item = localStorage.getItem('user'); + if (item) { + item = JSON.parse(item); + setUser(item.user); + } + }, [update]) + + useEffect(() => { + setContents(user ? : ) + }, [user, update]) + return ( -
-

Testing user auth workflow with PERN stack

- - - { items } - - -
+
+

This is my auth testing app

+ { contents } +
) } diff --git a/client/src/pages/protected/Items.jsx b/client/src/pages/protected/Items.jsx new file mode 100644 index 0000000..d42c69e --- /dev/null +++ b/client/src/pages/protected/Items.jsx @@ -0,0 +1,40 @@ +import { useState, useEffect } from "react"; +import API from "../../util/API"; +import { v4 } from "uuid"; + +function Items() { + const [items, setItems] = useState(

Loading...

); + const [content, setContent] = useState(); + + useEffect(() => { + (async() => { + try { + const data = await API.getItems(); + setItems(data.map(item => ( +
+ {item.name} +

{item.description}

+
+ ))) + } catch(e) { + setItems( +
+

Access Forbidden

+

{e.message}

+
+ ) + } + })(); + }, []) + + return ( +
+ Home +

List of items!

+ + { items } +
+ ) +} + +export default Items \ No newline at end of file diff --git a/client/src/pages/protected/SingleItem.jsx b/client/src/pages/protected/SingleItem.jsx new file mode 100644 index 0000000..3be1c60 --- /dev/null +++ b/client/src/pages/protected/SingleItem.jsx @@ -0,0 +1,42 @@ +import { useState, useEffect } from "react"; +import { useParams } from "react-router-dom" +import API from "../../util/API"; + +function SingleItem() { + const [content, setContent] = useState(

Loading...

); + const { id } = useParams(); + + useEffect(() => { + (async() => { + try { + const data = await API.getOneItem(id); + console.log(data); + setContent( + <> +

{data.name}

+

{data.description}

+ + ) + } catch (e) { + setContent( +
+

Access Forbidden

+

{e.message}

+
+ ) + } + })(); + }, []); + + return ( +
+
+ Home + Back +
+ { content } +
+ ) +} + +export default SingleItem \ No newline at end of file diff --git a/client/src/pages/protected/Welcome.jsx b/client/src/pages/protected/Welcome.jsx new file mode 100644 index 0000000..1ff9dc9 --- /dev/null +++ b/client/src/pages/protected/Welcome.jsx @@ -0,0 +1,20 @@ +import { useNavigate } from "react-router-dom" + +function Welcome({ user, handleLogout }) { + const navigate = useNavigate(); + + return ( +
+

Welcome, {user.username}!

+
+ +

Check out some cool protected actions:

+
+ + +
+
+ ) +} + +export default Welcome \ No newline at end of file diff --git a/client/src/util/API.js b/client/src/util/API.js index 30711c9..3fe237a 100644 --- a/client/src/util/API.js +++ b/client/src/util/API.js @@ -51,4 +51,16 @@ export default class API { }); return Promise.resolve(response.data); } + + static async getOneItem(id) { + const token = localStorage.getItem("token"); + + const response = await _api.get(`/app/item/${id}`, { + headers: { + "Content-Type": "application/json", + "Authorization": ("Bearer " + token) + } + }); + return Promise.resolve(response.data); + } } \ No newline at end of file diff --git a/server/models/Item.js b/server/models/Item.js index 608e10b..c16a0c1 100644 --- a/server/models/Item.js +++ b/server/models/Item.js @@ -26,7 +26,7 @@ module.exports = class Item { static async getOne(id) { const query = `SELECT * FROM item WHERE id = $1`; - const result = await pool.query(query, [id]); + const result = await pool.query(query, [parseInt(id)]); if (result.rows.length) { return result.rows[0]; } diff --git a/server/routes/auth.js b/server/routes/auth.js index a3285f4..aaadb02 100644 --- a/server/routes/auth.js +++ b/server/routes/auth.js @@ -17,7 +17,7 @@ async function authRoute(app, passport) { } }) - router.post('/login', passport.authenticate('local'), async (req, res, next) => { + router.post('/login', async (req, res, next) => { try { const data = req.body; let response = await AuthController.login(data);