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 1/2] =?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}
+
)}