feat(rbac): Viewer-Rolle auf alle verbleibenden CRUD-Seiten durchsetzen — v1.1.92

DNS, NTP, ForwardProxy, RoutingRules, Networks (Interfaces + Routes),
IPAddresses, WireGuard (Servers + Clients + Peers), Backups/RemoteTargets, SSL:
Add-Buttons, quickToggle-Switches und Settings-Save-Buttons für Viewer deaktiviert.
ActionButtons-Komponente schützt Edit/Delete bereits zentral; hier wurden nur
die verbleibenden Mutationspunkte (Add, Switch, Submit) nachgezogen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-24 13:56:24 +02:00
parent d4267e2003
commit d3a3b93c1b
15 changed files with 252 additions and 149 deletions

View File

@@ -10,6 +10,7 @@ import PageHeader from '../../components/PageHeader'
import ActionButtons from '../../components/ActionButtons'
import apiClient, { isEnvelope } from '../../api/client'
import { useAuthStore } from '../../stores/auth'
interface RoutingRule {
id: number
@@ -68,6 +69,7 @@ async function listHAProxyStats(): Promise<HAProxyStat[]> {
export default function RoutingRulesPage() {
const { t } = useTranslation()
const qc = useQueryClient()
const isViewer = useAuthStore((s) => s.user?.role) === 'viewer'
const { data: rules, isLoading } = useQuery({ queryKey: ['routing-rules'], queryFn: listRules })
const { data: domains } = useQuery({ queryKey: ['domains'], queryFn: listDomains })
@@ -160,6 +162,7 @@ export default function RoutingRulesPage() {
<Switch
size="small"
checked={v}
disabled={isViewer}
loading={quickToggle.isPending && quickToggle.variables?.id === row.id}
onChange={(checked) => quickToggle.mutate({ id: row.id, row, checked })}
/>
@@ -204,9 +207,11 @@ export default function RoutingRulesPage() {
dataSource={rules ?? []}
columns={columns}
extraActions={
<Button type="primary" icon={<PlusOutlined />} onClick={openCreate}>
{t('routing.addRule')}
</Button>
<Tooltip title={isViewer ? t('auth.viewerBadge') : undefined}>
<Button type="primary" icon={<PlusOutlined />} disabled={isViewer} onClick={openCreate}>
{t('routing.addRule')}
</Button>
</Tooltip>
}
emptyContent={
<EmptyState
@@ -214,9 +219,11 @@ export default function RoutingRulesPage() {
title={t('routing.emptyTitle')}
description={t('routing.emptyDesc')}
action={
<Button type="primary" icon={<PlusOutlined />} onClick={openCreate}>
{t('routing.addRule')}
</Button>
<Tooltip title={isViewer ? t('auth.viewerBadge') : undefined}>
<Button type="primary" icon={<PlusOutlined />} disabled={isViewer} onClick={openCreate}>
{t('routing.addRule')}
</Button>
</Tooltip>
}
/>
}