From b83ab834ef4cc595069a5d2d1781d2cfba95275f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E6=9E=B3?= <7854742+wang_rumeng@user.noreply.gitee.com> Date: Sat, 5 Jul 2025 21:39:13 +0800 Subject: [PATCH] =?UTF-8?q?=E7=99=BB=E5=BD=95=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/layout.tsx | 10 +- components/auth/auth-guard.tsx | 73 ++++++ components/pages/login.tsx | 25 ++- lib/api.ts | 123 ++++++++++ lib/auth.ts | 247 ++++++++++++++++----- package-lock.json | 395 ++++++++++++++++++++++++++++++++- package.json | 4 +- 7 files changed, 808 insertions(+), 69 deletions(-) create mode 100644 components/auth/auth-guard.tsx create mode 100644 lib/api.ts diff --git a/app/layout.tsx b/app/layout.tsx index bce797f..ac0f765 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -10,6 +10,12 @@ const OAuthCallbackHandler = dynamic( { ssr: false } ); +// Import AuthGuard dynamically for client-side only +const AuthGuard = dynamic( + () => import('@/components/auth/auth-guard'), + { ssr: false } +); + // Import dev helper in development environment only const DevHelper = dynamic( () => import('@/utils/dev-helper').then(() => ({ default: () => null })), @@ -35,7 +41,9 @@ export default function RootLayout({ enableSystem disableTransitionOnChange > - {children} + + {children} + {process.env.NODE_ENV === 'development' && } diff --git a/components/auth/auth-guard.tsx b/components/auth/auth-guard.tsx new file mode 100644 index 0000000..5fd38c5 --- /dev/null +++ b/components/auth/auth-guard.tsx @@ -0,0 +1,73 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useRouter, usePathname } from 'next/navigation'; +import { checkAuth, isAuthenticated } from '@/lib/auth'; + +interface AuthGuardProps { + children: React.ReactNode; +} + +export default function AuthGuard({ children }: AuthGuardProps) { + const [isLoading, setIsLoading] = useState(true); + const [isAuthorized, setIsAuthorized] = useState(false); + const router = useRouter(); + const pathname = usePathname(); + + // 不需要鉴权的页面 + const publicPaths = ['/login', '/signup', '/forgot-password']; + const isPublicPath = publicPaths.includes(pathname); + + useEffect(() => { + const verifyAuth = async () => { + // 如果是公共页面,不需要鉴权 + if (isPublicPath) { + setIsAuthorized(true); + setIsLoading(false); + return; + } + + // 检查是否有token + if (!isAuthenticated()) { + router.push('/login'); + return; + } + + try { + // 调用鉴权接口验证token + const isValid = await checkAuth(); + + if (isValid) { + setIsAuthorized(true); + } else { + // Token无效,重定向到登录页 + router.push('/login'); + } + } catch (error) { + console.error('Auth verification failed:', error); + router.push('/login'); + } finally { + setIsLoading(false); + } + }; + + verifyAuth(); + }, [pathname, router, isPublicPath]); + + // 显示加载状态 + if (isLoading) { + return ( +
+
+
+ ); + } + + // 如果未授权,返回null(会由router处理跳转) + if (!isAuthorized) { + return null; + } + + // 授权通过,渲染子组件 + return <>{children}; +} \ No newline at end of file diff --git a/components/pages/login.tsx b/components/pages/login.tsx index 92fed4a..ff4bf2a 100644 --- a/components/pages/login.tsx +++ b/components/pages/login.tsx @@ -51,10 +51,19 @@ export default function Login() { try { await loginUser(email, password); + // 登录成功后跳转到首页 router.push('/'); - } catch (error) { + } catch (error: any) { console.error('Login failed:', error); - setFormError('登录失败,请检查您的凭据后重试。'); + + // 根据错误类型显示不同的错误消息 + if (error.message) { + setFormError(error.message); + } else if (error.msg) { + setFormError(error.msg); + } else { + setFormError('登录失败,请检查您的凭据后重试。'); + } } finally { setIsSubmitting(false); } @@ -85,12 +94,12 @@ export default function Login() { )}
- + setEmail(e.target.value)} /> @@ -98,7 +107,7 @@ export default function Login() {
{formError && ( -
{formError}
+
+ {formError} +
)}