Remove .env.local file and update middleware, page, and auth callback files to enhance logging and user experience. Translate comments and console logs to English for better clarity. Refactor Create Short URL form and Debug page for improved user feedback and error handling.

This commit is contained in:
2025-04-23 18:17:41 +08:00
parent ebb1e77ecc
commit bee8369a6b
9 changed files with 196 additions and 192 deletions

View File

@@ -1,2 +0,0 @@
# Override the port to match what's in .env
PORT=3007

View File

@@ -9,38 +9,38 @@ export async function GET(request: NextRequest) {
console.log('Auth callback received:', { url: request.url, hasCode: !!code }); console.log('Auth callback received:', { url: request.url, hasCode: !!code });
// 如果没有code参数则重定向到登录页面 // If no code parameter found, redirect to login page
if (!code) { if (!code) {
console.log('没有找到code参数重定向到登录页面'); console.log('No code parameter found, redirecting to login page');
return NextResponse.redirect(new URL('/login', request.url)); return NextResponse.redirect(new URL('/login', request.url));
} }
try { try {
// 创建supabase客户端 // Create Supabase client
const cookieStore = cookies(); const cookieStore = cookies();
const supabaseRouteHandler = createRouteHandlerClient({ cookies: () => cookieStore }); const supabaseRouteHandler = createRouteHandlerClient({ cookies: () => cookieStore });
// 交换code获取会话 // Exchange code for session
console.log('开始交换code获取会话'); console.log('Starting code exchange for session');
const { data, error } = await supabaseRouteHandler.auth.exchangeCodeForSession(code); const { data, error } = await supabaseRouteHandler.auth.exchangeCodeForSession(code);
if (error) { if (error) {
console.error('交换会话时出错:', error); console.error('Error exchanging code for session:', error);
throw error; throw error;
} }
console.log('成功获取会话,用户:', data.session?.user.email); console.log('Successfully retrieved session, user:', data.session?.user.email);
// 检查会话是否成功创建 // Check if session was successfully created
if (data.session) { if (data.session) {
console.log('会话创建成功:', { console.log('Session created successfully:', {
userId: data.session.user.id, userId: data.session.user.id,
email: data.session.user.email, email: data.session.user.email,
expiresAt: data.session.expires_at ? new Date(data.session.expires_at * 1000).toISOString() : 'unknown' expiresAt: data.session.expires_at ? new Date(data.session.expires_at * 1000).toISOString() : 'unknown'
}); });
// 设置额外的cookie以确保客户端能检测到登录状态 // Set additional cookie to ensure client can detect login status
// 使用Next.jsResponse来设置cookie // Use Next.js Response to set cookie
const response = NextResponse.redirect(new URL('/', request.url)); const response = NextResponse.redirect(new URL('/', request.url));
response.cookies.set({ response.cookies.set({
name: 'sb-auth-token', name: 'sb-auth-token',
@@ -51,16 +51,16 @@ export async function GET(request: NextRequest) {
secure: process.env.NODE_ENV === 'production', secure: process.env.NODE_ENV === 'production',
httpOnly: false, httpOnly: false,
}); });
console.log('设置了备用cookie: sb-auth-token'); console.log('Set backup cookie: sb-auth-token');
return response; return response;
} }
// 优先使用应用程序根路径重定向 // Redirect to home page by default
console.log('重定向到首页'); console.log('Redirecting to home page');
return NextResponse.redirect(new URL('/', request.url)); return NextResponse.redirect(new URL('/', request.url));
} catch (error) { } catch (error) {
console.error('Auth callback error:', error); console.error('Auth callback error:', error);
// 出错时重定向到登录页面 // Redirect to login page on error
return NextResponse.redirect( return NextResponse.redirect(
new URL('/login?message=Authentication failed. Please try again.', request.url) new URL('/login?message=Authentication failed. Please try again.', request.url)
); );

View File

@@ -47,11 +47,11 @@ function CreateShortUrlForm() {
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState(false); const [success, setSuccess] = useState(false);
// 使用 useEffect 在加载时添加用户信息到表单数据中 // Use useEffect to add user information to form data when loading
useEffect(() => { useEffect(() => {
if (user) { if (user) {
console.log('当前用户:', user.email); console.log('Current user:', user.email);
// 可以在这里添加用户相关数据到表单中 // Can add user-related data to the form here
} }
}, [user]); }, [user]);
@@ -93,32 +93,32 @@ function CreateShortUrlForm() {
setError(null); setError(null);
try { try {
// 验证必填字段 // Validate required fields
if (!formData.originalUrl) { if (!formData.originalUrl) {
throw new Error('原始 URL 是必填项'); throw new Error('Original URL is required');
} }
if (!formData.title) { if (!formData.title) {
throw new Error('标题是必填项'); throw new Error('Title is required');
} }
if (!formData.teamId) { if (!formData.teamId) {
throw new Error('团队是必填项'); throw new Error('Team is required');
} }
if (!formData.projectId) { if (!formData.projectId) {
throw new Error('项目是必填项'); throw new Error('Project is required');
} }
if (!formData.domain) { if (!formData.domain) {
throw new Error('域名是必填项'); throw new Error('Domain is required');
} }
// 按照API要求构建请求数据 // Build request data according to API requirements
const requestData = { const requestData = {
type: "shorturl", type: "shorturl",
attributes: { attributes: {
// 可以添加任何额外属性但attributes不能为空 // Can add any additional attributes, but attributes cannot be empty
icon: "" icon: ""
}, },
shortUrl: { shortUrl: {
@@ -134,20 +134,20 @@ function CreateShortUrlForm() {
tagIds: formData.tags && formData.tags.length > 0 ? formData.tags : undefined tagIds: formData.tags && formData.tags.length > 0 ? formData.tags : undefined
}; };
// 调用 API 创建 shorturl 资源 // Call API to create shorturl resource
const response = await limqRequest('resource/shorturl', 'POST', requestData as unknown as Record<string, unknown>); const response = await limqRequest('resource/shorturl', 'POST', requestData as unknown as Record<string, unknown>);
console.log('创建成功:', response); console.log('Creation successful:', response);
setSuccess(true); setSuccess(true);
// 2秒后跳转到链接列表页面 // Redirect to links list page after 2 seconds
setTimeout(() => { setTimeout(() => {
router.push('/links'); router.push('/links');
}, 2000); }, 2000);
} catch (err) { } catch (err) {
console.error('创建短链接失败:', err); console.error('Failed to create short URL:', err);
setError(err instanceof Error ? err.message : '创建短链接失败,请稍后重试'); setError(err instanceof Error ? err.message : 'Failed to create short URL, please try again later');
} finally { } finally {
setIsSubmitting(false); setIsSubmitting(false);
} }
@@ -188,7 +188,7 @@ function CreateShortUrlForm() {
</div> </div>
<div className="ml-3"> <div className="ml-3">
<p className="text-sm text-green-700"> <p className="text-sm text-green-700">
... Short URL created successfully! Redirecting...
</p> </p>
</div> </div>
</div> </div>
@@ -196,10 +196,10 @@ function CreateShortUrlForm() {
)} )}
<form onSubmit={handleSubmit} className="p-6 space-y-6"> <form onSubmit={handleSubmit} className="p-6 space-y-6">
{/* 标题 */} {/* Title */}
<div> <div>
<label htmlFor="title" className="block text-sm font-medium text-gray-700"> <label htmlFor="title" className="block text-sm font-medium text-gray-700">
<span className="text-red-500">*</span> Title <span className="text-red-500">*</span>
</label> </label>
<input <input
type="text" type="text"
@@ -207,16 +207,16 @@ function CreateShortUrlForm() {
name="title" name="title"
value={formData.title} value={formData.title}
onChange={handleChange} onChange={handleChange}
placeholder="例如:产品发布活动" placeholder="e.g.: Product Launch Campaign"
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
required required
/> />
</div> </div>
{/* 原始 URL */} {/* Original URL */}
<div> <div>
<label htmlFor="originalUrl" className="block text-sm font-medium text-gray-700"> <label htmlFor="originalUrl" className="block text-sm font-medium text-gray-700">
URL <span className="text-red-500">*</span> Original URL <span className="text-red-500">*</span>
</label> </label>
<input <input
type="url" type="url"
@@ -230,10 +230,10 @@ function CreateShortUrlForm() {
/> />
</div> </div>
{/* 自定义短链接 */} {/* Custom Short Link */}
<div> <div>
<label htmlFor="customSlug" className="block text-sm font-medium text-gray-700"> <label htmlFor="customSlug" className="block text-sm font-medium text-gray-700">
<span className="text-gray-500">()</span> Custom Short Link <span className="text-gray-500">(Optional)</span>
</label> </label>
<div className="flex mt-1 rounded-md shadow-sm"> <div className="flex mt-1 rounded-md shadow-sm">
<span className="inline-flex items-center px-3 py-2 text-sm text-gray-500 border border-r-0 border-gray-300 rounded-l-md bg-gray-50"> <span className="inline-flex items-center px-3 py-2 text-sm text-gray-500 border border-r-0 border-gray-300 rounded-l-md bg-gray-50">
@@ -250,14 +250,14 @@ function CreateShortUrlForm() {
/> />
</div> </div>
<p className="mt-1 text-xs text-gray-500"> <p className="mt-1 text-xs text-gray-500">
Leave blank to generate a random short link
</p> </p>
</div> </div>
{/* 域名 */} {/* Domain */}
<div> <div>
<label htmlFor="domain" className="block text-sm font-medium text-gray-700"> <label htmlFor="domain" className="block text-sm font-medium text-gray-700">
<span className="text-red-500">*</span> Domain <span className="text-red-500">*</span>
</label> </label>
<input <input
type="text" type="text"
@@ -265,16 +265,16 @@ function CreateShortUrlForm() {
name="domain" name="domain"
value={formData.domain} value={formData.domain}
onChange={handleChange} onChange={handleChange}
placeholder="例如:googleads.link" placeholder="e.g.: googleads.link"
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
required required
/> />
</div> </div>
{/* 团队选择 */} {/* Team Selection */}
<div> <div>
<label htmlFor="teamId" className="block text-sm font-medium text-gray-700"> <label htmlFor="teamId" className="block text-sm font-medium text-gray-700">
<span className="text-red-500">*</span> Team <span className="text-red-500">*</span>
</label> </label>
<div className="mt-1"> <div className="mt-1">
<TeamSelector <TeamSelector
@@ -283,7 +283,7 @@ function CreateShortUrlForm() {
setFormData(prev => ({ setFormData(prev => ({
...prev, ...prev,
teamId: teamId as string, teamId: teamId as string,
// 当团队变更时清除已选项目 // Clear selected project when team changes
projectId: '' projectId: ''
})); }));
}} }}
@@ -291,10 +291,10 @@ function CreateShortUrlForm() {
</div> </div>
</div> </div>
{/* 项目选择 */} {/* Project Selection */}
<div> <div>
<label htmlFor="projectId" className="block text-sm font-medium text-gray-700"> <label htmlFor="projectId" className="block text-sm font-medium text-gray-700">
<span className="text-red-500">*</span> Project <span className="text-red-500">*</span>
</label> </label>
<div className="mt-1"> <div className="mt-1">
<ProjectSelector <ProjectSelector
@@ -310,10 +310,10 @@ function CreateShortUrlForm() {
</div> </div>
</div> </div>
{/* 描述 */} {/* Description */}
<div> <div>
<label htmlFor="description" className="block text-sm font-medium text-gray-700"> <label htmlFor="description" className="block text-sm font-medium text-gray-700">
<span className="text-gray-500">()</span> Description <span className="text-gray-500">(Optional)</span>
</label> </label>
<textarea <textarea
id="description" id="description"
@@ -321,15 +321,15 @@ function CreateShortUrlForm() {
value={formData.description} value={formData.description}
onChange={handleChange} onChange={handleChange}
rows={3} rows={3}
placeholder="对此链接的简短描述" placeholder="A brief description of this link"
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/> />
</div> </div>
{/* 标签 */} {/* Tags */}
<div> <div>
<label htmlFor="tagInput" className="block text-sm font-medium text-gray-700"> <label htmlFor="tagInput" className="block text-sm font-medium text-gray-700">
<span className="text-gray-500">()</span> Tags <span className="text-gray-500">(Optional)</span>
</label> </label>
<div className="flex mt-1 rounded-md shadow-sm"> <div className="flex mt-1 rounded-md shadow-sm">
<input <input
@@ -338,7 +338,7 @@ function CreateShortUrlForm() {
value={tagInput} value={tagInput}
onChange={(e) => setTagInput(e.target.value)} onChange={(e) => setTagInput(e.target.value)}
onKeyDown={handleTagKeyDown} onKeyDown={handleTagKeyDown}
placeholder="添加标签并按 Enter" placeholder="Add a tag and press Enter"
className="flex-1 block w-full min-w-0 px-3 py-2 border border-gray-300 rounded-l-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" className="flex-1 block w-full min-w-0 px-3 py-2 border border-gray-300 rounded-l-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/> />
<button <button
@@ -346,7 +346,7 @@ function CreateShortUrlForm() {
onClick={addTag} onClick={addTag}
className="inline-flex items-center px-3 py-2 text-sm font-medium text-white border border-transparent rounded-r-md shadow-sm bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" className="inline-flex items-center px-3 py-2 text-sm font-medium text-white border border-transparent rounded-r-md shadow-sm bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
> >
Add
</button> </button>
</div> </div>
@@ -360,7 +360,7 @@ function CreateShortUrlForm() {
onClick={() => removeTag(tag)} onClick={() => removeTag(tag)}
className="flex-shrink-0 ml-1 text-blue-500 rounded-full hover:text-blue-700 focus:outline-none" className="flex-shrink-0 ml-1 text-blue-500 rounded-full hover:text-blue-700 focus:outline-none"
> >
<span className="sr-only"> {tag}</span> <span className="sr-only">Remove tag {tag}</span>
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" /> <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
</svg> </svg>
@@ -371,14 +371,14 @@ function CreateShortUrlForm() {
)} )}
</div> </div>
{/* 提交按钮 */} {/* Submit Button */}
<div className="flex justify-end pt-4 border-t border-gray-200"> <div className="flex justify-end pt-4 border-t border-gray-200">
<button <button
type="button" type="button"
onClick={() => router.back()} onClick={() => router.back()}
className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 mr-3" className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 mr-3"
> >
Cancel
</button> </button>
<button <button
type="submit" type="submit"
@@ -391,9 +391,9 @@ function CreateShortUrlForm() {
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle> <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg> </svg>
... Processing...
</> </>
) : '创建短链接'} ) : 'Create Short URL'}
</button> </button>
</div> </div>
</form> </form>

View File

@@ -3,16 +3,22 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useAuth } from '@/lib/auth'; import { useAuth } from '@/lib/auth';
import supabase from '@/lib/supabase'; import supabase from '@/lib/supabase';
import { Session, User } from '@supabase/supabase-js';
interface SessionData {
session: Session | null;
user?: User | null;
}
export default function DebugPage() { export default function DebugPage() {
const { user, session, isLoading } = useAuth(); const { user, session, isLoading } = useAuth();
const [cookies, setCookies] = useState<Record<string, string>>({}); const [cookies, setCookies] = useState<Record<string, string>>({});
const [rawCookies, setRawCookies] = useState(''); const [rawCookies, setRawCookies] = useState('');
const [sessionData, setSessionData] = useState<{ session: any; user: any } | null>(null); const [sessionData, setSessionData] = useState<SessionData | null>(null);
const [redirectTarget, setRedirectTarget] = useState('/analytics'); const [redirectTarget, setRedirectTarget] = useState('/analytics');
useEffect(() => { useEffect(() => {
// 获取所有cookie // Get all cookies
const allCookies = document.cookie.split(';').reduce((acc, cookie) => { const allCookies = document.cookie.split(';').reduce((acc, cookie) => {
const [key, value] = cookie.trim().split('='); const [key, value] = cookie.trim().split('=');
if (key) acc[key] = value || ''; if (key) acc[key] = value || '';
@@ -22,10 +28,10 @@ export default function DebugPage() {
setCookies(allCookies); setCookies(allCookies);
setRawCookies(document.cookie); setRawCookies(document.cookie);
// 测试supabase会话 // Test Supabase session
const testSession = async () => { const testSession = async () => {
try { try {
console.log('正在获取Supabase会话'); console.log('Getting Supabase session');
const { data, error } = await supabase.auth.getSession(); const { data, error } = await supabase.auth.getSession();
console.log('Supabase session result:', { data, error }); console.log('Supabase session result:', { data, error });
@@ -35,7 +41,7 @@ export default function DebugPage() {
setSessionData(data); setSessionData(data);
} }
} catch (err) { } catch (err) {
console.error('获取会话出错:', err); console.error('Error getting session:', err);
} }
}; };
@@ -44,17 +50,17 @@ export default function DebugPage() {
const refreshSession = async () => { const refreshSession = async () => {
try { try {
console.log('手动刷新会话'); console.log('Manually refreshing session');
const { data, error } = await supabase.auth.refreshSession(); const { data, error } = await supabase.auth.refreshSession();
console.log('刷新结果:', { data, error }); console.log('Refresh result:', { data, error });
alert('会话刷新完成,请查看控制台日志'); alert('Session refresh complete, please check console logs');
if (!error && data.session) { if (!error && data.session) {
window.location.reload(); window.location.reload();
} }
} catch (err) { } catch (err) {
console.error('刷新会话出错:', err); console.error('Error refreshing session:', err);
alert('刷新会话出错: ' + String(err)); alert('Error refreshing session: ' + String(err));
} }
}; };
@@ -66,58 +72,58 @@ export default function DebugPage() {
return ( return (
<div className="container mx-auto p-8"> <div className="container mx-auto p-8">
<h1 className="text-3xl font-bold mb-6"></h1> <h1 className="text-3xl font-bold mb-6">Authentication Debug Page</h1>
<div className="bg-gray-100 p-6 rounded-lg mb-6"> <div className="bg-gray-100 p-6 rounded-lg mb-6">
<h2 className="text-xl font-semibold mb-4"></h2> <h2 className="text-xl font-semibold mb-4">User Status</h2>
<div className="space-y-2"> <div className="space-y-2">
<p>: {isLoading ? '加载中...' : '已加载'}</p> <p>Loading status: {isLoading ? 'Loading...' : 'Loaded'}</p>
<p>: {user ? '' : ''}</p> <p>Logged in: {user ? 'Yes' : 'No'}</p>
<p>: {user?.email || '未登录'}</p> <p>User email: {user?.email || 'Not logged in'}</p>
<p>ID: {user?.id || '未登录'}</p> <p>User ID: {user?.id || 'Not logged in'}</p>
<p>: {session ? '' : ''}</p> <p>Session valid: {session ? 'Yes' : 'No'}</p>
<p>: {session?.expires_at ? new Date(session.expires_at * 1000).toLocaleString() : '无会话'}</p> <p>Session expires: {session?.expires_at ? new Date(session.expires_at * 1000).toLocaleString() : 'No session'}</p>
</div> </div>
</div> </div>
<div className="bg-gray-100 p-6 rounded-lg mb-6"> <div className="bg-gray-100 p-6 rounded-lg mb-6">
<h2 className="text-xl font-semibold mb-4">Supabase </h2> <h2 className="text-xl font-semibold mb-4">Supabase Session Data</h2>
<pre className="bg-gray-200 p-4 rounded text-xs overflow-auto max-h-60"> <pre className="bg-gray-200 p-4 rounded text-xs overflow-auto max-h-60">
{sessionData ? JSON.stringify(sessionData, null, 2) : '加载中...'} {sessionData ? JSON.stringify(sessionData, null, 2) : 'Loading...'}
</pre> </pre>
<button <button
onClick={refreshSession} onClick={refreshSession}
className="mt-4 px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600" className="mt-4 px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
> >
Refresh Session
</button> </button>
</div> </div>
<div className="bg-gray-100 p-6 rounded-lg mb-6"> <div className="bg-gray-100 p-6 rounded-lg mb-6">
<h2 className="text-xl font-semibold mb-4">Cookies </h2> <h2 className="text-xl font-semibold mb-4">Cookie Information</h2>
<div className="space-y-2"> <div className="space-y-2">
<p className="text-sm mb-2">Cookie字符串:</p> <p className="text-sm mb-2">Raw cookie string:</p>
<pre className="bg-gray-200 p-4 rounded overflow-x-auto text-xs"> <pre className="bg-gray-200 p-4 rounded overflow-x-auto text-xs">
{rawCookies || '(empty)'} {rawCookies || '(empty)'}
</pre> </pre>
<p className="text-sm mt-4 mb-2">Cookies:</p> <p className="text-sm mt-4 mb-2">Parsed cookies:</p>
<pre className="bg-gray-200 p-4 rounded overflow-x-auto text-xs"> <pre className="bg-gray-200 p-4 rounded overflow-x-auto text-xs">
{JSON.stringify(cookies, null, 2) || '{}'} {JSON.stringify(cookies, null, 2) || '{}'}
</pre> </pre>
<p className="text-sm mt-4 mb-2">Supabase相关Cookies:</p> <p className="text-sm mt-4 mb-2">Supabase-related cookies:</p>
<div className="space-y-1"> <div className="space-y-1">
<p>sb-access-token: {cookies['sb-access-token'] ? '存在' : '不存在'}</p> <p>sb-access-token: {cookies['sb-access-token'] ? 'Exists' : 'Not found'}</p>
<p>sb-refresh-token: {cookies['sb-refresh-token'] ? '存在' : '不存在'}</p> <p>sb-refresh-token: {cookies['sb-refresh-token'] ? 'Exists' : 'Not found'}</p>
<p>supabase-auth-token: {cookies['supabase-auth-token'] ? '存在' : '不存在'}</p> <p>supabase-auth-token: {cookies['supabase-auth-token'] ? 'Exists' : 'Not found'}</p>
</div> </div>
</div> </div>
</div> </div>
<div className="bg-gray-100 p-6 rounded-lg mb-6"> <div className="bg-gray-100 p-6 rounded-lg mb-6">
<h2 className="text-xl font-semibold mb-4"></h2> <h2 className="text-xl font-semibold mb-4">Manual Redirect</h2>
<div className="flex space-x-2 items-center"> <div className="flex space-x-2 items-center">
<input <input
type="text" type="text"
@@ -130,7 +136,7 @@ export default function DebugPage() {
onClick={forceRedirect} onClick={forceRedirect}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600" className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
> >
Force Redirect
</button> </button>
</div> </div>
</div> </div>
@@ -140,21 +146,21 @@ export default function DebugPage() {
onClick={() => window.location.href = '/login'} onClick={() => window.location.href = '/login'}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600" className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
> >
Go to Login
</button> </button>
<button <button
onClick={async () => { onClick={async () => {
try { try {
await supabase.auth.signOut(); await supabase.auth.signOut();
alert('已登出,刷新页面中...'); alert('Signed out, refreshing page...');
window.location.reload(); window.location.reload();
} catch (err) { } catch (err) {
alert('登出出错: ' + String(err)); alert('Error signing out: ' + String(err));
} }
}} }}
className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600" className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
> >
Sign Out
</button> </button>
</div> </div>
</div> </div>

View File

@@ -29,7 +29,7 @@ export default function LoginPage() {
const [message, setMessage] = useState({ type: '', content: '' }); const [message, setMessage] = useState({ type: '', content: '' });
const [redirectUrl, setRedirectUrl] = useState<string | null>(null); const [redirectUrl, setRedirectUrl] = useState<string | null>(null);
// 获取重定向URL // Get redirect URL
useEffect(() => { useEffect(() => {
if (searchParams) { if (searchParams) {
const redirect = searchParams.get('redirect'); const redirect = searchParams.get('redirect');
@@ -39,19 +39,19 @@ export default function LoginPage() {
} }
}, [searchParams]); }, [searchParams]);
// 如果用户已登录,重定向到原始页面或首页 // If user is logged in, redirect to original page or home page
useEffect(() => { useEffect(() => {
if (user) { if (user) {
console.log('用户已登录,准备重定向', { redirectUrl }); console.log('User is logged in, preparing to redirect', { redirectUrl });
// 添加短暂延时确保状态更新完成 // Add a short delay to ensure state updates are complete
setTimeout(() => { setTimeout(() => {
if (redirectUrl) { if (redirectUrl) {
// 使用硬重定向替代router.push // Use hard redirect instead of router.push
console.log('重定向到原始URL:', redirectUrl); console.log('Redirecting to original URL:', redirectUrl);
window.location.href = redirectUrl; window.location.href = redirectUrl;
} else { } else {
console.log('重定向到首页'); console.log('Redirecting to home page');
window.location.href = '/'; window.location.href = '/';
} }
}, 100); }, 100);
@@ -79,7 +79,7 @@ export default function LoginPage() {
throw new Error(error instanceof Error ? error.message : 'Unknown error'); throw new Error(error instanceof Error ? error.message : 'Unknown error');
} }
// 登录成功后,会通过 useEffect 重定向 // After successful login, redirect via useEffect
} catch (error) { } catch (error) {
console.error('Login error:', error); console.error('Login error:', error);
setMessage({ setMessage({

View File

@@ -6,32 +6,32 @@ import { useAuth } from '@/lib/auth';
export default function Home() { export default function Home() {
const { user, isLoading } = useAuth(); const { user, isLoading } = useAuth();
// 添加调试日志 // Add debug logs
console.log('根页面状态:', { isLoading, userAuthenticated: !!user }); console.log('Root page state:', { isLoading, userAuthenticated: !!user });
useEffect(() => { useEffect(() => {
if (!isLoading) { if (!isLoading) {
console.log('准备从根页面重定向', { isLoggedIn: !!user }); console.log('Preparing to redirect from root page', { isLoggedIn: !!user });
// 使用硬重定向确保页面完全刷新 // Use hard redirect to ensure full page refresh
if (user) { if (user) {
console.log('用户已登录,重定向到分析页面'); console.log('User is logged in, redirecting to analytics page');
window.location.href = '/analytics'; window.location.href = '/analytics';
} else { } else {
console.log('用户未登录,重定向到登录页面'); console.log('User is not logged in, redirecting to login page');
window.location.href = '/login'; window.location.href = '/login';
} }
} }
}, [isLoading, user]); }, [isLoading, user]);
// 显示加载指示器,并包含状态信息 // Display loading indicator with status information
return ( return (
<div className="flex items-center justify-center min-h-screen"> <div className="flex items-center justify-center min-h-screen">
<div className="text-center"> <div className="text-center">
<div className="animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500 mx-auto"></div> <div className="animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500 mx-auto"></div>
<p className="mt-4 text-lg text-gray-700">...</p> <p className="mt-4 text-lg text-gray-700">Loading...</p>
<p className="mt-2 text-sm text-gray-500"> <p className="mt-2 text-sm text-gray-500">
: {isLoading ? '检查登录中' : (user ? '已登录' : '未登录')} Status: {isLoading ? 'Checking login status' : (user ? 'Logged in' : 'Not logged in')}
</p> </p>
</div> </div>
</div> </div>

View File

@@ -5,10 +5,10 @@ import { useRouter } from 'next/navigation';
import { Session, User } from '@supabase/supabase-js'; import { Session, User } from '@supabase/supabase-js';
import supabase from './supabase'; import supabase from './supabase';
// 定义用户类型 // Define user type
export type AuthUser = User | null; export type AuthUser = User | null;
// 定义验证上下文类型 // Define auth context type
export type AuthContextType = { export type AuthContextType = {
user: AuthUser; user: AuthUser;
session: Session | null; session: Session | null;
@@ -20,22 +20,22 @@ export type AuthContextType = {
signOut: () => Promise<void>; signOut: () => Promise<void>;
}; };
// 创建验证上下文 // Create auth context
const AuthContext = createContext<AuthContextType | undefined>(undefined); const AuthContext = createContext<AuthContextType | undefined>(undefined);
// 验证提供者组件 // Auth provider component
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [user, setUser] = useState<AuthUser>(null); const [user, setUser] = useState<AuthUser>(null);
const [session, setSession] = useState<Session | null>(null); const [session, setSession] = useState<Session | null>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const router = useRouter(); const router = useRouter();
// 初始化验证状态 // Initialize auth state
useEffect(() => { useEffect(() => {
const getSession = async () => { const getSession = async () => {
setIsLoading(true); setIsLoading(true);
try { try {
// 尝试从Supabase获取会话 // Try to get session from Supabase
const { data: { session }, error } = await supabase.auth.getSession(); const { data: { session }, error } = await supabase.auth.getSession();
if (error) { if (error) {
@@ -43,7 +43,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
return; return;
} }
// 打印会话信息,帮助调试 // Print session info for debugging
console.log('Supabase session loaded:', session ? 'Found' : 'Not found'); console.log('Supabase session loaded:', session ? 'Found' : 'Not found');
if (session) { if (session) {
console.log('User authenticated:', session.user.email); console.log('User authenticated:', session.user.email);
@@ -63,7 +63,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
getSession(); getSession();
// 监听验证状态变化 // Listen for auth state changes
const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
console.log('Auth state changed, event:', _event); console.log('Auth state changed, event:', _event);
console.log('New session:', session ? 'Valid' : 'None'); console.log('New session:', session ? 'Valid' : 'None');
@@ -71,56 +71,56 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
setUser(session?.user || null); setUser(session?.user || null);
}); });
// 清理函数 // Cleanup function
return () => { return () => {
subscription.unsubscribe(); subscription.unsubscribe();
}; };
}, []); }, []);
// 登录函数 // Sign in function
const signIn = async (email: string, password: string) => { const signIn = async (email: string, password: string) => {
setIsLoading(true); setIsLoading(true);
try { try {
console.log('尝试登录:', { email }); console.log('Attempting to sign in:', { email });
// 尝试通过Supabase登录 // Try to sign in with Supabase
const { data, error } = await supabase.auth.signInWithPassword({ const { data, error } = await supabase.auth.signInWithPassword({
email, email,
password, password,
}); });
if (error) { if (error) {
console.error('登录出错:', error); console.error('Sign in error:', error);
return { error }; return { error };
} }
// 登录成功,设置会话和用户信息 // Sign in successful, set session and user info
console.log('登录成功,用户信息:', data.user?.email); console.log('Sign in successful, user:', data.user?.email);
setSession(data.session); setSession(data.session);
setUser(data.user); setUser(data.user);
// 使用硬重定向代替router.push确保页面完全刷新 // Use hard redirect instead of router.push to ensure full page refresh
console.log('准备重定向到分析页面'); console.log('Preparing to redirect to analytics page');
// 添加短暂延时确保状态已更新,然后重定向 // Add short delay to ensure state is updated, then redirect
setTimeout(() => { setTimeout(() => {
window.location.href = '/analytics'; window.location.href = '/analytics';
}, 100); }, 100);
return {}; return {};
} catch (error) { } catch (error) {
console.error('登录过程出错:', error); console.error('Error during sign in process:', error);
return { error }; return { error };
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
}; };
// Google登录函数 // Google sign in function
const signInWithGoogle = async () => { const signInWithGoogle = async () => {
setIsLoading(true); setIsLoading(true);
try { try {
// 尝试通过Supabase登录Google // Try to sign in with Google via Supabase
const { error } = await supabase.auth.signInWithOAuth({ const { error } = await supabase.auth.signInWithOAuth({
provider: 'google', provider: 'google',
options: { options: {
@@ -133,24 +133,24 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
}); });
if (error) { if (error) {
console.error('Google登录出错:', error); console.error('Google sign in error:', error);
return { error }; return { error };
} }
return {}; // Return empty object when successful return {}; // Return empty object when successful
} catch (error) { } catch (error) {
console.error('Google登录过程出错:', error); console.error('Error during Google sign in process:', error);
return { error }; return { error };
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
}; };
// GitHub登录函数 // GitHub sign in function
const signInWithGitHub = async () => { const signInWithGitHub = async () => {
setIsLoading(true); setIsLoading(true);
try { try {
// 尝试通过Supabase登录GitHub // Try to sign in with GitHub via Supabase
const { error } = await supabase.auth.signInWithOAuth({ const { error } = await supabase.auth.signInWithOAuth({
provider: 'github', provider: 'github',
options: { options: {
@@ -172,11 +172,11 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
} }
}; };
// 注册函数 // Sign up function
const signUp = async (email: string, password: string) => { const signUp = async (email: string, password: string) => {
setIsLoading(true); setIsLoading(true);
try { try {
// 尝试通过Supabase注册 // Try to sign up via Supabase
const { error } = await supabase.auth.signUp({ const { error } = await supabase.auth.signUp({
email, email,
password, password,
@@ -186,35 +186,35 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
}); });
if (error) { if (error) {
console.error('注册出错:', error); console.error('Sign up error:', error);
throw error; throw error;
} }
// 注册成功后跳转到登录页面并显示确认消息 // After successful registration, redirect to login page with confirmation message
router.push('/login?message=Registration successful! Please check your email to verify your account before logging in.'); router.push('/login?message=Registration successful! Please check your email to verify your account before logging in.');
} catch (error) { } catch (error) {
console.error('注册过程出错:', error); console.error('Error during sign up process:', error);
throw error; throw error;
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
}; };
// 登出函数 // Sign out function
const signOut = async () => { const signOut = async () => {
setIsLoading(true); setIsLoading(true);
try { try {
// 尝试通过Supabase登出 // Try to sign out via Supabase
const { error } = await supabase.auth.signOut(); const { error } = await supabase.auth.signOut();
if (error) { if (error) {
console.error('登出出错:', error); console.error('Sign out error:', error);
throw error; throw error;
} }
setSession(null); setSession(null);
setUser(null); setUser(null);
router.push('/login'); router.push('/login');
} catch (error) { } catch (error) {
console.error('登出过程出错:', error); console.error('Error during sign out process:', error);
throw error; throw error;
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@@ -239,7 +239,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
); );
}; };
// 自定义钩子 // Custom hook
export const useAuth = () => { export const useAuth = () => {
const context = useContext(AuthContext); const context = useContext(AuthContext);
if (context === undefined) { if (context === undefined) {
@@ -248,7 +248,7 @@ export const useAuth = () => {
return context; return context;
}; };
// 受保护路由组件 // Protected route component
export const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => { export const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { user, isLoading } = useAuth(); const { user, isLoading } = useAuth();
const router = useRouter(); const router = useRouter();
@@ -264,7 +264,7 @@ export const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({ childr
<div className="flex items-center justify-center min-h-screen"> <div className="flex items-center justify-center min-h-screen">
<div className="text-center"> <div className="text-center">
<div className="animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500 mx-auto"></div> <div className="animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500 mx-auto"></div>
<p className="mt-4 text-lg text-gray-700 dark:text-gray-300">...</p> <p className="mt-4 text-lg text-gray-700 dark:text-gray-300">Loading...</p>
</div> </div>
</div> </div>
); );

View File

@@ -1,6 +1,6 @@
import { createClient } from '@supabase/supabase-js'; import { createClient } from '@supabase/supabase-js';
// 从环境变量获取Supabase配置 // Get Supabase configuration from environment variables
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL || ''; const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL || '';
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || process.env.SUPABASE_ANON_KEY || ''; const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || process.env.SUPABASE_ANON_KEY || '';
@@ -8,7 +8,7 @@ console.log('Supabase Configuration Check:', {
urlDefined: !!supabaseUrl, urlDefined: !!supabaseUrl,
keyDefined: !!supabaseAnonKey, keyDefined: !!supabaseAnonKey,
url: supabaseUrl, url: supabaseUrl,
// 打印部分key以便调试 // Print partial key for debugging
keyPrefix: supabaseAnonKey ? supabaseAnonKey.substring(0, 20) + '...' : 'undefined', keyPrefix: supabaseAnonKey ? supabaseAnonKey.substring(0, 20) + '...' : 'undefined',
keyLength: supabaseAnonKey ? supabaseAnonKey.length : 0 keyLength: supabaseAnonKey ? supabaseAnonKey.length : 0
}); });
@@ -17,7 +17,7 @@ if (!supabaseUrl || !supabaseAnonKey) {
console.error('Supabase URL and Anon Key are required'); console.error('Supabase URL and Anon Key are required');
} }
// 尝试解码JWT token并打印解码内容 // Try to decode JWT token and print decoded content
try { try {
if (supabaseAnonKey) { if (supabaseAnonKey) {
const parts = supabaseAnonKey.split('.'); const parts = supabaseAnonKey.split('.');
@@ -30,10 +30,10 @@ try {
} }
} }
} catch (error) { } catch (error) {
console.error('JWT解码失败:', error); console.error('JWT decoding failed:', error);
} }
// 创建自定义cookie处理逻辑 // Create custom cookie handling logic
const customStorage = { const customStorage = {
getItem: (key: string): string | null => { getItem: (key: string): string | null => {
if (typeof document === 'undefined') return null; if (typeof document === 'undefined') return null;
@@ -45,28 +45,28 @@ const customStorage = {
setItem: (key: string, value: string): void => { setItem: (key: string, value: string): void => {
if (typeof document === 'undefined') return; if (typeof document === 'undefined') return;
// 获取当前主机和端口,处理localhost上的不同端口情况 // Get current host and port to handle different ports on localhost
const host = typeof window !== 'undefined' ? window.location.hostname : ''; const host = typeof window !== 'undefined' ? window.location.hostname : '';
// 设置cookie对localhost使用通用domain // Set cookie, using generic domain for localhost
document.cookie = `${key}=${value}; path=/; max-age=${60 * 60 * 24 * 7}; samesite=lax; domain=${host}`; document.cookie = `${key}=${value}; path=/; max-age=${60 * 60 * 24 * 7}; samesite=lax; domain=${host}`;
console.log(`Cookie ${key} 已设置,domain=${host}`); console.log(`Cookie ${key} has been set, domain=${host}`);
}, },
removeItem: (key: string): void => { removeItem: (key: string): void => {
if (typeof document === 'undefined') return; if (typeof document === 'undefined') return;
// 获取当前主机和端口,处理localhost上的不同端口情况 // Get current host and port to handle different ports on localhost
const host = typeof window !== 'undefined' ? window.location.hostname : ''; const host = typeof window !== 'undefined' ? window.location.hostname : '';
// 删除cookie对localhost使用通用domain // Remove cookie, using generic domain for localhost
document.cookie = `${key}=; path=/; max-age=0; samesite=lax; domain=${host}`; document.cookie = `${key}=; path=/; max-age=0; samesite=lax; domain=${host}`;
console.log(`Cookie ${key} 已移除`); console.log(`Cookie ${key} has been removed`);
}, },
}; };
// 创建Supabase客户端 // Create Supabase client
export const supabase = createClient(supabaseUrl, supabaseAnonKey, { export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
auth: { auth: {
persistSession: true, persistSession: true,
@@ -77,7 +77,7 @@ export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
} }
}); });
// 测试Supabase连接 // Test Supabase connection
supabase.auth.onAuthStateChange((event, session) => { supabase.auth.onAuthStateChange((event, session) => {
console.log(`Supabase auth event: ${event}`, session ? 'Session exists' : 'No session'); console.log(`Supabase auth event: ${event}`, session ? 'Session exists' : 'No session');
if (session) { if (session) {
@@ -85,7 +85,7 @@ supabase.auth.onAuthStateChange((event, session) => {
} }
}); });
// 尝试执行健康检查 // Try to perform health check
async function checkSupabaseHealth() { async function checkSupabaseHealth() {
try { try {
const { data, error } = await supabase.from('_health').select('*').limit(1); const { data, error } = await supabase.from('_health').select('*').limit(1);

View File

@@ -2,76 +2,76 @@ import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server'; import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) { export function middleware(request: NextRequest) {
// 获取请求的路径 // Get the request path
const path = request.nextUrl.pathname; const path = request.nextUrl.pathname;
console.log(`[Middleware] 请求路径: ${path}`); console.log(`[Middleware] Request path: ${path}`);
// 定义不需要验证的路径 // Define paths that don't require authentication
const publicPaths = ['/login', '/register', '/auth/callback']; const publicPaths = ['/login', '/register', '/auth/callback'];
// API 路由不需要验证 // API routes don't require authentication
if (path.startsWith('/api/')) { if (path.startsWith('/api/')) {
console.log('[Middleware] API路由,跳过验证'); console.log('[Middleware] API route, skipping validation');
return NextResponse.next(); return NextResponse.next();
} }
// 静态资源不需要验证 // Static resources don't require authentication
if (path.includes('/_next/') || path.includes('/static/') || path.match(/\.(ico|png|jpg|jpeg|svg|css|js)$/)) { if (path.includes('/_next/') || path.includes('/static/') || path.match(/\.(ico|png|jpg|jpeg|svg|css|js)$/)) {
console.log('[Middleware] 静态资源,跳过验证'); console.log('[Middleware] Static resource, skipping validation');
return NextResponse.next(); return NextResponse.next();
} }
// 检查是否是公开路径 // Check if it's a public path
const isPublicPath = publicPaths.some(publicPath => path === publicPath || path.startsWith(publicPath)); const isPublicPath = publicPaths.some(publicPath => path === publicPath || path.startsWith(publicPath));
console.log(`[Middleware] 是公开路径: ${isPublicPath}`); console.log(`[Middleware] Is public path: ${isPublicPath}`);
// 获取所有 cookie // Get all cookies
const allCookies = Object.fromEntries(request.cookies.getAll().map(c => [c.name, c.value])); const allCookies = Object.fromEntries(request.cookies.getAll().map(c => [c.name, c.value]));
console.log('[Middleware] 所有Cookie:', JSON.stringify(allCookies)); console.log('[Middleware] All cookies:', JSON.stringify(allCookies));
// 检查各个认证cookie // Check each authentication cookie
const accessToken = request.cookies.get('sb-access-token'); const accessToken = request.cookies.get('sb-access-token');
const refreshToken = request.cookies.get('sb-refresh-token'); const refreshToken = request.cookies.get('sb-refresh-token');
const providerToken = request.cookies.get('sb-provider-token'); const providerToken = request.cookies.get('sb-provider-token');
const authToken = request.cookies.get('supabase-auth-token'); const authToken = request.cookies.get('supabase-auth-token');
const customAuthToken = request.cookies.get('sb-auth-token'); const customAuthToken = request.cookies.get('sb-auth-token');
console.log('[Middleware] 认证Cookie详情:', { console.log('[Middleware] Auth cookie details:', {
'sb-access-token': accessToken ? '存在' : '不存在', 'sb-access-token': accessToken ? 'exists' : 'not found',
'sb-refresh-token': refreshToken ? '存在' : '不存在', 'sb-refresh-token': refreshToken ? 'exists' : 'not found',
'sb-provider-token': providerToken ? '存在' : '不存在', 'sb-provider-token': providerToken ? 'exists' : 'not found',
'supabase-auth-token': authToken ? '存在' : '不存在', 'supabase-auth-token': authToken ? 'exists' : 'not found',
'sb-auth-token': customAuthToken ? '存在' : '不存在' 'sb-auth-token': customAuthToken ? 'exists' : 'not found'
}); });
// 检查用户是否登录 // Check if user is logged in
const isLoggedIn = !!(accessToken || refreshToken || providerToken || authToken || customAuthToken); const isLoggedIn = !!(accessToken || refreshToken || providerToken || authToken || customAuthToken);
console.log(`[Middleware] 用户是否登录: ${isLoggedIn}`); console.log(`[Middleware] User is logged in: ${isLoggedIn}`);
// 如果是公开路径但已登录,重定向到首页 // If it's a public path but user is logged in, redirect to home page
if (isPublicPath && isLoggedIn) { if (isPublicPath && isLoggedIn) {
console.log('[Middleware] 已登录用户访问公开路径,重定向到首页'); console.log('[Middleware] User is logged in and accessing public path, redirecting to home page');
return NextResponse.redirect(new URL('/', request.url)); return NextResponse.redirect(new URL('/', request.url));
} }
// 如果不是公开路径且未登录,重定向到登录页 // If it's not a public path and user is not logged in, redirect to login page
if (!isPublicPath && !isLoggedIn) { if (!isPublicPath && !isLoggedIn) {
console.log('[Middleware] 未登录用户访问私有路径,重定向到登录页'); console.log('[Middleware] User is not logged in and accessing private path, redirecting to login page');
const redirectUrl = new URL('/login', request.url); const redirectUrl = new URL('/login', request.url);
redirectUrl.searchParams.set('redirect', encodeURIComponent(request.url)); redirectUrl.searchParams.set('redirect', encodeURIComponent(request.url));
return NextResponse.redirect(redirectUrl); return NextResponse.redirect(redirectUrl);
} }
console.log('[Middleware] 通过验证,允许访问'); console.log('[Middleware] Validation passed, allowing access');
return NextResponse.next(); return NextResponse.next();
} }
// 配置中间件匹配的路径 // Configure middleware matching paths
export const config = { export const config = {
matcher: [ matcher: [
// 匹配所有路径,但排除静态资源 // Match all paths, but exclude static resources
'/((?!_next/static|_next/image|favicon.ico).*)', '/((?!_next/static|_next/image|favicon.ico).*)',
// 明确包括重要的路由 // Explicitly include important routes
'/', '/',
'/analytics', '/analytics',
'/links', '/links',