Twilioapp-admin/lib/supabase.ts
mars 1ba859196a 修复退出登录重定向问题和相关功能优化
- 修复DashboardLayout中的退出登录函数,确保清除所有认证信息
- 恢复_app.tsx中的认证逻辑,确保仪表盘页面需要登录访问
- 完善退出登录流程:清除本地存储 -> 调用登出API -> 重定向到登录页面
- 添加错误边界组件提升用户体验
- 优化React水合错误处理
- 添加JWT令牌验证API
- 完善各个仪表盘页面的功能和样式
2025-07-03 20:56:17 +08:00

354 lines
9.7 KiB
TypeScript

import { createClient } from '@supabase/supabase-js';
import { Database } from '../types/database';
// 环境变量检查和默认值
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || 'https://demo.supabase.co';
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || 'demo-key';
const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY || 'demo-service-key';
// 检查是否在演示模式
const isDemoMode = true; // 强制使用演示模式,避免 Supabase 实例创建
console.log('Supabase 配置: 演示模式已启用,不会创建 Supabase 客户端实例');
// 空的客户端对象,用于演示模式
const mockAuth = {
getUser: async () => ({ data: { user: null }, error: null }),
signInWithPassword: async () => ({ data: null, error: new Error('Demo mode') }),
signUp: async () => ({ data: null, error: new Error('Demo mode') }),
signOut: async () => ({ error: null }),
resetPasswordForEmail: async () => ({ data: null, error: new Error('Demo mode') }),
updateUser: async () => ({ data: null, error: new Error('Demo mode') }),
getSession: async () => ({ data: { session: null }, error: null }),
onAuthStateChange: () => ({ data: { subscription: { unsubscribe: () => {} } } })
};
// 导出模拟的客户端
export const supabase = {
auth: mockAuth,
from: () => ({
select: () => Promise.resolve({ data: [], error: null }),
insert: () => Promise.resolve({ data: null, error: new Error('Demo mode') }),
update: () => Promise.resolve({ data: null, error: new Error('Demo mode') }),
delete: () => Promise.resolve({ error: null })
})
} as any;
export const supabaseAdmin = {
auth: mockAuth,
from: () => ({
select: () => Promise.resolve({ data: [], error: null }),
insert: () => Promise.resolve({ data: null, error: new Error('Demo mode') }),
update: () => Promise.resolve({ data: null, error: new Error('Demo mode') }),
delete: () => Promise.resolve({ error: null })
})
} as any;
// 数据库表名常量
export const TABLES = {
USERS: 'users',
CALLS: 'calls',
APPOINTMENTS: 'appointments',
INTERPRETERS: 'interpreters',
DOCUMENTS: 'document_translations',
ORDERS: 'orders',
INVOICES: 'invoices',
ENTERPRISE_EMPLOYEES: 'enterprise_employees',
PRICING_RULES: 'pricing_rules',
NOTIFICATIONS: 'notifications',
SYSTEM_SETTINGS: 'system_settings',
ACCOUNT_BALANCES: 'account_balances',
} as const;
// 实时订阅配置
export const REALTIME_CHANNELS = {
CALLS: 'calls:*',
NOTIFICATIONS: 'notifications:*',
APPOINTMENTS: 'appointments:*',
} as const;
// 用户认证相关函数
export const auth = {
// 获取当前用户
getCurrentUser: async () => {
const { data: { user }, error } = await supabase.auth.getUser();
if (error) throw error;
return user;
},
// 登录
signIn: async (email: string, password: string) => {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) throw error;
return data;
},
// 注册
signUp: async (email: string, password: string, userData?: any) => {
const { data, error } = await supabase.auth.signUp({
email,
password,
options: {
data: userData,
},
});
if (error) throw error;
return data;
},
// 登出
signOut: async () => {
const { error } = await supabase.auth.signOut();
if (error) throw error;
},
// 重置密码
resetPassword: async (email: string) => {
const { data, error } = await supabase.auth.resetPasswordForEmail(email, {
redirectTo: `${window.location.origin}/auth/reset-password`,
});
if (error) throw error;
return data;
},
// 更新密码
updatePassword: async (password: string) => {
const { data, error } = await supabase.auth.updateUser({
password,
});
if (error) throw error;
return data;
},
// 获取当前会话
getSession: async () => {
const { data: { session }, error } = await supabase.auth.getSession();
if (error) throw error;
return session;
},
// 更新用户信息
updateUser: async (updates: any) => {
const { data, error } = await supabase.auth.updateUser(updates);
if (error) throw error;
return data;
},
};
// 数据库操作辅助函数
export const db = {
// 通用查询函数
select: async <T>(table: string, query?: any) => {
let queryBuilder = supabase.from(table).select(query || '*');
const { data, error } = await queryBuilder;
if (error) throw error;
return data as T[];
},
// 通用插入函数
insert: async <T>(table: string, data: any) => {
const { data: result, error } = await supabase
.from(table)
.insert(data)
.select()
.single();
if (error) throw error;
return result as T;
},
// 通用更新函数
update: async <T>(table: string, id: string, data: any) => {
const { data: result, error } = await supabase
.from(table)
.update(data)
.eq('id', id)
.select()
.single();
if (error) throw error;
return result as T;
},
// 通用删除函数
delete: async (table: string, id: string) => {
const { error } = await supabase
.from(table)
.delete()
.eq('id', id);
if (error) throw error;
},
// 根据条件查询单条记录
findOne: async <T>(table: string, conditions: Record<string, any>, select?: string) => {
let query = supabase.from(table).select(select || '*');
Object.entries(conditions).forEach(([key, value]) => {
query = query.eq(key, value);
});
const { data, error } = await query.single();
if (error) throw error;
return data as T;
},
// 根据条件查询多条记录
findMany: async <T>(table: string, conditions?: Record<string, any>, select?: string) => {
let query = supabase.from(table).select(select || '*');
if (conditions) {
Object.entries(conditions).forEach(([key, value]) => {
query = query.eq(key, value);
});
}
const { data, error } = await query;
if (error) throw error;
return data as T[];
},
// 计数查询
count: async (table: string, conditions?: Record<string, any>) => {
let query = supabase.from(table).select('*', { count: 'exact', head: true });
if (conditions) {
Object.entries(conditions).forEach(([key, value]) => {
query = query.eq(key, value);
});
}
const { count, error } = await query;
if (error) throw error;
return count || 0;
},
};
// 实时订阅管理
export const realtime = {
subscribe: (table: string, callback: (payload: any) => void) => {
const channel = supabase
.channel(`${table}_changes`)
.on('postgres_changes',
{
event: '*',
schema: 'public',
table: table
},
callback
)
.subscribe();
return channel;
},
unsubscribe: (channel: any) => {
if (channel) {
supabase.removeChannel(channel);
}
},
};
// 文件上传相关函数
export const storage = {
// 上传文件
upload: async (bucket: string, path: string, file: File) => {
const { data, error } = await supabase.storage
.from(bucket)
.upload(path, file);
if (error) throw error;
return data;
},
// 下载文件
download: async (bucket: string, path: string) => {
const { data, error } = await supabase.storage
.from(bucket)
.download(path);
if (error) throw error;
return data;
},
// 获取公共URL
getPublicUrl: (bucket: string, path: string) => {
const { data } = supabase.storage
.from(bucket)
.getPublicUrl(path);
return data.publicUrl;
},
// 删除文件
remove: async (bucket: string, paths: string[]) => {
const { data, error } = await supabase.storage
.from(bucket)
.remove(paths);
if (error) throw error;
return data;
},
};
// 用户类型定义
export type UserRole = 'admin' | 'interpreter' | 'client' | 'enterprise';
// 用户权限检查
export const permissions = {
// 检查用户是否有特定权限
hasPermission: (userRole: UserRole, requiredRole: UserRole) => {
const roleHierarchy: Record<UserRole, number> = {
'client': 1,
'interpreter': 2,
'enterprise': 3,
'admin': 4,
};
return roleHierarchy[userRole] >= roleHierarchy[requiredRole];
},
// 检查用户是否为管理员
isAdmin: (userRole: UserRole) => userRole === 'admin',
// 检查用户是否为翻译员
isInterpreter: (userRole: UserRole) => userRole === 'interpreter',
// 检查用户是否为企业用户
isEnterprise: (userRole: UserRole) => userRole === 'enterprise',
};
// 错误处理
export const handleSupabaseError = (error: any) => {
console.error('Supabase Error:', error);
// 根据错误类型返回用户友好的消息
if (error.code === 'PGRST116') {
return '未找到记录';
} else if (error.code === '23505') {
return '数据已存在';
} else if (error.code === '23503') {
return '数据关联错误';
} else if (error.message?.includes('JWT')) {
return '登录已过期,请重新登录';
} else if (error.message?.includes('permission')) {
return '权限不足';
} else {
return error.message || '操作失败,请稍后重试';
}
};
// 检查 Supabase 是否正确配置
export const isSupabaseConfigured = () => {
return !isDemoMode && supabaseUrl !== 'https://demo.supabase.co' && supabaseAnonKey !== 'demo-key';
};
// 获取配置状态
export const getConfigStatus = () => {
return {
isDemoMode,
isConfigured: isSupabaseConfigured(),
url: supabaseUrl,
hasAnonKey: supabaseAnonKey !== 'demo-key',
hasServiceKey: supabaseServiceKey !== 'demo-service-key',
};
};
// 默认导出
export default supabase;