From 41e531644356b2c08169bfaf8450d4c7843a01e7 Mon Sep 17 00:00:00 2001 From: Alex Frantz Date: Sun, 6 Sep 2026 21:50:26 -0400 Subject: [PATCH] hook up auth --- apps/backend/src/controllers/users.ts | 2 +- apps/frontend/src/components/header.tsx | 11 ++++ apps/frontend/src/main.tsx | 10 +++- apps/frontend/src/pages/login.tsx | 54 ++++++++++++++++++ apps/frontend/src/providers/AuthProvider.tsx | 59 ++++++++++++++++++++ apps/frontend/vite.config.ts | 9 +++ 6 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 apps/frontend/src/pages/login.tsx create mode 100644 apps/frontend/src/providers/AuthProvider.tsx diff --git a/apps/backend/src/controllers/users.ts b/apps/backend/src/controllers/users.ts index e47e13b..bbe5ed4 100644 --- a/apps/backend/src/controllers/users.ts +++ b/apps/backend/src/controllers/users.ts @@ -55,7 +55,7 @@ export async function login(req: Request, res: Response, next: NextFunction) { req.login(user, (loginErr) => { if (loginErr) return next(loginErr); - res.json({ id: user.id, email: user.email, name: user.name }); + res.redirect("/"); }); }, )(req, res, next); diff --git a/apps/frontend/src/components/header.tsx b/apps/frontend/src/components/header.tsx index a071801..459acd1 100644 --- a/apps/frontend/src/components/header.tsx +++ b/apps/frontend/src/components/header.tsx @@ -1,10 +1,21 @@ +import { useAuth } from "../providers/AuthProvider"; + export default function Header() { + const auth = useAuth(); + return (

Alex / dev + QA

+ {auth.user !== null ? ( + auth.logout()}> + Log Out + + ) : ( + Log In + )} , }, + { + path: "/login", + element: , + }, ]); createRoot(document.getElementById("root")!).render( - + + + , ); diff --git a/apps/frontend/src/pages/login.tsx b/apps/frontend/src/pages/login.tsx new file mode 100644 index 0000000..70c7d6c --- /dev/null +++ b/apps/frontend/src/pages/login.tsx @@ -0,0 +1,54 @@ +import { useAuth } from "../providers/AuthProvider"; +import { useEffect } from "react"; +import { useNavigate } from "react-router-dom"; + +type InputType = { + label: string; + forLabel: string; + type: "password" | "email"; +}; +const Input = ({ label, forLabel, type }: InputType) => { + return ( +
+ + +
+ ); +}; + +export default function LogIn() { + const auth = useAuth(); + const navigate = useNavigate(); + + useEffect(() => { + if (auth.user) navigate("/"); + }, [auth.user]); + + return ( +
+
+

Login

+
+
+
+ + +
+
+ +
+
+
+
+ ); +} diff --git a/apps/frontend/src/providers/AuthProvider.tsx b/apps/frontend/src/providers/AuthProvider.tsx new file mode 100644 index 0000000..c3a8552 --- /dev/null +++ b/apps/frontend/src/providers/AuthProvider.tsx @@ -0,0 +1,59 @@ +import { + createContext, + useContext, + useState, + useMemo, + type ReactNode, + useEffect, +} from "react"; + +interface User { + id: number; + email: string; + name: string; +} +interface AuthContextType { + user: User | null; + refetch: () => void; + logout: () => void; +} +const AuthContext = createContext(null); + +export function AuthProvider({ children }: { children: ReactNode }) { + const [user, setUser] = useState(null); + + const refetch = async () => { + const data = await fetch("/api/users/@me", { + credentials: "include", + }); + + if (data.ok) { + const refetchedUser = await data.json(); + setUser(refetchedUser); + } + }; + + const logout = async () => { + const res = await fetch("/api/users/logout", { + credentials: "include", + method: "POST", + }); + + if (res.ok) { + setUser(null); + } + }; + + useEffect(() => { + refetch(); + }, []); + + const value = useMemo(() => ({ user, refetch, logout }), [user]); + return {children}; +} + +export function useAuth() { + const ctx = useContext(AuthContext); + if (!ctx) throw new Error("useAuth must be used inside of AuthProvider"); + return ctx; +} diff --git a/apps/frontend/vite.config.ts b/apps/frontend/vite.config.ts index 521def0..9b97673 100644 --- a/apps/frontend/vite.config.ts +++ b/apps/frontend/vite.config.ts @@ -5,4 +5,13 @@ import tailwindcss from "@tailwindcss/vite"; // https://vite.dev/config/ export default defineConfig({ plugins: [react(), tailwindcss()], + server: { + proxy: { + "/api": { + target: "http://localhost:3000", + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ""), + }, + }, + }, });