fix(backends): Backend-Löschen verweigern wenn Routing-Regeln darauf zeigen (v1.1.142)
- Service: Pre-Check auf routing_rules.backend_id → ErrInUse vor DELETE - Handler: ErrInUse → HTTP 409 Conflict statt 500 (response.Conflict helper) - UI: Delete-Button deaktiviert + Tooltip "Von N Regeln verwendet" wenn Referenzen existieren Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -387,6 +387,7 @@
|
||||
"serverHintCreate": "Speichern legt nur den Pool an. Server kommen im nächsten Schritt — Pool öffnen → „Server hinzufügen\".",
|
||||
"actions": "Aktionen",
|
||||
"deleteConfirm": "Backend-Pool {{name}} wirklich löschen? Alle Server im Pool werden mitentfernt.",
|
||||
"deleteInUse": "Von {{n}} Routing-Regel(n) verwendet — zuerst diese löschen",
|
||||
"backToList": "Zurück zur Übersicht",
|
||||
"settingsCard": "Pool-Einstellungen",
|
||||
"healthCheckHint": "HTTP-Pfad den HAProxy alle 5s pro Server prüft. Leer lassen = kein Health-Check.",
|
||||
|
||||
@@ -387,6 +387,7 @@
|
||||
"serverHintCreate": "Saving creates the pool only. Add servers in the next step — open the pool and click “Add server”.",
|
||||
"actions": "Actions",
|
||||
"deleteConfirm": "Really delete backend pool {{name}}? All servers in the pool will be removed too.",
|
||||
"deleteInUse": "Used by {{n}} routing rule(s) — remove those first",
|
||||
"backToList": "Back to backends",
|
||||
"settingsCard": "Pool settings",
|
||||
"healthCheckHint": "HTTP path HAProxy probes every 5s per server. Leave empty to disable health checks.",
|
||||
|
||||
@@ -97,6 +97,15 @@ async function listDomains(): Promise<DomainFull[]> {
|
||||
return (r.data.data as { domains?: DomainFull[] }).domains ?? []
|
||||
}
|
||||
|
||||
interface RoutingRule { id: number; backend_id: number }
|
||||
async function listRoutingRules(): Promise<RoutingRule[]> {
|
||||
try {
|
||||
const r = await apiClient.get('/routing-rules')
|
||||
if (!isEnvelope(r.data)) return []
|
||||
return (r.data.data as { routing_rules?: RoutingRule[] }).routing_rules ?? []
|
||||
} catch { return [] }
|
||||
}
|
||||
|
||||
interface HAProxyStat {
|
||||
backend: string; server: string; status: string
|
||||
sessions: number; bytes_in: number; bytes_out: number
|
||||
@@ -124,6 +133,7 @@ export default function BackendsPage() {
|
||||
|
||||
const { data, isLoading } = useQuery({ queryKey: ['backends'], queryFn: listBackends })
|
||||
const { data: domains } = useQuery({ queryKey: ['domains'], queryFn: listDomains })
|
||||
const { data: routingRules } = useQuery({ queryKey: ['routing-rules'], queryFn: listRoutingRules })
|
||||
const { data: services } = useQuery({
|
||||
queryKey: ['system', 'services'],
|
||||
queryFn: async () => {
|
||||
@@ -177,6 +187,9 @@ export default function BackendsPage() {
|
||||
const domainsForBackend = (id: number) =>
|
||||
(domains ?? []).filter(d => d.primary_backend_id === id)
|
||||
|
||||
const ruleCountForBackend = (id: number) =>
|
||||
(routingRules ?? []).filter(r => r.backend_id === id).length
|
||||
|
||||
const [creating, setCreating] = useState(false)
|
||||
const [form] = Form.useForm<BackendFormValues>()
|
||||
|
||||
@@ -296,13 +309,18 @@ export default function BackendsPage() {
|
||||
},
|
||||
{
|
||||
title: t('common.actions'), key: 'actions',
|
||||
render: (_, row) => (
|
||||
<ActionButtons
|
||||
onEdit={() => navigate(`/backends/${row.id}`)}
|
||||
onDelete={() => del.mutate(row.id)}
|
||||
deleteConfirm={t('backends.deleteConfirm', { name: row.name })}
|
||||
/>
|
||||
),
|
||||
render: (_, row) => {
|
||||
const n = ruleCountForBackend(row.id)
|
||||
return (
|
||||
<ActionButtons
|
||||
onEdit={() => navigate(`/backends/${row.id}`)}
|
||||
onDelete={() => del.mutate(row.id)}
|
||||
deleteConfirm={t('backends.deleteConfirm', { name: row.name })}
|
||||
deleteDisabled={n > 0}
|
||||
deleteDisabledReason={t('backends.deleteInUse', { n })}
|
||||
/>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user