feat: add monitor-ai permissions to user management page

This commit is contained in:
aiyimickey 2026-07-07 18:38:58 +08:00
parent b851fbbda8
commit 727269c877
2 changed files with 19 additions and 8 deletions

View File

@ -33,8 +33,10 @@ export default function AdminUsersPage() {
const [email, setEmail] = useState('') const [email, setEmail] = useState('')
const [assetsRole, setAssetsRole] = useState('viewer') const [assetsRole, setAssetsRole] = useState('viewer')
const [issueRole, setIssueRole] = useState('viewer') const [issueRole, setIssueRole] = useState('viewer')
const [monitorRole, setMonitorRole] = useState('viewer')
const [assetsRoles, setAssetsRoles] = useState<Role[]>([]) const [assetsRoles, setAssetsRoles] = useState<Role[]>([])
const [issueRoles, setIssueRoles] = useState<Role[]>([]) const [issueRoles, setIssueRoles] = useState<Role[]>([])
const [monitorRoles, setMonitorRoles] = useState<Role[]>([])
const [users, setUsers] = useState<LdapUser[]>([]) const [users, setUsers] = useState<LdapUser[]>([])
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [result, setResult] = useState<{ ok: boolean; msg: string } | null>(null) const [result, setResult] = useState<{ ok: boolean; msg: string } | null>(null)
@ -57,6 +59,7 @@ export default function AdminUsersPage() {
]) ])
if (rolesR.assets?.length) setAssetsRoles(rolesR.assets) if (rolesR.assets?.length) setAssetsRoles(rolesR.assets)
if (rolesR.issue?.length) setIssueRoles(rolesR.issue) if (rolesR.issue?.length) setIssueRoles(rolesR.issue)
if (rolesR.monitor?.length) setMonitorRoles(rolesR.monitor)
if (usersR.users?.length) setUsers(usersR.users) if (usersR.users?.length) setUsers(usersR.users)
} catch {} } catch {}
}, []) }, [])
@ -81,7 +84,7 @@ export default function AdminUsersPage() {
e.preventDefault() e.preventDefault()
setLoading(true) setLoading(true)
try { try {
const res = await fetch('/api/admin/create-user', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, displayName, assetsRole, issueRole, email: email || undefined }) }) const res = await fetch('/api/admin/create-user', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, displayName, assetsRole, issueRole, monitorRole, email: email || undefined }) })
const d = await res.json() const d = await res.json()
if (res.ok) { if (res.ok) {
if (d.password) { setGeneratedPwd(d.password); setPwdUser(username); setPwdName(displayName || username); setShowPwd(true); setCopied(false) } if (d.password) { setGeneratedPwd(d.password); setPwdUser(username); setPwdName(displayName || username); setShowPwd(true); setCopied(false) }
@ -206,6 +209,12 @@ export default function AdminUsersPage() {
{issueRoles.map(r => <option key={r.name} value={r.name}>{r.display_name}{r.name}</option>)} {issueRoles.map(r => <option key={r.name} value={r.name}>{r.display_name}{r.name}</option>)}
</select> </select>
</div> </div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<label style={s.label}></label>
<select value={monitorRole} onChange={e => setMonitorRole(e.target.value)} style={s.select}>
{monitorRoles.map(r => <option key={r.name} value={r.name}>{r.display_name}{r.name}</option>)}
</select>
</div>
</div> </div>
</div> </div>
<button type="submit" disabled={loading} style={{ ...s.btn, background: loading ? '#93c5fd' : '#2563eb', cursor: loading ? 'not-allowed' : 'pointer' }}> <button type="submit" disabled={loading} style={{ ...s.btn, background: loading ? '#93c5fd' : '#2563eb', cursor: loading ? 'not-allowed' : 'pointer' }}>

View File

@ -4,8 +4,8 @@ import { useState, useEffect, useCallback } from 'react'
interface SiteUser { username: string; display_name: string; role: string } interface SiteUser { username: string; display_name: string; role: string }
interface RoleData { interface RoleData {
assetsUsers: SiteUser[]; issueUsers: SiteUser[] assetsUsers: SiteUser[]; issueUsers: SiteUser[]; monitorUsers: SiteUser[]
assetsRoles: string[]; issueRoles: string[] assetsRoles: string[]; issueRoles: string[]; monitorRoles: string[]
emails: Record<string, string> emails: Record<string, string>
} }
@ -37,7 +37,7 @@ export default function RoleManager({ setResult, onUserUpdated }: Props) {
const fetchRoleData = useCallback(async () => { const fetchRoleData = useCallback(async () => {
try { try {
const res = await fetch('/api/admin/user-roles'); const d = await res.json() const res = await fetch('/api/admin/user-roles'); const d = await res.json()
if (d.users) setRoleData({ assetsUsers: d.users.assets || [], issueUsers: d.users.issue || [], assetsRoles: d.assetsRoles || [], issueRoles: d.issueRoles || [], emails: d.emails || {} }) if (d.users) setRoleData({ assetsUsers: d.users.assets || [], issueUsers: d.users.issue || [], monitorUsers: d.users.monitor || [], assetsRoles: d.assetsRoles || [], issueRoles: d.issueRoles || [], monitorRoles: d.monitorRoles || [], emails: d.emails || {} })
} catch {} } catch {}
}, []) }, [])
@ -101,9 +101,10 @@ export default function RoleManager({ setResult, onUserUpdated }: Props) {
if (!roleData) return <p style={{ fontSize: 13, color: 'var(--text-muted)', textAlign: 'center', padding: 40 }}>...</p> if (!roleData) return <p style={{ fontSize: 13, color: 'var(--text-muted)', textAlign: 'center', padding: 40 }}>...</p>
const userMap = new Map<string, { displayName: string; email: string; assetsRole: string; issueRole: string }>() const userMap = new Map<string, { displayName: string; email: string; assetsRole: string; issueRole: string; monitorRole: string }>()
roleData.assetsUsers.forEach(u => userMap.set(u.username, { displayName: u.display_name, email: roleData.emails[u.username] || '', assetsRole: u.role, issueRole: '—' })) roleData.assetsUsers.forEach(u => userMap.set(u.username, { displayName: u.display_name, email: roleData.emails[u.username] || '', assetsRole: u.role, issueRole: '—', monitorRole: '—' }))
roleData.issueUsers.forEach(u => { const e = userMap.get(u.username); if (e) e.issueRole = u.role; else userMap.set(u.username, { displayName: u.display_name, email: roleData.emails[u.username] || '', assetsRole: '—', issueRole: u.role }) }) roleData.issueUsers.forEach(u => { const e = userMap.get(u.username); if (e) e.issueRole = u.role; else userMap.set(u.username, { displayName: u.display_name, email: roleData.emails[u.username] || '', assetsRole: '—', issueRole: u.role, monitorRole: '—' }) })
roleData.monitorUsers.forEach(u => { const e = userMap.get(u.username); if (e) e.monitorRole = u.role; else userMap.set(u.username, { displayName: u.display_name, email: roleData.emails[u.username] || '', assetsRole: '—', issueRole: '—', monitorRole: u.role }) })
const users = Array.from(userMap.entries()) const users = Array.from(userMap.entries())
const changed = Object.keys(pending).length const changed = Object.keys(pending).length
@ -119,7 +120,7 @@ export default function RoleManager({ setResult, onUserUpdated }: Props) {
<div style={{ overflowX: 'auto' }}><table style={ss.table}> <div style={{ overflowX: 'auto' }}><table style={ss.table}>
<thead> <thead>
<tr> <tr>
<th style={ss.th}></th><th style={ss.th}></th><th style={ss.th}></th><th style={ss.th}></th><th style={ss.th}></th> <th style={ss.th}></th><th style={ss.th}></th><th style={ss.th}></th><th style={ss.th}></th><th style={ss.th}></th><th style={ss.th}></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -162,6 +163,7 @@ export default function RoleManager({ setResult, onUserUpdated }: Props) {
</td> </td>
<td style={ss.td}><RoleCell site="assets" username={uname} originalRole={info.assetsRole} roles={roleData.assetsRoles} pending={pending} onSelect={handleSelect} getCurrentRole={getCurrentRole} /></td> <td style={ss.td}><RoleCell site="assets" username={uname} originalRole={info.assetsRole} roles={roleData.assetsRoles} pending={pending} onSelect={handleSelect} getCurrentRole={getCurrentRole} /></td>
<td style={ss.td}><RoleCell site="issue" username={uname} originalRole={info.issueRole} roles={roleData.issueRoles} pending={pending} onSelect={handleSelect} getCurrentRole={getCurrentRole} /></td> <td style={ss.td}><RoleCell site="issue" username={uname} originalRole={info.issueRole} roles={roleData.issueRoles} pending={pending} onSelect={handleSelect} getCurrentRole={getCurrentRole} /></td>
<td style={ss.td}><RoleCell site="monitor" username={uname} originalRole={info.monitorRole} roles={roleData.monitorRoles} pending={pending} onSelect={handleSelect} getCurrentRole={getCurrentRole} /></td>
</tr> </tr>
))} ))}
</tbody> </tbody>