mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-15 17:21:35 +10:00
refactor(v4.0.0-alpha): beginning of a new era
This commit is contained in:
18
apps/client/src/router/guards/auth.tsx
Normal file
18
apps/client/src/router/guards/auth.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { Navigate, Outlet, useLocation } from "react-router-dom";
|
||||
|
||||
import { useUser } from "@/client/services/user";
|
||||
|
||||
export const AuthGuard = () => {
|
||||
const location = useLocation();
|
||||
const redirectTo = location.pathname + location.search;
|
||||
|
||||
const { user, loading } = useUser();
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
if (user) {
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
return <Navigate to={`/auth/login?redirect=${redirectTo}`} replace />;
|
||||
};
|
||||
16
apps/client/src/router/guards/guest.tsx
Normal file
16
apps/client/src/router/guards/guest.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { Navigate, Outlet, useSearchParams } from "react-router-dom";
|
||||
|
||||
import { useAuthStore } from "@/client/stores/auth";
|
||||
|
||||
export const GuestGuard = () => {
|
||||
const isLoggedIn = useAuthStore((state) => !!state.user);
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
const redirect = searchParams.get("redirect") || "/dashboard";
|
||||
|
||||
if (isLoggedIn) {
|
||||
return <Navigate to={redirect} />;
|
||||
}
|
||||
|
||||
return <Outlet />;
|
||||
};
|
||||
92
apps/client/src/router/index.tsx
Normal file
92
apps/client/src/router/index.tsx
Normal file
@ -0,0 +1,92 @@
|
||||
import { createBrowserRouter, createRoutesFromElements, Navigate, Route } from "react-router-dom";
|
||||
|
||||
import { BackupOtpPage } from "../pages/auth/backup-otp/page";
|
||||
import { ForgotPasswordPage } from "../pages/auth/forgot-password/page";
|
||||
import { AuthLayout } from "../pages/auth/layout";
|
||||
import { LoginPage } from "../pages/auth/login/page";
|
||||
import { RegisterPage } from "../pages/auth/register/page";
|
||||
import { ResetPasswordPage } from "../pages/auth/reset-password/page";
|
||||
import { VerifyEmailPage } from "../pages/auth/verify-email/page";
|
||||
import { VerifyOtpPage } from "../pages/auth/verify-otp/page";
|
||||
import { BuilderLayout } from "../pages/builder/layout";
|
||||
import { builderLoader, BuilderPage } from "../pages/builder/page";
|
||||
import { DashboardLayout } from "../pages/dashboard/layout";
|
||||
import { ResumesPage } from "../pages/dashboard/resumes/page";
|
||||
import { SettingsPage } from "../pages/dashboard/settings/page";
|
||||
import { HomeLayout } from "../pages/home/layout";
|
||||
import { HomePage } from "../pages/home/page";
|
||||
import { PrinterPage } from "../pages/printer/page";
|
||||
import { publicLoader, PublicResumePage } from "../pages/public/page";
|
||||
import { Providers } from "../providers";
|
||||
import { AuthGuard } from "./guards/auth";
|
||||
import { GuestGuard } from "./guards/guest";
|
||||
import { authLoader } from "./loaders/auth";
|
||||
|
||||
export const routes = createRoutesFromElements(
|
||||
<Route element={<Providers />}>
|
||||
<Route element={<HomeLayout />}>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
</Route>
|
||||
|
||||
<Route path="auth">
|
||||
<Route element={<AuthLayout />}>
|
||||
<Route element={<GuestGuard />}>
|
||||
<Route path="login" element={<LoginPage />} />
|
||||
<Route path="register" element={<RegisterPage />} />
|
||||
</Route>
|
||||
|
||||
{/* Password Recovery */}
|
||||
<Route element={<GuestGuard />}>
|
||||
<Route path="forgot-password" element={<ForgotPasswordPage />} />
|
||||
<Route path="reset-password" element={<ResetPasswordPage />} />
|
||||
</Route>
|
||||
|
||||
{/* Two-Factor Authentication */}
|
||||
<Route element={<GuestGuard />}>
|
||||
<Route path="verify-otp" element={<VerifyOtpPage />} />
|
||||
<Route path="backup-otp" element={<BackupOtpPage />} />
|
||||
</Route>
|
||||
|
||||
{/* Email Verification */}
|
||||
<Route element={<AuthGuard />}>
|
||||
<Route path="verify-email" element={<VerifyEmailPage />} />
|
||||
</Route>
|
||||
|
||||
{/* OAuth Callback */}
|
||||
<Route path="callback" loader={authLoader} />
|
||||
</Route>
|
||||
|
||||
<Route index element={<Navigate to="/auth/login" replace />} />
|
||||
</Route>
|
||||
|
||||
<Route path="dashboard">
|
||||
<Route element={<AuthGuard />}>
|
||||
<Route element={<DashboardLayout />}>
|
||||
<Route path="resumes" element={<ResumesPage />} />
|
||||
<Route path="settings" element={<SettingsPage />} />
|
||||
|
||||
<Route index element={<Navigate to="/dashboard/resumes" replace />} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
<Route path="builder">
|
||||
<Route element={<AuthGuard />}>
|
||||
<Route element={<BuilderLayout />}>
|
||||
<Route path=":id" loader={builderLoader} element={<BuilderPage />} />
|
||||
|
||||
<Route index element={<Navigate to="/dashboard/resumes" replace />} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
<Route path="printer" element={<PrinterPage />} />
|
||||
|
||||
{/* Public Routes */}
|
||||
<Route path=":username">
|
||||
<Route path=":slug" loader={publicLoader} element={<PublicResumePage />} />
|
||||
</Route>
|
||||
</Route>,
|
||||
);
|
||||
|
||||
export const router = createBrowserRouter(routes);
|
||||
36
apps/client/src/router/loaders/auth.ts
Normal file
36
apps/client/src/router/loaders/auth.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { authResponseSchema, UserDto } from "@reactive-resume/dto";
|
||||
import { LoaderFunction, redirect } from "react-router-dom";
|
||||
|
||||
import { USER_KEY } from "@/client/constants/query-keys";
|
||||
import { queryClient } from "@/client/libs/query-client";
|
||||
import { fetchUser } from "@/client/services/user";
|
||||
import { useAuthStore } from "@/client/stores/auth";
|
||||
|
||||
export const authLoader: LoaderFunction<UserDto> = async ({ request }) => {
|
||||
const status = new URL(request.url).searchParams.get("status");
|
||||
|
||||
const { success } = authResponseSchema
|
||||
.pick({ status: true })
|
||||
.safeParse({ status: new URL(request.url).searchParams.get("status") });
|
||||
|
||||
if (!success) return redirect("/auth/login");
|
||||
|
||||
const user = await queryClient.fetchQuery({
|
||||
queryKey: [USER_KEY],
|
||||
queryFn: fetchUser,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
redirect("/auth/login");
|
||||
}
|
||||
|
||||
if (status === "2fa_required") {
|
||||
return redirect("/auth/verify-otp");
|
||||
}
|
||||
|
||||
if (status === "authenticated") {
|
||||
useAuthStore.setState({ user });
|
||||
|
||||
return redirect("/dashboard");
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user