Twilioapp-admin/lib/supabase.ts

290 lines
7.0 KiB
TypeScript

import { createClient } from '@supabase/supabase-js';
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
// 环境变量检查和默认值
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(supabaseUrl, supabaseAnonKey);
// 组件中使用的 Supabase 客户端
export const createSupabaseClient = () => {
if (isDemoMode) {
// 在演示模式下返回一个模拟客户端
return {
auth: {
getUser: () => Promise.resolve({ data: { user: null }, error: null }),
signInWithPassword: () => Promise.resolve({ data: null, error: { message: '演示模式:请配置 Supabase 环境变量' } }),
signOut: () => Promise.resolve({ error: null }),
onAuthStateChange: () => ({ data: { subscription: { unsubscribe: () => {} } } }),
}
} as any;
}
return createClientComponentClient();
};
// 服务端使用的 Supabase 客户端(具有管理员权限)
export const supabaseAdmin = isDemoMode
? createClient(supabaseUrl, supabaseServiceKey, {
auth: {
autoRefreshToken: false,
persistSession: false,
},
realtime: {
params: {
eventsPerSecond: 0,
},
},
})
: createClient(supabaseUrl, supabaseServiceKey, {
auth: {
autoRefreshToken: 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, metadata?: any) => {
const { data, error } = await supabase.auth.signUp({
email,
password,
options: {
data: metadata,
},
});
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;
},
};
// 数据库操作辅助函数
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;
},
// 分页查询函数
paginate: async <T>(
table: string,
page: number = 1,
limit: number = 10,
query?: any,
orderBy?: { column: string; ascending?: boolean }
) => {
const from = (page - 1) * limit;
const to = from + limit - 1;
let queryBuilder = supabase
.from(table)
.select(query || '*', { count: 'exact' })
.range(from, to);
if (orderBy) {
queryBuilder = queryBuilder.order(orderBy.column, {
ascending: orderBy.ascending ?? true,
});
}
const { data, error, count } = await queryBuilder;
if (error) throw error;
return {
data: data as T[],
total: count || 0,
page,
limit,
has_more: (count || 0) > page * limit,
};
},
};
// 文件上传函数
export const storage = {
// 上传文件
upload: async (bucket: string, path: string, file: File) => {
const { data, error } = await supabase.storage
.from(bucket)
.upload(path, file, {
cacheControl: '3600',
upsert: false,
});
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 const realtime = {
// 订阅表变化
subscribe: (
table: string,
callback: (payload: any) => void,
filter?: string
) => {
if (isDemoMode) {
// 演示模式下返回模拟的订阅对象
return {
unsubscribe: () => {},
};
}
const channel = supabase
.channel(`${table}-changes`)
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table,
filter,
},
callback
)
.subscribe();
return channel;
},
// 取消订阅
unsubscribe: (channel: any) => {
if (isDemoMode) {
return;
}
supabase.removeChannel(channel);
},
};