- fix(DataAnalysisPanel): 活跃用户卡片固定 400px + overflow-y 独立滚动容器,底部渐变遮罩提示滚动;列表项 tooltip 增加 max-height 与 overflow 截断;新增圆形头像显示与 @error 回退首字母 - feat(admin): 新增 getUserAvatarUrl 统一头像策略链(OAuth identity→Gravatar→DiceBear 7.x initials),stats/realtime、active-users、users/index 三处接口返回 avatarUrl - fix(admin): 建立 SUPER_ADMIN(100)>ADMIN(80)>USER(10) 分级常量与 canChangeRoleOf/canResetPasswordFor 等谓词;reset-password、[id].put、index.post 全面应用级别守卫;前端 UserManager 按钮按权限禁用、availableRoles 按操作者级别过滤 SUPER_ADMIN/ADMIN 等高角色
198 lines
5.2 KiB
JavaScript
198 lines
5.2 KiB
JavaScript
import fs from 'fs'
|
|
import path from 'path'
|
|
|
|
// 获取角色配置文件路径
|
|
const getRolesFilePath = () => {
|
|
return path.resolve(process.cwd(), 'server', 'config', 'roles.json')
|
|
}
|
|
|
|
// 获取当前角色配置
|
|
export let ROLES = {}
|
|
try {
|
|
const filePath = getRolesFilePath()
|
|
if (fs.existsSync(filePath)) {
|
|
const data = fs.readFileSync(filePath, 'utf-8')
|
|
ROLES = JSON.parse(data)
|
|
}
|
|
} catch (err) {
|
|
console.error('Failed to load roles.json:', err)
|
|
}
|
|
|
|
// 确保角色对象存在
|
|
if (!ROLES.USER) {
|
|
ROLES = {
|
|
USER: { displayName: '学生(普通用户)', pages: [], canAccessAdmin: false },
|
|
SONG_ADMIN: { displayName: '工作人员', pages: ['schedule', 'songs', 'print'], canAccessAdmin: true },
|
|
VICE_MINISTER: { displayName: '副部', pages: ['overview', 'schedule', 'songs', 'print', 'users'], canAccessAdmin: true },
|
|
ADMIN: { displayName: '部长', pages: ['overview', 'schedule', 'songs', 'print', 'users', 'data-analysis', 'notifications', 'playtimes', 'semesters'], canAccessAdmin: true },
|
|
RESIDENT: { displayName: '常驻', pages: ['schedule', 'songs', 'print'], canAccessAdmin: true },
|
|
SUPER_ADMIN: { displayName: '技术运维', pages: ['overview', 'workflow-tutorial', 'schedule', 'songs', 'print', 'users', 'roles', 'blacklist', 'api-keys', 'notifications', 'smtp-config', 'playtimes', 'request-times', 'semesters', 'settings', 'site-config', 'database', 'data-analysis'], canAccessAdmin: true }
|
|
}
|
|
}
|
|
|
|
// 保存角色配置
|
|
export function saveRolesConfig(newRoles) {
|
|
try {
|
|
ROLES = newRoles
|
|
const filePath = getRolesFilePath()
|
|
const dir = path.dirname(filePath)
|
|
if (!fs.existsSync(dir)) {
|
|
fs.mkdirSync(dir, { recursive: true })
|
|
}
|
|
fs.writeFileSync(filePath, JSON.stringify(ROLES, null, 2), 'utf-8')
|
|
return true
|
|
} catch (err) {
|
|
console.error('Failed to save roles.json:', err)
|
|
return false
|
|
}
|
|
}
|
|
|
|
// 检查用户是否可以访问后台
|
|
function canAccessAdmin(user) {
|
|
if (!user || !user.role) return false
|
|
|
|
const roleConfig = ROLES[user.role]
|
|
if (!roleConfig) return false
|
|
|
|
return roleConfig.canAccessAdmin
|
|
}
|
|
|
|
// 检查用户是否可以访问指定页面
|
|
function canAccessPage(user, page) {
|
|
if (!user || !user.role) return false
|
|
|
|
// 超级管理员可以访问所有页面
|
|
if (user.role === 'SUPER_ADMIN') return true
|
|
|
|
const roleConfig = ROLES[user.role]
|
|
if (!roleConfig) return false
|
|
|
|
return roleConfig.pages.includes(page)
|
|
}
|
|
|
|
// 获取用户可访问的页面列表
|
|
function getUserPages(user) {
|
|
if (!user || !user.role) return []
|
|
|
|
const roleConfig = ROLES[user.role]
|
|
if (!roleConfig) return []
|
|
|
|
return roleConfig.pages
|
|
}
|
|
|
|
// 获取角色信息
|
|
function getRoleInfo(roleName) {
|
|
return ROLES[roleName] || null
|
|
}
|
|
|
|
const ROLE_LEVELS = {
|
|
SUPER_ADMIN: 100,
|
|
ADMIN: 80,
|
|
MINISTER: 80,
|
|
VICE_MINISTER: 70,
|
|
SONG_ADMIN: 60,
|
|
STAFF: 60,
|
|
RESIDENT: 50,
|
|
TEACHER: 40,
|
|
USER: 10
|
|
}
|
|
|
|
export function getRoleLevel(roleName) {
|
|
if (!roleName) return 0
|
|
const upperRole = String(roleName).toUpperCase()
|
|
return ROLE_LEVELS[upperRole] ?? 0
|
|
}
|
|
|
|
export function isRoleHigherOrEqual(currentRole, targetRole) {
|
|
return getRoleLevel(currentRole) >= getRoleLevel(targetRole)
|
|
}
|
|
|
|
export function isRoleHigher(currentRole, targetRole) {
|
|
return getRoleLevel(currentRole) > getRoleLevel(targetRole)
|
|
}
|
|
|
|
function canResetPasswordFor(currentUser, targetUser) {
|
|
if (!currentUser || !currentUser.role || !targetUser) return false
|
|
|
|
const currentLevel = getRoleLevel(currentUser.role)
|
|
const targetLevel = getRoleLevel(targetUser.role)
|
|
|
|
if (currentUser.role === 'SUPER_ADMIN') {
|
|
return true
|
|
}
|
|
|
|
if (currentUser.role === 'ADMIN' || currentUser.role === 'MINISTER') {
|
|
return targetLevel <= getRoleLevel('USER')
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
function canChangeRoleOf(currentUser, targetUser, newRole) {
|
|
if (!currentUser || !currentUser.role || !targetUser) return false
|
|
|
|
const currentLevel = getRoleLevel(currentUser.role)
|
|
const targetLevel = getRoleLevel(targetUser.role)
|
|
const newRoleLevel = getRoleLevel(newRole)
|
|
|
|
if (String(currentUser.id) === String(targetUser.id)) {
|
|
return false
|
|
}
|
|
|
|
if (currentUser.role === 'SUPER_ADMIN') {
|
|
if (targetUser.id === 1) return false
|
|
return newRoleLevel <= currentLevel
|
|
}
|
|
|
|
if (currentLevel < targetLevel) {
|
|
return false
|
|
}
|
|
|
|
if (newRoleLevel >= currentLevel) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
function canDeleteUser(currentUser, targetUser) {
|
|
if (!currentUser || !currentUser.role || !targetUser) return false
|
|
|
|
if (String(currentUser.id) === String(targetUser.id)) {
|
|
return false
|
|
}
|
|
|
|
if (targetUser.id === 1) {
|
|
return false
|
|
}
|
|
|
|
const currentLevel = getRoleLevel(currentUser.role)
|
|
const targetLevel = getRoleLevel(targetUser.role)
|
|
|
|
return currentLevel > targetLevel
|
|
}
|
|
|
|
function canManageUserRole(currentUser, targetRole) {
|
|
if (!currentUser || !currentUser.role) return false
|
|
|
|
if (currentUser.role === 'SUPER_ADMIN') {
|
|
return Object.keys(ROLES).includes(targetRole) && targetRole !== 'SUPER_ADMIN'
|
|
}
|
|
|
|
const currentLevel = getRoleLevel(currentUser.role)
|
|
const targetLevel = getRoleLevel(targetRole)
|
|
return targetLevel < currentLevel
|
|
}
|
|
|
|
// 导出所有函数和常量
|
|
export {
|
|
canAccessAdmin,
|
|
canAccessPage,
|
|
getUserPages,
|
|
getRoleInfo,
|
|
canManageUserRole,
|
|
canResetPasswordFor,
|
|
canChangeRoleOf,
|
|
canDeleteUser
|
|
}
|