128 lines
3.7 KiB
TypeScript
128 lines
3.7 KiB
TypeScript
// lib/auth.ts - 简化版认证工具
|
|
// 用于在 Next.js 中获取主应用的登录信息
|
|
|
|
export interface User {
|
|
id: string;
|
|
username: string;
|
|
email: string;
|
|
subscription_tier: string;
|
|
avatar?: string;
|
|
}
|
|
|
|
export interface AuthInfo {
|
|
isAuthenticated: boolean;
|
|
user?: User;
|
|
canAccessChat?: boolean;
|
|
message?: string;
|
|
}
|
|
|
|
/**
|
|
* 客户端检查认证状态
|
|
* 直接调用 Flask 后端的 session 接口
|
|
*/
|
|
export async function checkAuth(): Promise<AuthInfo> {
|
|
try {
|
|
// 根据访问方式决定 API URL
|
|
const isProduction = typeof window !== 'undefined' && window.location.hostname === 'valuefrontier.cn';
|
|
const apiUrl = isProduction
|
|
? 'https://valuefrontier.cn' // 生产环境通过域名访问
|
|
: (process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5001'); // 开发环境
|
|
|
|
console.log('Auth check - API URL:', apiUrl, 'isProduction:', isProduction);
|
|
|
|
// 调用主应用的 session 检查接口
|
|
const response = await fetch(`${apiUrl}/api/auth/session`, {
|
|
method: 'GET',
|
|
credentials: 'include', // 重要:携带 Cookie
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
},
|
|
// 对于同域请求,不需要额外的 CORS 配置
|
|
});
|
|
|
|
if (!response.ok) {
|
|
return { isAuthenticated: false };
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
// 检查是否登录
|
|
if (!data.isAuthenticated || !data.user) {
|
|
return {
|
|
isAuthenticated: false,
|
|
message: '请先登录'
|
|
};
|
|
}
|
|
|
|
// 检查订阅权限 - 注意:后端返回的是 subscription_type 而不是 subscription_tier
|
|
const userType = data.user.subscription_type?.toLowerCase() || '';
|
|
const userTier = data.user.subscription_tier?.toLowerCase() || '';
|
|
const subscriptionLevel = userType || userTier; // 兼容两个字段
|
|
|
|
// 检查是否为付费用户
|
|
const canAccessChat =
|
|
['max', 'premium', 'pro', 'enterprise', 'vip', 'plus'].includes(subscriptionLevel) ||
|
|
data.user.is_subscription_active === true ||
|
|
data.user.subscription_status === 'active';
|
|
|
|
console.log('User subscription:', {
|
|
type: data.user.subscription_type,
|
|
tier: data.user.subscription_tier,
|
|
active: data.user.is_subscription_active,
|
|
status: data.user.subscription_status,
|
|
canAccess: canAccessChat
|
|
});
|
|
|
|
return {
|
|
isAuthenticated: true,
|
|
user: data.user,
|
|
canAccessChat,
|
|
message: canAccessChat ? undefined : '需要订阅才能使用 AI 助手功能'
|
|
};
|
|
} catch (error) {
|
|
console.error('Auth check failed:', error);
|
|
return {
|
|
isAuthenticated: false,
|
|
message: '认证服务暂时不可用'
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 调用 MCP API
|
|
* 自动携带认证信息
|
|
*/
|
|
export async function callMCPApi(endpoint: string, options: RequestInit = {}) {
|
|
const url = `${process.env.NEXT_PUBLIC_API_URL}${endpoint}`;
|
|
|
|
const response = await fetch(url, {
|
|
...options,
|
|
credentials: 'include', // 携带 Cookie
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...options.headers,
|
|
},
|
|
});
|
|
|
|
if (response.status === 401) {
|
|
// 未登录,跳转到主应用登录页
|
|
window.location.href = `${process.env.NEXT_PUBLIC_MAIN_APP_URL}/auth/sign-in?redirect=/chat`;
|
|
throw new Error('Unauthorized');
|
|
}
|
|
|
|
if (response.status === 403) {
|
|
// 无权限,跳转到订阅页
|
|
window.location.href = `${process.env.NEXT_PUBLIC_MAIN_APP_URL}/subscription?feature=ai-chat`;
|
|
throw new Error('Subscription required');
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
/**
|
|
* 登出(跳转到主应用的登出接口)
|
|
*/
|
|
export function logout() {
|
|
window.location.href = `${process.env.NEXT_PUBLIC_MAIN_APP_URL}/api/auth/logout?redirect=/`;
|
|
} |