Skip to content
Merged
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
8 changes: 4 additions & 4 deletions src/lib/api/user-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const loginUser = async (
var userSession: UserSession = defaultUserSession();
var err: string = "";

const data = await axios
await axios
.post(
`${siteConfig.env.backendServiceURL}/login`,
{
Expand All @@ -45,8 +45,8 @@ export const loginUser = async (
// deserialize successfully, then downstream operations will see the default
// userSessionData in state and we will experience subtle Bugs. We should consider
// how best we want to handle this. Ex. clear auth cookie?
console.debug("userSessionData: ", userSessionData)
throw { message: 'Login Failed to produce valid User Session data' }
console.debug("userSessionData: ", userSessionData);
throw { message: "Login Failed to produce valid User Session data" };
}
})
.catch(function (error: AxiosError) {
Expand All @@ -73,7 +73,7 @@ export const logoutUser = async (): Promise<string> => {
const data = await axios
.get(`${siteConfig.env.backendServiceURL}/logout`, {
withCredentials: true,
setTimeout: 5000, // 5 seconds before timing out trying to log in with the backend
setTimeout: 5000, // 5 seconds before timing out trying to log out with the backend
})
.then(function (response: AxiosResponse) {
// handle success
Expand Down
31 changes: 26 additions & 5 deletions src/lib/providers/auth-store-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
"use client";
// The purpose of this provider is to provide compatibility with
// Next.js re-rendering and component caching
"use client";

import { type ReactNode, createContext, useRef, useContext } from "react";
import {
type ReactNode,
createContext,
useRef,
useContext,
useMemo,
useEffect,
useState,
} from "react";
import { type StoreApi, useStore } from "zustand";

import { type AuthStore, createAuthStore } from "@/lib/stores/auth-store";
Expand All @@ -15,9 +23,22 @@ export interface AuthStoreProviderProps {
}

export const AuthStoreProvider = ({ children }: AuthStoreProviderProps) => {
const storeRef = useRef<StoreApi<AuthStore>>(undefined);
if (!storeRef.current) {
storeRef.current = createAuthStore();
const storeRef = useRef<StoreApi<AuthStore> | null>(null);
const [isInitialized, setIsInitialized] = useState(false);

useEffect(() => {
if (typeof window !== "undefined") {
// Now safe to access localStorage
const storedValue = localStorage.getItem("auth-store");
const initialState = storedValue ? JSON.parse(storedValue).state : null;
storeRef.current = createAuthStore(initialState);
setIsInitialized(true);
}
}, []);

// Ensure store is initialized before rendering the provider
if (!isInitialized) {
return null; // or return a loading component
}

return (
Expand Down
2 changes: 1 addition & 1 deletion src/lib/stores/auth-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const createAuthStore = (initState: AuthState = defaultInitState) => {
}),
{
name: "auth-store",
storage: createJSONStorage(() => sessionStorage),
storage: createJSONStorage(() => localStorage),
}
)
)
Expand Down
45 changes: 45 additions & 0 deletions src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { NextRequest, NextResponse } from "next/server";

// 1. Specify protected and public routes
const protectedRoutes = [
"/dashboard",
"/coaching-sessions",
"/settings",
"/profile",
];
const publicRoutes = ["/"];

export default async function middleware(req: NextRequest) {
// 2. Check if the current route is protected or public
const path = req.nextUrl.pathname;
const isProtectedRoute = protectedRoutes.some((route) =>
path.startsWith(route)
);
const isPublicRoute = publicRoutes.some((route) => path === route);

// 3. Get the session from the cookie
const sessionCookie = req.cookies.get("id");
const session = sessionCookie?.value;

// 4. Redirect to / if the user is not authenticated
// 4b. TODO: Check session validity/expiration?
if (isProtectedRoute && !session) {
return NextResponse.redirect(new URL("/", req.nextUrl));
}

// 5. Redirect to /dashboard if the user is authenticated
if (
isPublicRoute &&
session &&
!req.nextUrl.pathname.startsWith("/dashboard")
) {
return NextResponse.redirect(new URL("/dashboard", req.nextUrl));
}

return NextResponse.next();
}

// Routes Middleware should not run on
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico|.*\\.png$).*)"],
};