diff --git a/backend/app/__init__.py b/backend/app/__init__.py
index 072d86e..4debff8 100644
--- a/backend/app/__init__.py
+++ b/backend/app/__init__.py
@@ -8,8 +8,6 @@ from config import Config
db = SQLAlchemy()
migrate = Migrate()
login = LoginManager()
-login.login_view = "login"
-login.login_message = "Please log in to access this page."
def create_app(config_class=Config):
diff --git a/backend/app/routes.py b/backend/app/routes.py
index e45b6ee..649411a 100644
--- a/backend/app/routes.py
+++ b/backend/app/routes.py
@@ -1,6 +1,6 @@
-from flask_login import login_user, logout_user
+from flask_login import login_required, login_user, logout_user
from app.bp import bp
-from flask import Response, jsonify, request
+from flask import jsonify, request
from app.errors import error_response
from flask_login import current_user
@@ -20,10 +20,10 @@ def login_route():
if current_user.is_authenticated:
return error_response(400, "A user is already logged in!")
- if not data.get("user_id") or not data.get("password"):
- return error_response(400, "Must supply user_id and password")
+ if not data.get("username") or not data.get("password"):
+ return error_response(400, "Must supply username and password")
- user = User.query.get(data.get("user_id"))
+ user = User.query.filter_by(username=data["username"]).first()
if not user:
return error_response(400, "User not found")
@@ -36,10 +36,8 @@ def login_route():
@bp.route("/logout", methods=["POST"])
+@login_required
def logout_route():
- if not current_user.is_authenticated:
- return error_response(400, "No users are logged in!")
-
resp = jsonify(current_user.to_dict())
logout_user()
return resp
diff --git a/backend/main.py b/backend/main.py
index 1cbaba5..be9b9c8 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -3,10 +3,12 @@ dotenv.load_dotenv()
from app import create_app, db
from app.models import User
+from flask_cors import CORS
app = create_app()
+CORS(app, supports_credentials=True, resources={r"/.*": {"origins": r".*localhost.*"}})
+
@app.shell_context_processor
def make_shell_context():
- return {'db': db, 'User': User}
-
+ return {"db": db, "User": User}
diff --git a/backend/requirements.txt b/backend/requirements.txt
index 06f76c6..6937fee 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -1,6 +1,7 @@
alembic==1.10.2
click==8.1.3
Flask==2.2.3
+Flask-Cors==3.0.10
Flask-HTTPAuth==4.7.0
Flask-Login==0.6.2
Flask-Migrate==4.0.4
@@ -12,6 +13,7 @@ Jinja2==3.1.2
Mako==1.2.4
MarkupSafe==2.1.2
python-dotenv==1.0.0
+six==1.16.0
SQLAlchemy==2.0.6
typing_extensions==4.5.0
Werkzeug==2.2.3
diff --git a/frontend/src/App.js b/frontend/src/App.js
index e77fd2e..7a5767a 100644
--- a/frontend/src/App.js
+++ b/frontend/src/App.js
@@ -2,12 +2,15 @@ import "./App.css";
import { Route } from "wouter";
import HomePage from "./pages/HomePage";
import LoginPage from "./pages/LoginPage";
+import { UserContextProvider } from "./contexts/UserContext";
function App() {
return (
-
-
+
+
+
+
);
}
diff --git a/frontend/src/components/MyNavbar.jsx b/frontend/src/components/MyNavbar.jsx
index 658395b..1cd46ff 100644
--- a/frontend/src/components/MyNavbar.jsx
+++ b/frontend/src/components/MyNavbar.jsx
@@ -1,6 +1,9 @@
-import { Nav, Container, Navbar, NavDropdown } from "react-bootstrap";
+import { useContext } from "react";
+import { Nav, Container, Navbar } from "react-bootstrap";
+import UserContext from "../contexts/UserContext";
const MyNavbar = () => {
+ const { currentUser } = useContext(UserContext);
return (
@@ -9,7 +12,9 @@ const MyNavbar = () => {
diff --git a/frontend/src/contexts/UserContext.jsx b/frontend/src/contexts/UserContext.jsx
new file mode 100644
index 0000000..c0b3a97
--- /dev/null
+++ b/frontend/src/contexts/UserContext.jsx
@@ -0,0 +1,30 @@
+import { createContext, useMemo, useState } from "react";
+
+const UserContext = createContext({});
+
+const UserContextProvider = ({ children }) => {
+
+ const [currentUser, setCurrentUser] = useState(undefined);
+
+ // useMemo does not wait for after render, like useEffect.
+ useMemo(()=>{
+ setCurrentUser(JSON.parse(localStorage.getItem("currentUser")));
+ },[]);
+
+ const contextValues = {
+ currentUser,
+ setCurrentUser: (data) => {
+ localStorage.setItem("currentUser", JSON.stringify(data))
+ setCurrentUser(data);
+ },
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export default UserContext;
+export { UserContextProvider };
diff --git a/frontend/src/pages/HomePage.jsx b/frontend/src/pages/HomePage.jsx
index 527d63a..ebc5b5d 100644
--- a/frontend/src/pages/HomePage.jsx
+++ b/frontend/src/pages/HomePage.jsx
@@ -1,7 +1,17 @@
+import { useContext, useEffect } from "react";
import { Container } from "react-bootstrap";
import MyNavbar from "../components/MyNavbar";
+import UserContext from "../contexts/UserContext";
const HomePage = () => {
+ const { currentUser } = useContext(UserContext);
+
+ useEffect(() => {
+ if (!currentUser?.id) {
+ window.location.replace("/login");
+ }
+ }, [currentUser]);
+
return (
diff --git a/frontend/src/pages/LoginPage.jsx b/frontend/src/pages/LoginPage.jsx
index 2699150..21ef318 100644
--- a/frontend/src/pages/LoginPage.jsx
+++ b/frontend/src/pages/LoginPage.jsx
@@ -1,27 +1,88 @@
-import React from "react";
-import { Col, Container, Form, Row } from "react-bootstrap";
+import React, { useContext, useEffect, useState } from "react";
+import { Button, Col, Container, Form, Row, Alert } from "react-bootstrap";
import MyNavbar from "../components/MyNavbar";
+import UserContext from "../contexts/UserContext";
+import { makeRequest } from "../utils.ts";
const LoginPage = () => {
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+ const [error, setError] = useState(null);
+
+ const { currentUser, setCurrentUser } = useContext(UserContext);
+
+ useEffect(() => {
+ console.log(currentUser);
+ if (currentUser?.id) {
+ gotoHome();
+ }
+ }, [currentUser]);
+
+ const gotoHome = () => {
+ window.location.href = "/";
+ };
+
+ const sendLoginRequest = async (e) => {
+ e?.preventDefault();
+ await makeRequest({
+ url: "http://localhost:5000/login",
+ method: "POST",
+ body: { username, password },
+ })
+ .then((resp) => resp.json())
+ .then((data) => {
+ if (data.error) {
+ setError(data);
+ return;
+ }
+ setCurrentUser(data);
+ });
+ };
+
return (
+ {error && (
+
+
+ {error.message}
+
+
+ )}
-
-
+
Username
-
-
+
+ {
+ setUsername(e.target.value);
+ }}
+ />
-
+
Password
-
-
+
+ {
+ setPassword(e.target.value);
+ }}
+ />
+
+
+
+
+
+
diff --git a/frontend/src/utils.ts b/frontend/src/utils.ts
new file mode 100644
index 0000000..c7b116f
--- /dev/null
+++ b/frontend/src/utils.ts
@@ -0,0 +1,11 @@
+const makeRequest = ({ url, method, body }): Promise => {
+ return fetch(url, {
+ method: method,
+ credentials: "include",
+ body: JSON.stringify(body),
+ headers: { "content-type": "application/json" },
+ mode: "cors",
+ });
+};
+
+export { makeRequest };