Twilioapp-admin/lib/supabase.ts
mars f20988b90c feat: 完成所有页面的演示模式实现
- 更新 DashboardLayout 组件,统一使用演示模式布局
- 实现仪表盘页面的完整演示数据和功能
- 完成用户管理页面的演示模式,包含搜索、过滤、分页等功能
- 实现通话记录页面的演示数据和录音播放功能
- 完成翻译员管理页面的演示模式
- 实现订单管理页面的完整功能
- 完成发票管理页面的演示数据
- 更新文档管理页面
- 添加 utils.ts 工具函数库
- 完善 API 路由和数据库结构
- 修复各种 TypeScript 类型错误
- 统一界面风格和用户体验
2025-06-30 19:42:43 +08:00

360 lines
9.2 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 = supabaseUrl === 'https://demo.supabase.co';
// 单一的 Supabase 客户端实例
export const supabase = isDemoMode
? createClient(supabaseUrl, supabaseAnonKey, {
realtime: {
params: {
eventsPerSecond: 0,
},
},
auth: {
persistSession: false,
autoRefreshToken: false,
},
})
: createClient<Database>(supabaseUrl, supabaseAnonKey, {
auth: {
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: true
}
});
// 服务端使用的 Supabase 客户端(具有管理员权限)
export const supabaseAdmin = isDemoMode
? createClient(supabaseUrl, supabaseServiceKey, {
auth: {
autoRefreshToken: false,
persistSession: false,
},
realtime: {
params: {
eventsPerSecond: 0,
},
},
})
: createClient(supabaseUrl, supabaseServiceKey, {
auth: {
autoRefreshToken: false,
persistSession: false,
},
});
// 数据库表名常量
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;