From 2b056a0d824dfa0add658158435ccaa2d3e33e2a Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 7 Oct 2025 10:30:55 +0800 Subject: [PATCH] feat: forbidden page --- app/forbidden.tsx | 10 ++++ app/unauthorized.tsx | 5 ++ components/forbidden-layout/page.tsx | 58 +++++++++++++++++++++++ components/forbidden-layout/user-info.tsx | 16 +++++++ lib/auth.ts | 7 --- next.config.ts | 1 + providers/use-protected-route.tsx | 13 +++-- 7 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 app/forbidden.tsx create mode 100644 app/unauthorized.tsx create mode 100644 components/forbidden-layout/page.tsx create mode 100644 components/forbidden-layout/user-info.tsx diff --git a/app/forbidden.tsx b/app/forbidden.tsx new file mode 100644 index 0000000..b6e9514 --- /dev/null +++ b/app/forbidden.tsx @@ -0,0 +1,10 @@ +import ForbiddenLayout from "@/components/forbidden-layout/page"; +import type { Metadata } from "next"; + +export const metadata: Metadata = { + title: "帳號尚未開通", +}; + +export default function ForbiddenPage() { + return ; +} diff --git a/app/unauthorized.tsx b/app/unauthorized.tsx new file mode 100644 index 0000000..b3f0f28 --- /dev/null +++ b/app/unauthorized.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default async function UnauthorizedPage() { + redirect("/login"); +} diff --git a/components/forbidden-layout/page.tsx b/components/forbidden-layout/page.tsx new file mode 100644 index 0000000..a3358ba --- /dev/null +++ b/components/forbidden-layout/page.tsx @@ -0,0 +1,58 @@ +import { Logo } from "@/components/logo"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import AuthorizedApolloWrapper from "@/providers/use-apollo.rsc"; +import { AlertTriangle } from "lucide-react"; +import Link from "next/link"; +import { Suspense } from "react"; +import { UserInfo } from "./user-info"; + +export default async function ForbiddenLayout() { + return ( +
+ +
+ +
+ 資料庫練功坊 + + + + + 無權開啟此頁面 + + 您的帳號尚未開通,無法使用系統。請聯絡管理員開通帳號。 + + + + + + + + + + + + + +
+ ); +} diff --git a/components/forbidden-layout/user-info.tsx b/components/forbidden-layout/user-info.tsx new file mode 100644 index 0000000..82b5915 --- /dev/null +++ b/components/forbidden-layout/user-info.tsx @@ -0,0 +1,16 @@ +"use client"; + +import useUser from "@/hooks/use-user"; + +export function UserInfo() { + const { user } = useUser(); + + return ( +
+

+ 您目前登入的帳號是:{user?.name} ({user?.email}) +

+

如果這不是您想登入的帳號,請切換 Google 帳號後重新登入

+
+ ); +} diff --git a/lib/auth.ts b/lib/auth.ts index 4a02159..8836860 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -250,12 +250,5 @@ export async function getAuthStatus(token: string): Promise { }; } - if (parsedData.data.scope.includes("*")) { - return { - loggedIn: true, - introspectResult: parsedData.data, - }; - } - return { loggedIn: true, introspectResult: parsedData.data }; } diff --git a/next.config.ts b/next.config.ts index 4293b29..0b69c3e 100644 --- a/next.config.ts +++ b/next.config.ts @@ -13,6 +13,7 @@ const nextConfig: NextConfig = { ], ], ppr: "incremental", + authInterrupts: true, }, async rewrites() { return [ diff --git a/providers/use-protected-route.tsx b/providers/use-protected-route.tsx index 9b6489a..d1eef61 100644 --- a/providers/use-protected-route.tsx +++ b/providers/use-protected-route.tsx @@ -1,18 +1,23 @@ "use server"; import { getAuthStatus, getAuthToken } from "@/lib/auth"; -import { redirect, unauthorized } from "next/navigation"; +import { forbidden, unauthorized } from "next/navigation"; export default async function ProtectedRoute({ children }: { children: React.ReactNode }) { const token = await getAuthToken(); if (!token) { - redirect("/login"); + unauthorized(); } - const { loggedIn } = await getAuthStatus(token); - if (!loggedIn) { + const { loggedIn, introspectResult } = await getAuthStatus(token); + if (!loggedIn || !introspectResult?.active) { unauthorized(); } + // requires for verification + if (introspectResult.scope.includes("unverified")) { + forbidden(); + } + return children; }