diff --git a/VERSION b/VERSION index e64b3f7..8731870 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.88 +1.1.89 diff --git a/cmd/edgeguard-api/main.go b/cmd/edgeguard-api/main.go index 42cfb4a..0da789e 100644 --- a/cmd/edgeguard-api/main.go +++ b/cmd/edgeguard-api/main.go @@ -60,7 +60,7 @@ import ( usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users" ) -var version = "1.1.88" +var version = "1.1.89" func main() { addr := os.Getenv("EDGEGUARD_API_ADDR") diff --git a/cmd/edgeguard-ctl/main.go b/cmd/edgeguard-ctl/main.go index 3701ee5..3818852 100644 --- a/cmd/edgeguard-ctl/main.go +++ b/cmd/edgeguard-ctl/main.go @@ -11,7 +11,7 @@ import ( "git.netcell-it.de/projekte/edgeguard-native/internal/services/setup" ) -var version = "1.1.88" +var version = "1.1.89" const usage = `edgeguard-ctl — EdgeGuard CLI diff --git a/cmd/edgeguard-scheduler/main.go b/cmd/edgeguard-scheduler/main.go index 0a99513..3f70e61 100644 --- a/cmd/edgeguard-scheduler/main.go +++ b/cmd/edgeguard-scheduler/main.go @@ -35,7 +35,7 @@ import ( "git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts" ) -var version = "1.1.88" +var version = "1.1.89" const ( // renewTickInterval — how often we re-evaluate expiring certs. diff --git a/management-ui/src/components/ActionButtons.tsx b/management-ui/src/components/ActionButtons.tsx index 73ee9dc..0ee35bd 100644 --- a/management-ui/src/components/ActionButtons.tsx +++ b/management-ui/src/components/ActionButtons.tsx @@ -9,9 +9,9 @@ import { useAuthStore } from '../stores/auth' // // Either prop may be omitted to suppress that button — useful for // rows that aren't editable (e.g. builtin services). -// Viewer-role accounts automatically get delete disabled — mutations -// are blocked at the API level too, but hiding the button avoids -// confusing "access denied" errors for read-only users. +// Viewer-role accounts get both buttons disabled — mutations are +// blocked at the API level too, but disabling here avoids confusing +// "access denied" errors for read-only users. interface ActionButtonsProps { onEdit?: () => void onDelete?: () => void @@ -35,20 +35,23 @@ export default function ActionButtons({ const role = useAuthStore((s) => s.user?.role) const isViewer = role === 'viewer' + const viewerReason = isViewer ? t('auth.viewerBadge') : undefined + + const editDis = editDisabled || isViewer + const editDisReason = isViewer ? viewerReason : editDisabledReason + const delDisabled = deleteDisabled || isViewer - const delDisabledReason = isViewer - ? t('auth.viewerBadge') - : deleteDisabledReason + const delDisabledReason = isViewer ? viewerReason : deleteDisabledReason return ( {onEdit && ( - + + + + @@ -223,7 +227,7 @@ export default function BackendDetailPage() { - + @@ -231,7 +235,7 @@ export default function BackendDetailPage() { ) } -function ServerPanel({ backendID, haproxyStats }: { backendID: number; haproxyStats: HAProxyStat[] }) { +function ServerPanel({ backendID, haproxyStats, isViewer }: { backendID: number; haproxyStats: HAProxyStat[]; isViewer: boolean }) { const { t } = useTranslation() const qc = useQueryClient() const [open, setOpen] = useState(false) @@ -347,12 +351,14 @@ function ServerPanel({ backendID, haproxyStats }: { backendID: number; haproxySt return ( <>
- + + +
s.user?.role) === 'viewer' const { data: domain, isLoading } = useQuery({ queryKey: ['domain', domainID], @@ -193,8 +195,8 @@ export default function DomainDetailPage() { {certBadge()} - {backendHealth === 'UP' && backend UP} - {backendHealth === 'DOWN' && backend DOWN} + {backendHealth === 'UP' && {t('domains.backendUp')}} + {backendHealth === 'DOWN' && {t('domains.backendDown')}} } extra={ @@ -205,23 +207,30 @@ export default function DomainDetailPage() { description={t('ssl.renewConfirmDesc', { domain: domain.name })} onConfirm={() => issueCert.mutate(domain.name)} okText={t('common.yes')} cancelText={t('common.no')} + disabled={isViewer} > - + + + ) : ( - + + + )} - + @@ -347,16 +356,18 @@ export default function DomainDetailPage() { - + + + - + @@ -365,7 +376,7 @@ export default function DomainDetailPage() { // ── Routing Rules Panel ───────────────────────────────────────────────── -function RoutingRulesPanel({ domainID, domainName }: { domainID: number; domainName: string }) { +function RoutingRulesPanel({ domainID, domainName, isViewer }: { domainID: number; domainName: string; isViewer: boolean }) { const { t } = useTranslation() const qc = useQueryClient() const [open, setOpen] = useState(false) @@ -428,6 +439,7 @@ function RoutingRulesPanel({ domainID, domainName }: { domainID: number; domainN quickToggle.mutate({ id: r.id, row: r, checked })} /> @@ -437,19 +449,28 @@ function RoutingRulesPanel({ domainID, domainName }: { domainID: number; domainN title: t('common.actions'), key: 'a', width: 90, render: (_, r) => ( - + + + } > @@ -517,7 +540,7 @@ function RoutingRulesPanel({ domainID, domainName }: { domainID: number; domainN // ── Response Headers Panel ────────────────────────────────────────────── -function HeadersPanel({ domainID, domainName }: { domainID: number; domainName: string }) { +function HeadersPanel({ domainID, domainName, isViewer }: { domainID: number; domainName: string; isViewer: boolean }) { const { t } = useTranslation() const qc = useQueryClient() const [open, setOpen] = useState(false) @@ -566,19 +589,28 @@ function HeadersPanel({ domainID, domainName }: { domainID: number; domainName: title: t('common.actions'), key: 'a', width: 80, render: (_, r) => ( - + + + } >