diff --git a/backend/routes/users.js b/backend/routes/users.js index 0ce52ea..d3ee410 100644 --- a/backend/routes/users.js +++ b/backend/routes/users.js @@ -24,6 +24,7 @@ router.post('/', authenticate, requireRole('owner','admin'), async (req, res) => const { username, password, email, game_name, game_uid, role, source } = req.body; if (!username||!password||!email||!game_name||!game_uid||!role) return res.status(400).json({ error: '所有字段必填' }); if (!['owner','admin','player'].includes(role)) return res.status(400).json({ error: '无效角色' }); + if (role === 'owner' && req.user.role !== 'owner') return res.status(403).json({ error: '仅服主可创建服主账号' }); if (password.length < 6) return res.status(400).json({ error: '密码至少6位' }); if (await getRow('SELECT id FROM users WHERE username = ?', [username])) return res.status(400).json({ error: '用户名已存在' }); const hashed = bcrypt.hashSync(password, 10); diff --git a/public/js/app.js b/public/js/app.js index 1962fa0..33a8951 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -1,17 +1,17 @@ const App = { navItems: [ { id: 'dashboard', label: '控制台', icon: 'fa-chart-simple', roles: ['owner','admin','player'] }, - { id: 'unclaimed', label: '待处理工单', icon: 'fa-inbox', roles: ['owner','admin'] }, + { id: 'tickets-create', label: '提交工单', icon: 'fa-plus-circle', roles: ['player'] }, { id: 'tickets', label: '工单列表', icon: 'fa-ticket-alt', roles: ['owner','admin','player'] }, - { id: 'tracking', label: '追踪工单', icon: 'fa-search', roles: ['owner','admin','player'] }, - { id: 'users', label: '用户管理', icon: 'fa-users', roles: ['owner','admin'] }, - { id: 'notifications', label: '通知配置', icon: 'fa-bell', roles: ['owner','admin'] }, - { id: 'templates', label: '邮件模板', icon: 'fa-envelope', roles: ['owner','admin'] }, - { id: 'settings', label: '系统设置', icon: 'fa-cog', roles: ['owner','admin'] }, + { id: 'unclaimed', label: '待处理工单', icon: 'fa-inbox', roles: ['owner','admin'] }, + { id: 'users', label: '用户管理', icon: 'fa-users', roles: ['owner'] }, + { id: 'servers', label: '服务器管理', icon: 'fa-server', roles: ['owner'] }, + { id: 'polls', label: '投票', icon: 'fa-poll', roles: ['owner'] }, + { id: 'features', label: '更新内容', icon: 'fa-lightbulb', roles: ['owner'] }, + { id: 'notifications', label: '通知配置', icon: 'fa-bell', roles: ['owner'] }, + { id: 'templates', label: '邮件模板', icon: 'fa-envelope', roles: ['owner'] }, + { id: 'settings', label: '系统设置', icon: 'fa-cog', roles: ['owner'] }, { id: 'export', label: '数据导出', icon: 'fa-download', roles: ['owner'] }, - { id: 'polls', label: '投票', icon: 'fa-poll', roles: ['owner','admin','player'] }, - { id: 'features', label: '更新内容', icon: 'fa-lightbulb', roles: ['owner','admin','player'] }, - { id: 'servers', label: '服务器管理', icon: 'fa-server', roles: ['owner','admin'] }, ], async init() { @@ -81,6 +81,7 @@ const App = { switch (page) { case 'dashboard': this.renderMain('控制台', Dashboard, param); break; case 'unclaimed': this.renderMain('待处理工单', UnclaimedPage, param); break; + case 'tickets-create': App.navigate('tickets','create?type=report'); break; case 'tickets': if (param === 'create') { const q = new URLSearchParams((location.hash.split('?')[1] || '')); @@ -120,10 +121,9 @@ const App = { if (Auth.logged()) { const u = Auth.user(); el.innerHTML = `${U.esc(u.game_name||u.username)} - 控制台 - 投票 - 更新 - ${Auth.isAdmin() ? `待处理`:''} + ${Auth.isAdmin() ? ` 控制台` : ''} + ${Auth.isAdmin() ? `待处理` : ''} + ${Auth.isOwner() ? `` : ''} `; } else { el.innerHTML = ` 登录 注册`; @@ -136,7 +136,7 @@ const App = { document.getElementById('public-page').classList.add('hidden'); document.getElementById('main-layout').classList.remove('hidden'); const u = Auth.user(); - document.getElementById('user-info').innerHTML = `