diff --git a/.vscode/settings.json b/.vscode/settings.json index 7ee8470b..711bf800 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,8 @@ "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" + }, + "files.associations": { + "*.css": "tailwindcss" } } diff --git a/src/client/AuthContext.tsx b/src/client/AuthContext.tsx index a65f8671..5d991567 100644 --- a/src/client/AuthContext.tsx +++ b/src/client/AuthContext.tsx @@ -1,21 +1,23 @@ // AuthContext.js -import React, { createContext, useContext, useState } from "react"; +import React, { createContext, useContext, useEffect, useState } from "react"; import type { ReactNode } from "react"; export interface AuthContextType { isAuthenticated: boolean; login: () => void; logout: () => void; + isLoading: boolean; } // Create AuthContext as undefined values for now: // const AuthContext = createContext(undefined); // Create the AuthContext with default values -const AuthContext = createContext({ +const AuthContext = createContext({ isAuthenticated: false, login: () => {}, logout: () => {}, + isLoading: true, }); interface AuthProviderProps { @@ -24,12 +26,40 @@ interface AuthProviderProps { // AuthProvider component that wraps your app and provides auth state export const AuthProvider: React.FC = ({ children }) => { const [isAuthenticated, setIsAuthenticated] = useState(false); + const [isLoading, setIsLoading] = useState(true); - // TODO: Mock funcitons! - const login = () => setIsAuthenticated(true); - const logout = () => setIsAuthenticated(false); + const checkSession = async () => { + try { + const response = await fetch("/api/auth/session", { + credentials: "include", + }); - return {children}; + if (response.ok) { + setIsAuthenticated(true); + } else { + setIsAuthenticated(false); + } + } catch (error) { + console.error(error); + setIsAuthenticated(false); + } finally { + setIsLoading(false); // userbutton wont render unless loading is false + } + }; + + useEffect(() => { + checkSession(); // validate user session whenever authcontext renders + }, []); + + const login = () => { + setIsAuthenticated(true); + }; + + const logout = () => { + setIsAuthenticated(false); + }; + + return {children}; }; // Custom hook to use the AuthContext diff --git a/src/client/components/navigation/Header.tsx b/src/client/components/navigation/Header.tsx index 44a13c1a..b9ac3abd 100644 --- a/src/client/components/navigation/Header.tsx +++ b/src/client/components/navigation/Header.tsx @@ -49,7 +49,7 @@ const Header: React.FC = () => { const location = useLocation(); const isHomePage = location.pathname === "/"; const [menuOpen, setMenuOpen] = useState(false); - const { isAuthenticated, logout } = useAuth(); + const { isAuthenticated, isLoading, logout } = useAuth(); const menuRef = useRef(null); const hamburgerRef = useRef(null); @@ -98,7 +98,7 @@ const Header: React.FC = () => {
- + {!isLoading && }
@@ -106,6 +106,7 @@ const Header: React.FC = () => { {/* Mobile Nav */}
+ {!isLoading && } diff --git a/src/server/api/auth.ts b/src/server/api/auth.ts index 6f80ec96..659c242b 100644 --- a/src/server/api/auth.ts +++ b/src/server/api/auth.ts @@ -116,6 +116,43 @@ authRoutes.post("/auth/logout", async (c) => { } }); +// used for validating sessions +authRoutes.get("/auth/session", async (c) => { + const sessionId = c.req.header("Cookie")?.match(/sessionId=([^;]*)/)?.[1]; + + if (!sessionId) { + return new Response("No active session", { status: 401 }); + } + + try { + const session = await db.select().from(Schema.sessions).where(eq(Schema.sessions.id, sessionId)); + + if (session.length === 0) { + return new Response("Session not found", { status: 401 }); + } + + if (session[0].expires_at < Date.now()) { + await db.delete(Schema.sessions).where(eq(Schema.sessions.id, sessionId)); + // maybe renew session? + return new Response("Session expired", { status: 401 }); + } + + const user = await db.select({ username: Schema.users.username }).from(Schema.users).where(eq(Schema.users.id, session[0].user_id)); + + if (user.length === 0) { + return new Response("User not found", { status: 401 }); + } + + return new Response(JSON.stringify({ username: user[0].username }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } catch (error) { + console.log(error); + return new Response("Error checking session", { status: 500 }); + } +}); + async function createSession(sessionID: string, userID: string) { try { await db.insert(Schema.sessions).values({