ghp / packages /server /src /middleware /withControl.ts
QSLY's picture
deploy: build Hugging Face Space from source
00a912e
Raw
History Blame Contribute Delete
2.02 kB
import { EVENTS, ERROR_CODE, defineAbilityFor } from '@music-together/shared'
import type { Actions, Subjects, UserRole } from '@music-together/shared'
import type { HandlerContext, TypedServer } from './types.js'
import { createWithRoom } from './withRoom.js'
import { userRepo } from '../repositories/userRepository.js'
/** Server administrators have full room permissions regardless of room role. */
export function defineAbilityForRoomUser(userId: string, roomRole: UserRole) {
return defineAbilityFor(userRepo.isServerAdmin(userId) ? 'owner' : roomRole)
}
/**
* Socket 中间件:基于 CASL 权限检查。
* 根据用户 role 生成 ability,检查 (action, subject) 是否允许。
*/
export function createWithPermission(io: TypedServer) {
const withRoom = createWithRoom(io)
return function withPermission<T = void>(
action: Actions,
subject: Subjects,
handler: (ctx: HandlerContext, data: T) => void | Promise<void>,
) {
return withRoom<T>((ctx, data) => {
const ability = defineAbilityForRoomUser(ctx.user.id, ctx.user.role)
if (!ability.can(action, subject)) {
ctx.socket.emit(EVENTS.ROOM_ERROR, {
code: ERROR_CODE.NO_PERMISSION,
message: '你没有权限执行此操作',
})
return
}
return handler(ctx, data)
})
}
}
/**
* Socket 中间件:仅 Owner(房间创建者)可执行。
* 用于房间设置、角色管理等只有房主才能操作的场景。
*/
export function createWithOwnerOnly(io: TypedServer) {
const withRoom = createWithRoom(io)
return function withOwnerOnly<T = void>(handler: (ctx: HandlerContext, data: T) => void | Promise<void>) {
return withRoom<T>((ctx, data) => {
if (ctx.user.role !== 'owner' && !userRepo.isServerAdmin(ctx.user.id)) {
ctx.socket.emit(EVENTS.ROOM_ERROR, {
code: ERROR_CODE.NO_PERMISSION,
message: '只有房主可以操作',
})
return
}
return handler(ctx, data)
})
}
}