login page
This commit is contained in:
256
lib/auth.tsx
Normal file
256
lib/auth.tsx
Normal file
@@ -0,0 +1,256 @@
|
||||
'use client';
|
||||
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Session, User } from '@supabase/supabase-js';
|
||||
import supabase from './supabase';
|
||||
|
||||
// 定义用户类型
|
||||
export type AuthUser = User | null;
|
||||
|
||||
// 定义验证上下文类型
|
||||
export type AuthContextType = {
|
||||
user: AuthUser;
|
||||
session: Session | null;
|
||||
isLoading: boolean;
|
||||
signIn: (email: string, password: string) => Promise<void>;
|
||||
signInWithGoogle: () => Promise<void>;
|
||||
signUp: (email: string, password: string) => Promise<void>;
|
||||
signOut: () => Promise<void>;
|
||||
autoRegisterTestUser: () => Promise<void>; // 添加自动注册测试用户函数
|
||||
};
|
||||
|
||||
// 创建验证上下文
|
||||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||
|
||||
// 测试账户常量 - 使用已验证的账户
|
||||
const TEST_EMAIL = 'vitalitymailg@gmail.com';
|
||||
const TEST_PASSWORD = 'password123';
|
||||
|
||||
// 验证提供者组件
|
||||
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const [user, setUser] = useState<AuthUser>(null);
|
||||
const [session, setSession] = useState<Session | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const router = useRouter();
|
||||
|
||||
// 初始化验证状态
|
||||
useEffect(() => {
|
||||
const getSession = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// 尝试从Supabase获取会话
|
||||
const { data: { session }, error } = await supabase.auth.getSession();
|
||||
|
||||
if (error) {
|
||||
console.error('Error getting session:', error);
|
||||
return;
|
||||
}
|
||||
|
||||
setSession(session);
|
||||
setUser(session?.user || null);
|
||||
} catch (error) {
|
||||
console.error('Unexpected error during getSession:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
getSession();
|
||||
|
||||
// 监听验证状态变化
|
||||
const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
|
||||
setSession(session);
|
||||
setUser(session?.user || null);
|
||||
});
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 登录函数
|
||||
const signIn = async (email: string, password: string) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
console.log('尝试登录:', { email });
|
||||
|
||||
// 尝试通过Supabase登录
|
||||
const { data, error } = await supabase.auth.signInWithPassword({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('登录出错:', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
setSession(data.session);
|
||||
setUser(data.user);
|
||||
router.push('/dashboard');
|
||||
} catch (error) {
|
||||
console.error('登录过程出错:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Google登录函数
|
||||
const signInWithGoogle = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// 尝试通过Supabase登录Google
|
||||
const { error } = await supabase.auth.signInWithOAuth({
|
||||
provider: 'google',
|
||||
options: {
|
||||
redirectTo: `${window.location.origin}/auth/callback`,
|
||||
},
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('Google登录出错:', error);
|
||||
throw error;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Google登录过程出错:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 注册函数
|
||||
const signUp = async (email: string, password: string) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// 尝试通过Supabase注册
|
||||
const { error } = await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('注册出错:', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// 注册成功后跳转到登录页面并显示确认消息
|
||||
router.push('/login?message=注册成功,请查看邮箱确认账户');
|
||||
} catch (error) {
|
||||
console.error('注册过程出错:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 登出函数
|
||||
const signOut = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// 尝试通过Supabase登出
|
||||
const { error } = await supabase.auth.signOut();
|
||||
if (error) {
|
||||
console.error('登出出错:', error);
|
||||
throw error;
|
||||
}
|
||||
setSession(null);
|
||||
setUser(null);
|
||||
router.push('/login');
|
||||
} catch (error) {
|
||||
console.error('登出过程出错:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 自动注册测试用户函数
|
||||
const autoRegisterTestUser = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
console.log('正在使用测试账户登录:', TEST_EMAIL);
|
||||
|
||||
// 使用测试账户直接登录
|
||||
const { data, error } = await supabase.auth.signInWithPassword({
|
||||
email: TEST_EMAIL,
|
||||
password: TEST_PASSWORD,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('测试账户登录失败:', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.log('测试账户登录成功!');
|
||||
setSession(data.session);
|
||||
setUser(data.user);
|
||||
router.push('/dashboard');
|
||||
} catch (error) {
|
||||
console.error('测试账户登录出错:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const contextValue: AuthContextType = {
|
||||
user,
|
||||
session,
|
||||
isLoading,
|
||||
signIn,
|
||||
signInWithGoogle,
|
||||
signUp,
|
||||
signOut,
|
||||
autoRegisterTestUser,
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// 自定义钩子
|
||||
export const useAuth = () => {
|
||||
const context = useContext(AuthContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useAuth must be used within an AuthProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
// 受保护路由组件
|
||||
export const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const { user, isLoading } = useAuth();
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && !user) {
|
||||
router.push('/login');
|
||||
}
|
||||
}, [user, isLoading, router]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<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>
|
||||
<p className="mt-4 text-lg text-gray-700 dark:text-gray-300">加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export default AuthContext;
|
||||
65
lib/supabase.ts
Normal file
65
lib/supabase.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
// 从环境变量获取Supabase配置
|
||||
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 || '';
|
||||
|
||||
console.log('Supabase Configuration Check:', {
|
||||
urlDefined: !!supabaseUrl,
|
||||
keyDefined: !!supabaseAnonKey,
|
||||
url: supabaseUrl,
|
||||
// 打印部分key以便调试
|
||||
keyPrefix: supabaseAnonKey ? supabaseAnonKey.substring(0, 20) + '...' : 'undefined',
|
||||
keyLength: supabaseAnonKey ? supabaseAnonKey.length : 0
|
||||
});
|
||||
|
||||
if (!supabaseUrl || !supabaseAnonKey) {
|
||||
console.error('Supabase URL and Anon Key are required');
|
||||
}
|
||||
|
||||
// 尝试解码JWT token并打印解码内容
|
||||
try {
|
||||
if (supabaseAnonKey) {
|
||||
const parts = supabaseAnonKey.split('.');
|
||||
if (parts.length === 3) {
|
||||
const payload = parts[1];
|
||||
const decoded = atob(payload);
|
||||
console.log('JWT Payload:', decoded);
|
||||
} else {
|
||||
console.error('Invalid JWT format, expected 3 parts but got:', parts.length);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('JWT解码失败:', error);
|
||||
}
|
||||
|
||||
// 创建Supabase客户端
|
||||
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
||||
auth: {
|
||||
persistSession: true,
|
||||
autoRefreshToken: true,
|
||||
detectSessionInUrl: true,
|
||||
}
|
||||
});
|
||||
|
||||
// 测试Supabase连接
|
||||
supabase.auth.onAuthStateChange((event, session) => {
|
||||
console.log(`Supabase auth event: ${event}`, session ? 'Session exists' : 'No session');
|
||||
if (session) {
|
||||
console.log('Current user:', session.user.email);
|
||||
}
|
||||
});
|
||||
|
||||
// 尝试执行健康检查
|
||||
async function checkSupabaseHealth() {
|
||||
try {
|
||||
const { data, error } = await supabase.from('_health').select('*').limit(1);
|
||||
console.log('Supabase health check:', error ? `Error: ${error.message}` : 'Success', data);
|
||||
} catch (error) {
|
||||
console.error('Supabase health check error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
checkSupabaseHealth();
|
||||
|
||||
export default supabase;
|
||||
Reference in New Issue
Block a user