From c05febd1f5860ab06ea7e8a5ed445d30fdc1de46 Mon Sep 17 00:00:00 2001 From: Pijus Kamandulis Date: Wed, 9 Oct 2024 21:24:51 +0300 Subject: [PATCH] Protect routes that need authentication --- src/lib/context/user.tsx | 25 +++++++++++--- src/main.tsx | 52 ++++++++++++++++++++++++----- src/pages/Estimation/Estimation.tsx | 2 +- src/pages/Home.tsx | 2 +- 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/lib/context/user.tsx b/src/lib/context/user.tsx index c30f958..f480fbf 100644 --- a/src/lib/context/user.tsx +++ b/src/lib/context/user.tsx @@ -8,8 +8,9 @@ import { } from 'react'; import { account } from '../appwrite'; -interface UserContextType { +export interface UserContextType { current: Models.Session | Models.User | null; + isLoading: boolean; login: (email: string, password: string) => Promise; logout: () => Promise; register: (email: string, password: string) => Promise; @@ -30,11 +31,15 @@ export const UserProvider = (props: PropsWithChildren) => { const [user, setUser] = useState< Models.Session | Models.User | null >(null); + const [isLoading, setIsLoading] = useState(true); const login = async (email: string, password: string) => { const loggedIn = await account.createEmailPasswordSession(email, password); setUser(loggedIn); - window.location.replace('/'); // you can use different redirect method for your application + + const params = new URLSearchParams(window.location.search); + const redirectPath = params.get('redirect'); + window.location.replace(redirectPath || '/'); }; const logout = async () => { @@ -50,7 +55,10 @@ export const UserProvider = (props: PropsWithChildren) => { const loginAsGuest = async () => { const session = await account.createAnonymousSession(); setUser(session); - window.location.replace('/'); // you can use different redirect method for your application + + const params = new URLSearchParams(window.location.search); + const redirectPath = params.get('redirect'); + window.location.replace(redirectPath || '/'); }; const init = async () => { @@ -59,6 +67,8 @@ export const UserProvider = (props: PropsWithChildren) => { setUser(loggedIn); } catch (err) { setUser(null); + } finally { + setIsLoading(false); } }; @@ -68,7 +78,14 @@ export const UserProvider = (props: PropsWithChildren) => { return ( {props.children} diff --git a/src/main.tsx b/src/main.tsx index 00dd74c..0b2e903 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,24 +2,48 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import './index.css'; import { - createRootRoute, + createRootRouteWithContext, createRoute, createRouter, + Outlet, + redirect, RouterProvider, } from '@tanstack/react-router'; import Home from './pages/Home'; -import { UserProvider } from './lib/context/user'; +import { UserContextType, UserProvider, useUser } from './lib/context/user'; import Login from './pages/Login'; import { EstimationSessionProvider } from './lib/context/estimationSession'; import { EstimationContextProvider } from './lib/context/estimation'; import Estimation from './pages/Estimation/Estimation'; -const rootRoute = createRootRoute(); +interface RouterContext { + userContext: UserContextType; +} + +const rootRoute = createRootRouteWithContext()({ + component: () => , +}); + +const authenticatedRoute = createRoute({ + id: '_authenticated', + getParentRoute: () => rootRoute, + beforeLoad: async ({ location, context }) => { + console.log(context); + if (!context.userContext.current) { + throw redirect({ + to: '/login', + search: { + redirect: location.href, + }, + }); + } + }, +}); const indexRoute = createRoute({ path: '/', component: Home, - getParentRoute: () => rootRoute, + getParentRoute: () => authenticatedRoute, }); const loginRoute = createRoute({ @@ -31,15 +55,17 @@ const loginRoute = createRoute({ const estimationSessionRoute = createRoute({ path: 'estimate/session/$sessionId', component: Estimation, - getParentRoute: () => rootRoute, + getParentRoute: () => authenticatedRoute, }); const router = createRouter({ routeTree: rootRoute.addChildren([ - indexRoute, + authenticatedRoute.addChildren([indexRoute, estimationSessionRoute]), loginRoute, - estimationSessionRoute, ]), + context: { + userContext: undefined!, + }, }); declare module '@tanstack/react-router' { @@ -48,13 +74,23 @@ declare module '@tanstack/react-router' { } } +const InnerApp = () => { + const userContext = useUser(); + + return userContext.isLoading ? ( +

Loading...

+ ) : ( + + ); +}; + createRoot(document.getElementById('root')!).render( {/* TODO: Move ctx providers to layout */} - + diff --git a/src/pages/Estimation/Estimation.tsx b/src/pages/Estimation/Estimation.tsx index 8d80fe8..737c3e9 100644 --- a/src/pages/Estimation/Estimation.tsx +++ b/src/pages/Estimation/Estimation.tsx @@ -9,7 +9,7 @@ import CreateTicketForm from './components/CreateTicketForm'; const fibonacciSequence = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 100]; -const route = getRouteApi('/estimate/session/$sessionId'); +const route = getRouteApi('/_authenticated/estimate/session/$sessionId'); const Estimation: React.FC = () => { const { sessionId } = route.useParams(); diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 355f74f..5c1ff71 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -12,7 +12,7 @@ import { } from '../components'; import { useState } from 'react'; -const route = getRouteApi('/'); +const route = getRouteApi('/_authenticated/'); function Home() { const user = useUser();