feat(waf): Regel direkt aus Alert als Ausnahme hinzufügen — v1.2.78
Jede Alert-Zeile hat jetzt einen "Als Ausnahme"-Button. Klick: 1. Lädt aktuelle WAF-Config der betroffenen Domain 2. Fügt die Rule-ID zu rule_exclusions hinzu (dedupliziert) 3. Speichert via PUT /waf/configs/:domain_id → triggert HAProxy-Reload 4. Erfolgsmeldung + WAF-Config-Query invalidiert Button disabled wenn domain_id fehlt (Domain nicht aufgelöst) oder Viewer-Rolle. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1823,7 +1823,11 @@
|
||||
"ruleId": "Regel-ID",
|
||||
"severity": "Schwere",
|
||||
"msg": "Meldung"
|
||||
}
|
||||
},
|
||||
"addException": "Als Ausnahme",
|
||||
"exceptionAdded": "Regel als Ausnahme für diese Domain hinzugefügt.",
|
||||
"exceptionFailed": "Ausnahme konnte nicht gespeichert werden.",
|
||||
"noDomain": "Domain nicht gefunden — Ausnahme manuell konfigurieren."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1823,7 +1823,11 @@
|
||||
"ruleId": "Rule ID",
|
||||
"severity": "Severity",
|
||||
"msg": "Message"
|
||||
}
|
||||
},
|
||||
"addException": "Add exception",
|
||||
"exceptionAdded": "Rule added as exception for this domain.",
|
||||
"exceptionFailed": "Failed to add exception.",
|
||||
"noDomain": "Domain not found — configure exception manually."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,6 +272,25 @@ function AlertsTab({ domainId }: { domainId?: number }) {
|
||||
},
|
||||
})
|
||||
|
||||
const addException = useMutation({
|
||||
mutationFn: async ({ domainId, ruleId }: { domainId: number; ruleId: number }) => {
|
||||
// Fetch current config (returns default if none exists yet)
|
||||
const r = await apiClient.get(`/waf/configs/${domainId}`)
|
||||
const cfg: WafConfig = isEnvelope(r.data)
|
||||
? (r.data.data as { config: WafConfig }).config
|
||||
: defaultConfig(domainId)
|
||||
const exclusions = [...(cfg.rule_exclusions ?? [])]
|
||||
const ruleStr = String(ruleId)
|
||||
if (!exclusions.includes(ruleStr)) exclusions.push(ruleStr)
|
||||
return apiClient.put(`/waf/configs/${domainId}`, { ...cfg, rule_exclusions: exclusions })
|
||||
},
|
||||
onSuccess: () => {
|
||||
message.success(t('waf.alerts.exceptionAdded'))
|
||||
void qc.invalidateQueries({ queryKey: ['waf'] })
|
||||
},
|
||||
onError: () => message.error(t('waf.alerts.exceptionFailed')),
|
||||
})
|
||||
|
||||
const severityColor = (s: string) => {
|
||||
switch (s?.toLowerCase()) {
|
||||
case 'critical': return 'red'
|
||||
@@ -316,6 +335,32 @@ function AlertsTab({ domainId }: { domainId?: number }) {
|
||||
render: (v: string) => <Tag color={severityColor(v)}>{v || '—'}</Tag> },
|
||||
{ title: t('waf.alerts.col.msg'), dataIndex: 'rule_msg', key: 'rule_msg', ellipsis: true,
|
||||
render: (v: string) => <Text style={{ fontSize: 11, color: '#64748B' }}>{v || '—'}</Text> },
|
||||
{
|
||||
title: '',
|
||||
key: 'exception',
|
||||
width: 130,
|
||||
render: (_: unknown, row: WafAlert) => {
|
||||
const canExclude = !!row.domain_id && row.rule_id > 0 && !isViewer
|
||||
const isPending = addException.isPending &&
|
||||
addException.variables?.domainId === row.domain_id &&
|
||||
addException.variables?.ruleId === row.rule_id
|
||||
return (
|
||||
<Tooltip title={!row.domain_id ? t('waf.alerts.noDomain') : t('waf.alerts.addException')}>
|
||||
<Button
|
||||
size="small"
|
||||
disabled={!canExclude}
|
||||
loading={isPending}
|
||||
onClick={() => row.domain_id && addException.mutate({
|
||||
domainId: row.domain_id,
|
||||
ruleId: row.rule_id,
|
||||
})}
|
||||
>
|
||||
{t('waf.alerts.addException')}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user