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:
@@ -8,6 +8,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import apiClient, { isEnvelope } from '../../api/client'
|
||||
import { useAuthStore } from '../../stores/auth'
|
||||
import DataTable from '../../components/DataTable'
|
||||
import EmptyState from '../../components/EmptyState'
|
||||
import PageHeader from '../../components/PageHeader'
|
||||
@@ -180,6 +181,7 @@ export default function NTPPage() {
|
||||
function PoolsTab() {
|
||||
const { t } = useTranslation()
|
||||
const qc = useQueryClient()
|
||||
const isViewer = useAuthStore((s) => s.user?.role) === 'viewer'
|
||||
const { data, isLoading } = useQuery({ queryKey: ['ntp', 'pools'], queryFn: listPools })
|
||||
|
||||
const [editing, setEditing] = useState<Pool | null>(null)
|
||||
@@ -234,6 +236,7 @@ function PoolsTab() {
|
||||
<Switch
|
||||
size="small"
|
||||
checked={v}
|
||||
disabled={isViewer}
|
||||
loading={quickToggle.isPending && quickToggle.variables?.id === row.id}
|
||||
onChange={(checked) => quickToggle.mutate({ id: row.id, row, checked })}
|
||||
/>
|
||||
@@ -267,9 +270,11 @@ function PoolsTab() {
|
||||
dataSource={data ?? []}
|
||||
columns={cols}
|
||||
extraActions={
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={openCreate}>
|
||||
{t('ntp.pool.add')}
|
||||
</Button>
|
||||
<Tooltip title={isViewer ? t('auth.viewerBadge') : undefined}>
|
||||
<Button type="primary" icon={<PlusOutlined />} disabled={isViewer} onClick={openCreate}>
|
||||
{t('ntp.pool.add')}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
}
|
||||
emptyContent={
|
||||
<EmptyState
|
||||
@@ -277,9 +282,11 @@ function PoolsTab() {
|
||||
title={t('ntp.pool.emptyTitle')}
|
||||
description={t('ntp.pool.emptyDesc')}
|
||||
action={
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={openCreate}>
|
||||
{t('ntp.pool.add')}
|
||||
</Button>
|
||||
<Tooltip title={isViewer ? t('auth.viewerBadge') : undefined}>
|
||||
<Button type="primary" icon={<PlusOutlined />} disabled={isViewer} onClick={openCreate}>
|
||||
{t('ntp.pool.add')}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
}
|
||||
@@ -336,6 +343,7 @@ function PoolsTab() {
|
||||
function SettingsTab() {
|
||||
const { t } = useTranslation()
|
||||
const qc = useQueryClient()
|
||||
const isViewer = useAuthStore((s) => s.user?.role) === 'viewer'
|
||||
const { data, isLoading } = useQuery({ queryKey: ['ntp', 'settings'], queryFn: getSettings })
|
||||
const { data: sys } = useQuery({ queryKey: ['system', 'interfaces'], queryFn: listSystemInterfaces })
|
||||
const [form] = Form.useForm<SettingsForm>()
|
||||
@@ -415,9 +423,11 @@ function SettingsTab() {
|
||||
<Input placeholder="right/UTC" allowClear />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" loading={save.isPending}>
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
<Tooltip title={isViewer ? t('auth.viewerBadge') : undefined}>
|
||||
<Button type="primary" htmlType="submit" disabled={isViewer} loading={save.isPending}>
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user