(null);
- // 如果用户已登录,重定向到首页
+ // 获取重定向URL
+ useEffect(() => {
+ if (searchParams) {
+ const redirect = searchParams.get('redirect');
+ if (redirect) {
+ setRedirectUrl(decodeURIComponent(redirect));
+ }
+ }
+ }, [searchParams]);
+
+ // 如果用户已登录,重定向到原始页面或首页
useEffect(() => {
if (user) {
- router.push('/');
+ if (redirectUrl) {
+ router.push(redirectUrl);
+ } else {
+ router.push('/');
+ }
}
- }, [user, router]);
+ }, [user, router, redirectUrl]);
const handleEmailSignIn = async (e: React.FormEvent) => {
e.preventDefault();
@@ -53,7 +69,7 @@ export default function LoginPage() {
const { error } = await signIn(email, password);
if (error) {
- throw new Error(error.message);
+ throw new Error(error instanceof Error ? error.message : 'Unknown error');
}
// 登录成功后,会通过 useEffect 重定向
@@ -67,6 +83,28 @@ export default function LoginPage() {
}
};
+ const handleGoogleSignIn = async () => {
+ try {
+ setIsLoading(true);
+ setMessage({ type: '', content: '' });
+
+ const { error } = await signInWithGoogle();
+
+ if (error) {
+ throw new Error(error instanceof Error ? error.message : 'Unknown error');
+ }
+
+ // Google OAuth will redirect the user
+ } catch (error) {
+ console.error('Google login error:', error);
+ setMessage({
+ type: 'error',
+ content: error instanceof Error ? error.message : 'Failed to sign in with Google'
+ });
+ setIsLoading(false);
+ }
+ };
+
return (
{/* Wrap the component using useSearchParams in Suspense */}
@@ -149,6 +187,33 @@ export default function LoginPage() {
+
+
+
+
+ Or continue with
+
+
+
+
+
+
+
+
Don't have an account?{' '}
diff --git a/app/page.tsx b/app/page.tsx
index 87264c6..7b1fbde 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,5 +1,32 @@
-import { redirect } from 'next/navigation';
+'use client';
+
+import { useEffect } from 'react';
+import { useRouter } from 'next/navigation';
+import { useAuth } from '@/lib/auth';
export default function Home() {
- redirect('/analytics');
+ const router = useRouter();
+ const { user, isLoading } = useAuth();
+
+ useEffect(() => {
+ if (!isLoading) {
+ if (user) {
+ // 已登录用户重定向到分析页面
+ router.push('/analytics');
+ } else {
+ // 未登录用户重定向到登录页面
+ router.push('/login');
+ }
+ }
+ }, [user, isLoading, router]);
+
+ // 显示加载指示器
+ return (
+
+ );
}
\ No newline at end of file
diff --git a/lib/auth.tsx b/lib/auth.tsx
index 266b8d8..e7d2fb4 100644
--- a/lib/auth.tsx
+++ b/lib/auth.tsx
@@ -4,7 +4,6 @@ import React, { createContext, useContext, useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { Session, User } from '@supabase/supabase-js';
import supabase from './supabase';
-import { limqRequest } from './api';
// 定义用户类型
export type AuthUser = User | null;
@@ -44,6 +43,15 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
return;
}
+ // 打印会话信息,帮助调试
+ console.log('Supabase session loaded:', session ? 'Found' : 'Not found');
+ if (session) {
+ console.log('User authenticated:', session.user.email);
+ if (session.expires_at) {
+ console.log('Session expires at:', new Date(session.expires_at * 1000).toLocaleString());
+ }
+ }
+
setSession(session);
setUser(session?.user || null);
} catch (error) {
@@ -57,6 +65,8 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
// 监听验证状态变化
const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
+ console.log('Auth state changed, event:', _event);
+ console.log('New session:', session ? 'Valid' : 'None');
setSession(session);
setUser(session?.user || null);
});
@@ -106,6 +116,10 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
provider: 'google',
options: {
redirectTo: `${window.location.origin}/auth/callback`,
+ queryParams: {
+ access_type: 'offline',
+ prompt: 'consent',
+ }
},
});
diff --git a/middleware.ts b/middleware.ts
new file mode 100644
index 0000000..fc0dd3e
--- /dev/null
+++ b/middleware.ts
@@ -0,0 +1,69 @@
+import { NextResponse } from 'next/server';
+import type { NextRequest } from 'next/server';
+
+export function middleware(request: NextRequest) {
+ // 获取请求的路径
+ const path = request.nextUrl.pathname;
+ console.log(`[Middleware] 请求路径: ${path}`);
+
+ // 定义不需要验证的路径
+ const publicPaths = ['/login', '/register', '/auth/callback'];
+
+ // API 路由不需要验证
+ if (path.startsWith('/api/')) {
+ console.log('[Middleware] API路由,跳过验证');
+ return NextResponse.next();
+ }
+
+ // 静态资源不需要验证
+ if (path.includes('/_next/') || path.includes('/static/') || path.match(/\.(ico|png|jpg|jpeg|svg|css|js)$/)) {
+ console.log('[Middleware] 静态资源,跳过验证');
+ return NextResponse.next();
+ }
+
+ // 检查是否是公开路径
+ const isPublicPath = publicPaths.some(publicPath => path === publicPath || path.startsWith(publicPath));
+ console.log(`[Middleware] 是公开路径: ${isPublicPath}`);
+
+ // 获取所有 cookie
+ const allCookies = Object.fromEntries(request.cookies.getAll().map(c => [c.name, c.value]));
+ console.log('[Middleware] 所有Cookie:', JSON.stringify(allCookies));
+
+ // 检查用户是否登录
+ const supabaseCookie = request.cookies.get('sb-access-token') ||
+ request.cookies.get('sb-refresh-token') ||
+ request.cookies.get('sb-provider-token') ||
+ request.cookies.get('supabase-auth-token');
+ const isLoggedIn = !!supabaseCookie;
+ console.log(`[Middleware] 用户是否登录: ${isLoggedIn}`);
+
+ // 如果是公开路径但已登录,重定向到首页
+ if (isPublicPath && isLoggedIn) {
+ console.log('[Middleware] 已登录用户访问公开路径,重定向到首页');
+ return NextResponse.redirect(new URL('/', request.url));
+ }
+
+ // 如果不是公开路径且未登录,重定向到登录页
+ if (!isPublicPath && !isLoggedIn) {
+ console.log('[Middleware] 未登录用户访问私有路径,重定向到登录页');
+ const redirectUrl = new URL('/login', request.url);
+ redirectUrl.searchParams.set('redirect', encodeURIComponent(request.url));
+ return NextResponse.redirect(redirectUrl);
+ }
+
+ console.log('[Middleware] 通过验证,允许访问');
+ return NextResponse.next();
+}
+
+// 配置中间件匹配的路径
+export const config = {
+ matcher: [
+ // 匹配所有路径,但排除静态资源
+ '/((?!_next/static|_next/image|favicon.ico).*)',
+ // 明确包括重要的路由
+ '/',
+ '/analytics',
+ '/links',
+ '/create-shorturl',
+ ],
+};
\ No newline at end of file
diff --git a/next.config.ts b/next.config.ts
index dcc3f4a..77f2f86 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -6,8 +6,13 @@ const nextConfig: NextConfig = {
// 配置实验性选项
experimental: {
- // 禁用外部目录处理,避免monorepo问题
- // externalDir: true,
+ // 启用边缘函数中间件
+ instrumentationHook: true,
+ // 配置中间件匹配
+ middleware: {
+ // 确保匹配所有路径
+ matchAll: '/((?!_next|static|api|public).*)',
+ },
},
// 禁用严格模式,避免开发时重复渲染
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 42a9428..2d2ef16 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -237,67 +237,79 @@ packages:
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linux-arm@1.0.5':
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
cpu: [arm]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.0.4':
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linux-x64@1.0.4':
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@img/sharp-linux-arm64@0.33.5':
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@img/sharp-linux-arm@0.33.5':
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
+ libc: [glibc]
'@img/sharp-linux-s390x@0.33.5':
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@img/sharp-linux-x64@0.33.5':
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.33.5':
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@img/sharp-linuxmusl-x64@0.33.5':
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@img/sharp-wasm32@0.33.5':
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
@@ -366,24 +378,28 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@next/swc-linux-arm64-musl@15.2.3':
resolution: {integrity: sha512-2gAPA7P652D3HzR4cLyAuVYwYqjG0mt/3pHSWTCyKZq/N/dJcUAEoNQMyUmwTZWCJRKofB+JPuDVP2aD8w2J6Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@next/swc-linux-x64-gnu@15.2.3':
resolution: {integrity: sha512-ODSKvrdMgAJOVU4qElflYy1KSZRM3M45JVbeZu42TINCMG3anp7YCBn80RkISV6bhzKwcUqLBAmOiWkaGtBA9w==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@next/swc-linux-x64-musl@15.2.3':
resolution: {integrity: sha512-ZR9kLwCWrlYxwEoytqPi1jhPd1TlsSJWAc+H/CJHmHkf2nD92MQpSRIURR1iNgA/kuFSdxB8xIPt4p/T78kwsg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@next/swc-win32-arm64-msvc@15.2.3':
resolution: {integrity: sha512-+G2FrDcfm2YDbhDiObDU/qPriWeiz/9cRR0yMWJeTLGGX6/x8oryO3tt7HhodA1vZ8r2ddJPCjtLcpaVl7TE2Q==}
@@ -1164,24 +1180,28 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.0.15':
resolution: {integrity: sha512-342GVnhH/6PkVgKtEzvNVuQ4D+Q7B7qplvuH20Cfz9qEtydG6IQczTZ5IT4JPlh931MG1NUCVxg+CIorr1WJyw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.0.15':
resolution: {integrity: sha512-g76GxlKH124RuGqacCEFc2nbzRl7bBrlC8qDQMiUABkiifDRHOIUjgKbLNG4RuR9hQAD/MKsqZ7A8L08zsoBrw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.0.15':
resolution: {integrity: sha512-Gg/Y1XrKEvKpq6WeNt2h8rMIKOBj/W3mNa5NMvkQgMC7iO0+UNLrYmt6zgZufht66HozNpn+tJMbbkZ5a3LczA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@tailwindcss/oxide-win32-arm64-msvc@4.0.15':
resolution: {integrity: sha512-7QtSSJwYZ7ZK1phVgcNZpuf7c7gaCj8Wb0xjliligT5qCGCp79OV2n3SJummVZdw4fbTNKUOYMO7m1GinppZyA==}
@@ -1350,21 +1370,25 @@ packages:
resolution: {integrity: sha512-fp4Azi8kHz6TX8SFmKfyScZrMLfp++uRm2srpqRjsRZIIBzH74NtSkdEUHImR4G7f7XJ+sVZjCc6KDDK04YEpQ==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@unrs/rspack-resolver-binding-linux-arm64-musl@1.2.2':
resolution: {integrity: sha512-gMiG3DCFioJxdGBzhlL86KcFgt9HGz0iDhw0YVYPsShItpN5pqIkNrI+L/Q/0gfDiGrfcE0X3VANSYIPmqEAlQ==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@unrs/rspack-resolver-binding-linux-x64-gnu@1.2.2':
resolution: {integrity: sha512-n/4n2CxaUF9tcaJxEaZm+lqvaw2gflfWQ1R9I7WQgYkKEKbRKbpG/R3hopYdUmLSRI4xaW1Cy0Bz40eS2Yi4Sw==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@unrs/rspack-resolver-binding-linux-x64-musl@1.2.2':
resolution: {integrity: sha512-cHyhAr6rlYYbon1L2Ag449YCj3p6XMfcYTP0AQX+KkQo025d1y/VFtPWvjMhuEsE2lLvtHm7GdJozj6BOMtzVg==}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@unrs/rspack-resolver-binding-wasm32-wasi@1.2.2':
resolution: {integrity: sha512-eogDKuICghDLGc32FtP+WniG38IB1RcGOGz0G3z8406dUdjJvxfHGuGs/dSlM9YEp/v0lEqhJ4mBu6X2nL9pog==}
@@ -2301,24 +2325,28 @@ packages:
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
lightningcss-linux-arm64-musl@1.29.2:
resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
lightningcss-linux-x64-gnu@1.29.2:
resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
lightningcss-linux-x64-musl@1.29.2:
resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
+ libc: [musl]
lightningcss-win32-arm64-msvc@1.29.2:
resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==}