Initial commit of akmon project
This commit is contained in:
609
supabase_message_client_complete.js
Normal file
609
supabase_message_client_complete.js
Normal file
@@ -0,0 +1,609 @@
|
||||
/**
|
||||
* Supabase 消息系统客户端 - 完整角色管理版本
|
||||
* 包含角色管理、权限检查、消息操作等功能
|
||||
*/
|
||||
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
// Supabase 配置
|
||||
const supabaseUrl = 'YOUR_SUPABASE_URL'
|
||||
const supabaseKey = 'YOUR_SUPABASE_ANON_KEY'
|
||||
const supabase = createClient(supabaseUrl, supabaseKey)
|
||||
|
||||
/**
|
||||
* 用户角色管理类
|
||||
*/
|
||||
class UserRoleManager {
|
||||
constructor(supabaseClient) {
|
||||
this.supabase = supabaseClient
|
||||
this.currentUser = null
|
||||
this.currentRole = null
|
||||
this.permissions = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化用户角色信息
|
||||
*/
|
||||
async initialize() {
|
||||
try {
|
||||
const { data: { user } } = await this.supabase.auth.getUser()
|
||||
|
||||
if (user) {
|
||||
this.currentUser = user
|
||||
this.currentRole = await this.getUserRole(user.id)
|
||||
this.permissions = await this.getUserPermissions(user.id)
|
||||
|
||||
console.log('✅ 用户角色初始化完成:', {
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
role: this.currentRole,
|
||||
permissions: this.permissions
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
user: this.currentUser,
|
||||
role: this.currentRole,
|
||||
permissions: this.permissions
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ 用户角色初始化失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色
|
||||
* @param {string} userId - 用户ID
|
||||
* @returns {Promise<string>} 用户角色
|
||||
*/
|
||||
async getUserRole(userId = null) {
|
||||
try {
|
||||
const { data, error } = await this.supabase
|
||||
.rpc('get_user_role', { target_user_id: userId })
|
||||
|
||||
if (error) throw error
|
||||
|
||||
return data || 'student'
|
||||
} catch (error) {
|
||||
console.error('获取用户角色失败:', error)
|
||||
return 'student'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户权限
|
||||
* @param {string} userId - 用户ID
|
||||
* @returns {Promise<Object>} 用户权限对象
|
||||
*/
|
||||
async getUserPermissions(userId = null) {
|
||||
try {
|
||||
const { data, error } = await this.supabase
|
||||
.from('user_roles')
|
||||
.select('permissions')
|
||||
.eq('user_id', userId || this.currentUser?.id)
|
||||
.eq('is_active', true)
|
||||
.single()
|
||||
|
||||
if (error && error.code !== 'PGRST116') throw error
|
||||
|
||||
return data?.permissions || {}
|
||||
} catch (error) {
|
||||
console.error('获取用户权限失败:', error)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否有特定权限
|
||||
* @param {string} permission - 权限名称
|
||||
* @param {string} userId - 用户ID(可选)
|
||||
* @returns {Promise<boolean>} 是否有权限
|
||||
*/
|
||||
async hasPermission(permission, userId = null) {
|
||||
try {
|
||||
const { data, error } = await this.supabase
|
||||
.rpc('user_has_permission', {
|
||||
permission_name: permission,
|
||||
target_user_id: userId
|
||||
})
|
||||
|
||||
if (error) throw error
|
||||
|
||||
return data === true
|
||||
} catch (error) {
|
||||
console.error('权限检查失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否可以访问资源
|
||||
* @param {string} resourceType - 资源类型
|
||||
* @param {string} resourceId - 资源ID
|
||||
* @param {string} accessType - 访问类型
|
||||
* @returns {Promise<boolean>} 是否可以访问
|
||||
*/
|
||||
async canAccessResource(resourceType, resourceId, accessType = 'read') {
|
||||
try {
|
||||
const { data, error } = await this.supabase
|
||||
.rpc('can_access_resource', {
|
||||
resource_type: resourceType,
|
||||
resource_id: resourceId,
|
||||
access_type: accessType
|
||||
})
|
||||
|
||||
if (error) throw error
|
||||
|
||||
return data === true
|
||||
} catch (error) {
|
||||
console.error('资源访问检查失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户角色(仅管理员)
|
||||
* @param {string} targetUserId - 目标用户ID
|
||||
* @param {string} newRole - 新角色
|
||||
* @param {Object} additionalData - 额外数据
|
||||
* @returns {Promise<boolean>} 是否更新成功
|
||||
*/
|
||||
async updateUserRole(targetUserId, newRole, additionalData = {}) {
|
||||
try {
|
||||
const { data, error } = await this.supabase
|
||||
.rpc('update_user_role', {
|
||||
target_user_id: targetUserId,
|
||||
new_role: newRole,
|
||||
additional_data: additionalData
|
||||
})
|
||||
|
||||
if (error) throw error
|
||||
|
||||
console.log('✅ 用户角色更新成功:', { targetUserId, newRole })
|
||||
return data === true
|
||||
} catch (error) {
|
||||
console.error('❌ 用户角色更新失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新用户角色
|
||||
* @param {Array} roleUpdates - 角色更新数组
|
||||
* @returns {Promise<Array>} 更新结果
|
||||
*/
|
||||
async batchUpdateUserRoles(roleUpdates) {
|
||||
try {
|
||||
const { data, error } = await this.supabase
|
||||
.rpc('batch_update_user_roles', {
|
||||
role_updates: roleUpdates
|
||||
})
|
||||
|
||||
if (error) throw error
|
||||
|
||||
console.log('✅ 批量角色更新完成:', data)
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('❌ 批量角色更新失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户详细信息(管理员视图)
|
||||
*/
|
||||
async getUsersWithRoles() {
|
||||
try {
|
||||
const { data, error } = await this.supabase
|
||||
.from('user_roles_detailed')
|
||||
.select('*')
|
||||
.order('created_at', { ascending: false })
|
||||
|
||||
if (error) throw error
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('获取用户角色列表失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试权限系统
|
||||
* @param {string} testUserId - 测试用户ID
|
||||
*/
|
||||
async testPermissions(testUserId = null) {
|
||||
try {
|
||||
const { data, error } = await this.supabase
|
||||
.rpc('test_message_permissions', {
|
||||
test_user_id: testUserId || this.currentUser?.id
|
||||
})
|
||||
|
||||
if (error) throw error
|
||||
|
||||
console.log('🧪 权限测试结果:', data)
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('权限测试失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息系统管理类
|
||||
*/
|
||||
class MessageManager {
|
||||
constructor(supabaseClient, roleManager) {
|
||||
this.supabase = supabaseClient
|
||||
this.roleManager = roleManager
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送安全消息
|
||||
* @param {Object} messageData - 消息数据
|
||||
* @returns {Promise<string>} 消息ID
|
||||
*/
|
||||
async sendSecureMessage(messageData) {
|
||||
try {
|
||||
const {
|
||||
messageTypeId,
|
||||
receiverType,
|
||||
receiverId,
|
||||
title,
|
||||
content,
|
||||
metadata = {}
|
||||
} = messageData
|
||||
|
||||
const { data, error } = await this.supabase
|
||||
.rpc('send_secure_message', {
|
||||
message_type_id: messageTypeId,
|
||||
receiver_type: receiverType,
|
||||
receiver_id: receiverId,
|
||||
title,
|
||||
content,
|
||||
metadata_json: metadata
|
||||
})
|
||||
|
||||
if (error) throw error
|
||||
|
||||
console.log('✅ 消息发送成功:', data)
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('❌ 消息发送失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户可访问的消息列表
|
||||
* @param {Object} options - 查询选项
|
||||
* @returns {Promise<Array>} 消息列表
|
||||
*/
|
||||
async getAccessibleMessages(options = {}) {
|
||||
try {
|
||||
const {
|
||||
limit = 50,
|
||||
offset = 0,
|
||||
messageType = null,
|
||||
unreadOnly = false
|
||||
} = options
|
||||
|
||||
let query = this.supabase
|
||||
.from('ak_messages')
|
||||
.select(`
|
||||
*,
|
||||
ak_message_types(type_name, display_name),
|
||||
ak_message_recipients(
|
||||
read_at,
|
||||
replied_at,
|
||||
status
|
||||
)
|
||||
`)
|
||||
.order('created_at', { ascending: false })
|
||||
.range(offset, offset + limit - 1)
|
||||
|
||||
if (messageType) {
|
||||
query = query.eq('ak_message_types.type_name', messageType)
|
||||
}
|
||||
|
||||
if (unreadOnly) {
|
||||
query = query.is('ak_message_recipients.read_at', null)
|
||||
}
|
||||
|
||||
const { data, error } = await query
|
||||
|
||||
if (error) throw error
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('获取消息列表失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记消息为已读
|
||||
* @param {string} messageId - 消息ID
|
||||
*/
|
||||
async markMessageAsRead(messageId) {
|
||||
try {
|
||||
const { error } = await this.supabase
|
||||
.from('ak_message_recipients')
|
||||
.update({
|
||||
read_at: new Date().toISOString(),
|
||||
status: 'read'
|
||||
})
|
||||
.eq('message_id', messageId)
|
||||
.eq('user_id', this.roleManager.currentUser?.id)
|
||||
|
||||
if (error) throw error
|
||||
|
||||
console.log('✅ 消息已标记为已读:', messageId)
|
||||
} catch (error) {
|
||||
console.error('标记消息已读失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息统计
|
||||
*/
|
||||
async getMessageStats() {
|
||||
try {
|
||||
const { data, error } = await this.supabase
|
||||
.from('ak_message_stats')
|
||||
.select('*')
|
||||
.eq('entity_type', 'user')
|
||||
.eq('entity_id', this.roleManager.currentUser?.id)
|
||||
|
||||
if (error) throw error
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('获取消息统计失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入消息群组
|
||||
* @param {string} groupId - 群组ID
|
||||
* @param {string} joinMessage - 加入消息
|
||||
*/
|
||||
async joinMessageGroup(groupId, joinMessage = '') {
|
||||
try {
|
||||
const { data, error } = await this.supabase
|
||||
.rpc('join_message_group', {
|
||||
target_group_id: groupId,
|
||||
join_message: joinMessage
|
||||
})
|
||||
|
||||
if (error) throw error
|
||||
|
||||
if (data === true) {
|
||||
console.log('✅ 成功加入群组:', groupId)
|
||||
} else {
|
||||
console.log('⏳ 申请已提交,等待审批:', groupId)
|
||||
}
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('加入群组失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户可访问的群组列表
|
||||
*/
|
||||
async getAccessibleGroups() {
|
||||
try {
|
||||
const { data, error } = await this.supabase
|
||||
.from('ak_message_groups')
|
||||
.select(`
|
||||
*,
|
||||
ak_message_group_members!inner(
|
||||
status,
|
||||
joined_at,
|
||||
role
|
||||
)
|
||||
`)
|
||||
.eq('ak_message_group_members.user_id', this.roleManager.currentUser?.id)
|
||||
.eq('ak_message_group_members.status', 'active')
|
||||
.order('created_at', { ascending: false })
|
||||
|
||||
if (error) throw error
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('获取群组列表失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主应用类
|
||||
*/
|
||||
class MessageApp {
|
||||
constructor() {
|
||||
this.supabase = supabase
|
||||
this.roleManager = new UserRoleManager(this.supabase)
|
||||
this.messageManager = new MessageManager(this.supabase, this.roleManager)
|
||||
this.initialized = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化应用
|
||||
*/
|
||||
async initialize() {
|
||||
try {
|
||||
await this.roleManager.initialize()
|
||||
this.initialized = true
|
||||
|
||||
// 监听认证状态变化
|
||||
this.supabase.auth.onAuthStateChange(async (event, session) => {
|
||||
console.log('🔄 认证状态变化:', event)
|
||||
|
||||
if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED') {
|
||||
await this.roleManager.initialize()
|
||||
} else if (event === 'SIGNED_OUT') {
|
||||
this.roleManager.currentUser = null
|
||||
this.roleManager.currentRole = null
|
||||
this.roleManager.permissions = {}
|
||||
}
|
||||
})
|
||||
|
||||
console.log('✅ 消息应用初始化完成')
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('❌ 消息应用初始化失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @param {string} email - 邮箱
|
||||
* @param {string} password - 密码
|
||||
*/
|
||||
async signIn(email, password) {
|
||||
try {
|
||||
const { data, error } = await this.supabase.auth.signInWithPassword({
|
||||
email,
|
||||
password
|
||||
})
|
||||
|
||||
if (error) throw error
|
||||
|
||||
await this.roleManager.initialize()
|
||||
console.log('✅ 登录成功')
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('❌ 登录失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
* @param {string} email - 邮箱
|
||||
* @param {string} password - 密码
|
||||
* @param {Object} metadata - 额外元数据
|
||||
*/
|
||||
async signUp(email, password, metadata = {}) {
|
||||
try {
|
||||
const { data, error } = await this.supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: {
|
||||
data: metadata
|
||||
}
|
||||
})
|
||||
|
||||
if (error) throw error
|
||||
|
||||
console.log('✅ 注册成功,请检查邮箱验证')
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('❌ 注册失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户退出
|
||||
*/
|
||||
async signOut() {
|
||||
try {
|
||||
const { error } = await this.supabase.auth.signOut()
|
||||
if (error) throw error
|
||||
|
||||
console.log('✅ 退出成功')
|
||||
} catch (error) {
|
||||
console.error('❌ 退出失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
getCurrentUserInfo() {
|
||||
return {
|
||||
user: this.roleManager.currentUser,
|
||||
role: this.roleManager.currentRole,
|
||||
permissions: this.roleManager.permissions,
|
||||
initialized: this.initialized
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色管理器
|
||||
*/
|
||||
get roles() {
|
||||
return this.roleManager
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息管理器
|
||||
*/
|
||||
get messages() {
|
||||
return this.messageManager
|
||||
}
|
||||
}
|
||||
|
||||
// 创建全局实例
|
||||
const messageApp = new MessageApp()
|
||||
|
||||
// 导出
|
||||
export { MessageApp, UserRoleManager, MessageManager, messageApp }
|
||||
|
||||
// 使用示例
|
||||
/*
|
||||
// 1. 初始化应用
|
||||
await messageApp.initialize()
|
||||
|
||||
// 2. 用户登录
|
||||
await messageApp.signIn('teacher@example.com', 'password123')
|
||||
|
||||
// 3. 检查用户角色和权限
|
||||
const userInfo = messageApp.getCurrentUserInfo()
|
||||
console.log('当前用户:', userInfo)
|
||||
|
||||
// 4. 检查特定权限
|
||||
const canSendBroadcast = await messageApp.roles.hasPermission('can_send_broadcasts')
|
||||
console.log('可以发送广播:', canSendBroadcast)
|
||||
|
||||
// 5. 发送消息
|
||||
const messageId = await messageApp.messages.sendSecureMessage({
|
||||
messageTypeId: 'some-type-id',
|
||||
receiverType: 'user',
|
||||
receiverId: 'student-user-id',
|
||||
title: '作业通知',
|
||||
content: '请完成本周的作业',
|
||||
metadata: { priority: 'high' }
|
||||
})
|
||||
|
||||
// 6. 获取消息列表
|
||||
const messages = await messageApp.messages.getAccessibleMessages({
|
||||
limit: 20,
|
||||
unreadOnly: true
|
||||
})
|
||||
|
||||
// 7. 加入群组
|
||||
const joinResult = await messageApp.messages.joinMessageGroup('group-id')
|
||||
|
||||
// 8. 管理员操作:更新用户角色
|
||||
if (userInfo.role === 'admin') {
|
||||
await messageApp.roles.updateUserRole('user-id', 'teacher', {
|
||||
department: 'Mathematics',
|
||||
class_id: 'class-123'
|
||||
})
|
||||
}
|
||||
|
||||
// 9. 测试权限系统
|
||||
await messageApp.roles.testPermissions()
|
||||
*/
|
||||
Reference in New Issue
Block a user