| @@ -3,6 +3,7 @@ import type { Metadata, Viewport } from "next"; | |||||
| // import ThemeRegistry from "@/theme/ThemeRegistry"; | // import ThemeRegistry from "@/theme/ThemeRegistry"; | ||||
| import { detectLanguage } from "../i18n"; | import { detectLanguage } from "../i18n"; | ||||
| import ThemeRegistry from "../theme/ThemeRegistry"; | import ThemeRegistry from "../theme/ThemeRegistry"; | ||||
| import SessionProviderWrapper from "@/components/SessionProviderWrapper/SessionProviderWrapper"; | |||||
| export const metadata: Metadata = { | export const metadata: Metadata = { | ||||
| title: "FPSMS", | title: "FPSMS", | ||||
| @@ -27,7 +28,9 @@ export default async function RootLayout({ | |||||
| return ( | return ( | ||||
| <html lang={lang}> | <html lang={lang}> | ||||
| <body> | <body> | ||||
| <ThemeRegistry lang={lang}>{children}</ThemeRegistry> | |||||
| <SessionProviderWrapper> | |||||
| <ThemeRegistry lang={lang}>{children}</ThemeRegistry> | |||||
| </SessionProviderWrapper> | |||||
| </body> | </body> | ||||
| </html> | </html> | ||||
| ); | ); | ||||
| @@ -1,26 +1,19 @@ | |||||
| import { getServerSession } from "next-auth"; | |||||
| import { redirect } from "next/navigation"; | |||||
| import { authOptions } from "@/config/authConfig"; | |||||
| import { I18nProvider } from "@/i18n"; | import { I18nProvider } from "@/i18n"; | ||||
| import LoginPage from "@/components/LoginPage/LoginPage"; | import LoginPage from "@/components/LoginPage/LoginPage"; | ||||
| import LoginRedirectIfAuthenticated from "@/components/LoginPage/LoginRedirectIfAuthenticated"; | |||||
| type Props = { searchParams?: Promise<{ [key: string]: string | string[] | undefined }> }; | |||||
| const Login: React.FC<Props> = async ({ searchParams }) => { | |||||
| const session = await getServerSession(authOptions); | |||||
| const params = await searchParams; | |||||
| const sessionParam = params?.session; | |||||
| const forceLogin = sessionParam === "expired" || (Array.isArray(sessionParam) && sessionParam.includes("expired")); | |||||
| const now = Math.floor(Date.now() / 1000); | |||||
| const notExpired = !session?.exp || session.exp > now; | |||||
| if (session?.user && notExpired && !forceLogin) { | |||||
| redirect("/"); | |||||
| } | |||||
| /** | |||||
| * Redirect when already authenticated is done in LoginRedirectIfAuthenticated | |||||
| * (client-side with useSearchParams) so it works in production where server | |||||
| * searchParams can be undefined after build. | |||||
| */ | |||||
| const Login: React.FC = () => { | |||||
| return ( | return ( | ||||
| <I18nProvider namespaces={["login"]}> | |||||
| <LoginPage /> | |||||
| </I18nProvider> | |||||
| <LoginRedirectIfAuthenticated> | |||||
| <I18nProvider namespaces={["login"]}> | |||||
| <LoginPage /> | |||||
| </I18nProvider> | |||||
| </LoginRedirectIfAuthenticated> | |||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -0,0 +1,31 @@ | |||||
| "use client"; | |||||
| import { useSession } from "next-auth/react"; | |||||
| import { useSearchParams } from "next/navigation"; | |||||
| import { useRouter } from "next/navigation"; | |||||
| import { useEffect, type ReactNode } from "react"; | |||||
| /** | |||||
| * Client-side only: redirect to "/" when user is authenticated and this page | |||||
| * was not opened after a 401 (session=expired). Relies on useSearchParams() so | |||||
| * it works in production where server searchParams can be undefined. | |||||
| */ | |||||
| export default function LoginRedirectIfAuthenticated({ | |||||
| children, | |||||
| }: { | |||||
| children: ReactNode; | |||||
| }) { | |||||
| const { status } = useSession(); | |||||
| const searchParams = useSearchParams(); | |||||
| const router = useRouter(); | |||||
| useEffect(() => { | |||||
| if (status !== "authenticated") return; | |||||
| const sessionExpired = searchParams.get("session") === "expired"; | |||||
| if (!sessionExpired) { | |||||
| router.replace("/"); | |||||
| } | |||||
| }, [status, searchParams, router]); | |||||
| return <>{children}</>; | |||||
| } | |||||