Protect routes that need authentication

This commit is contained in:
Pijus Kamandulis 2024-10-09 21:24:51 +03:00
parent 253d13abd4
commit c05febd1f5
4 changed files with 67 additions and 14 deletions

View File

@ -8,8 +8,9 @@ import {
} from 'react'; } from 'react';
import { account } from '../appwrite'; import { account } from '../appwrite';
interface UserContextType { export interface UserContextType {
current: Models.Session | Models.User<Models.Preferences> | null; current: Models.Session | Models.User<Models.Preferences> | null;
isLoading: boolean;
login: (email: string, password: string) => Promise<void>; login: (email: string, password: string) => Promise<void>;
logout: () => Promise<void>; logout: () => Promise<void>;
register: (email: string, password: string) => Promise<void>; register: (email: string, password: string) => Promise<void>;
@ -30,11 +31,15 @@ export const UserProvider = (props: PropsWithChildren) => {
const [user, setUser] = useState< const [user, setUser] = useState<
Models.Session | Models.User<Models.Preferences> | null Models.Session | Models.User<Models.Preferences> | null
>(null); >(null);
const [isLoading, setIsLoading] = useState(true);
const login = async (email: string, password: string) => { const login = async (email: string, password: string) => {
const loggedIn = await account.createEmailPasswordSession(email, password); const loggedIn = await account.createEmailPasswordSession(email, password);
setUser(loggedIn); 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 () => { const logout = async () => {
@ -50,7 +55,10 @@ export const UserProvider = (props: PropsWithChildren) => {
const loginAsGuest = async () => { const loginAsGuest = async () => {
const session = await account.createAnonymousSession(); const session = await account.createAnonymousSession();
setUser(session); 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 () => { const init = async () => {
@ -59,6 +67,8 @@ export const UserProvider = (props: PropsWithChildren) => {
setUser(loggedIn); setUser(loggedIn);
} catch (err) { } catch (err) {
setUser(null); setUser(null);
} finally {
setIsLoading(false);
} }
}; };
@ -68,7 +78,14 @@ export const UserProvider = (props: PropsWithChildren) => {
return ( return (
<UserContext.Provider <UserContext.Provider
value={{ current: user, login, logout, register, loginAsGuest }} value={{
current: user,
isLoading,
login,
logout,
register,
loginAsGuest,
}}
> >
{props.children} {props.children}
</UserContext.Provider> </UserContext.Provider>

View File

@ -2,24 +2,48 @@ import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import './index.css'; import './index.css';
import { import {
createRootRoute, createRootRouteWithContext,
createRoute, createRoute,
createRouter, createRouter,
Outlet,
redirect,
RouterProvider, RouterProvider,
} from '@tanstack/react-router'; } from '@tanstack/react-router';
import Home from './pages/Home'; 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 Login from './pages/Login';
import { EstimationSessionProvider } from './lib/context/estimationSession'; import { EstimationSessionProvider } from './lib/context/estimationSession';
import { EstimationContextProvider } from './lib/context/estimation'; import { EstimationContextProvider } from './lib/context/estimation';
import Estimation from './pages/Estimation/Estimation'; import Estimation from './pages/Estimation/Estimation';
const rootRoute = createRootRoute(); interface RouterContext {
userContext: UserContextType;
}
const rootRoute = createRootRouteWithContext<RouterContext>()({
component: () => <Outlet />,
});
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({ const indexRoute = createRoute({
path: '/', path: '/',
component: Home, component: Home,
getParentRoute: () => rootRoute, getParentRoute: () => authenticatedRoute,
}); });
const loginRoute = createRoute({ const loginRoute = createRoute({
@ -31,15 +55,17 @@ const loginRoute = createRoute({
const estimationSessionRoute = createRoute({ const estimationSessionRoute = createRoute({
path: 'estimate/session/$sessionId', path: 'estimate/session/$sessionId',
component: Estimation, component: Estimation,
getParentRoute: () => rootRoute, getParentRoute: () => authenticatedRoute,
}); });
const router = createRouter({ const router = createRouter({
routeTree: rootRoute.addChildren([ routeTree: rootRoute.addChildren([
indexRoute, authenticatedRoute.addChildren([indexRoute, estimationSessionRoute]),
loginRoute, loginRoute,
estimationSessionRoute,
]), ]),
context: {
userContext: undefined!,
},
}); });
declare module '@tanstack/react-router' { declare module '@tanstack/react-router' {
@ -48,13 +74,23 @@ declare module '@tanstack/react-router' {
} }
} }
const InnerApp = () => {
const userContext = useUser();
return userContext.isLoading ? (
<p>Loading...</p>
) : (
<RouterProvider router={router} context={{ userContext }} />
);
};
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById('root')!).render(
<StrictMode> <StrictMode>
<UserProvider> <UserProvider>
<EstimationSessionProvider> <EstimationSessionProvider>
<EstimationContextProvider> <EstimationContextProvider>
{/* TODO: Move ctx providers to layout */} {/* TODO: Move ctx providers to layout */}
<RouterProvider router={router} /> <InnerApp />
</EstimationContextProvider> </EstimationContextProvider>
</EstimationSessionProvider> </EstimationSessionProvider>
</UserProvider> </UserProvider>

View File

@ -9,7 +9,7 @@ import CreateTicketForm from './components/CreateTicketForm';
const fibonacciSequence = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 100]; 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 Estimation: React.FC = () => {
const { sessionId } = route.useParams(); const { sessionId } = route.useParams();

View File

@ -12,7 +12,7 @@ import {
} from '../components'; } from '../components';
import { useState } from 'react'; import { useState } from 'react';
const route = getRouteApi('/'); const route = getRouteApi('/_authenticated/');
function Home() { function Home() {
const user = useUser(); const user = useUser();