feat(monitor): F6 日志告警引擎 — AlertEngine 支持 type=log 规则查询Loki/ES+触发/恢复 + 数据源认证升级(none/basic/bearer/custom) + 从查询创建告警真实API对接

This commit is contained in:
attacker committed 2026-06-28 04:00:38 +08:00
1 parent 894cb16a05
commit 6bba6144f3
17 files changed
+657 -74

No files matched your search

@@ -14,6 +14,7 @@ import {
} from '@/components/ui/dialog';
import { Loader2 } from 'lucide-react';
import { alertApi } from '@/api';
import { logCenterApi, type LogDataSource } from '@/api/monitor/log-center';
import { parseChannels, getChannelIcon, getChannelTypeLabel } from './alertShared';
import type { JsonData } from '@/types/common';
@@ -25,6 +26,7 @@ const emptyRuleForm = {
expression: '',
severity: 'warning',
duration: 300,
datasource_id: undefined as number | undefined,
notify_channels: [] as string[],
};
@@ -46,6 +48,18 @@ export function AlertRuleDialog({
}) {
const [ruleForm, setRuleForm] = useState({ ...emptyRuleForm });
const [ruleSaving, setRuleSaving] = useState(false);
const [logSources, setLogSources] = useState<LogDataSource[]>([]);
// 日志规则打开时加载对应类型的数据源列表 // Logan.Li
useEffect(() => {
if (!open || ruleForm.type !== 'log') {
setLogSources([]);
return;
}
logCenterApi.listDatasources(ruleForm.source).then((res) => {
setLogSources(res?.list || []);
}).catch(() => setLogSources([]));
}, [open, ruleForm.type, ruleForm.source]);
// 打开时同步表单(编辑时填充,新建时重置)
useEffect(() => {
@@ -59,6 +73,7 @@ export function AlertRuleDialog({
expression: editingRule.expression || '',
severity: editingRule.severity || 'warning',
duration: editingRule.duration || 300,
datasource_id: editingRule.datasource_id,
notify_channels: parseChannels(editingRule.notify_channels),
});
} else {
@@ -91,6 +106,7 @@ export function AlertRuleDialog({
expression: ruleForm.expression.trim(),
severity: ruleForm.severity,
duration: Number(ruleForm.duration) || 0,
datasource_id: ruleForm.datasource_id,
notify_channels: ruleForm.notify_channels,
};
if (editingRule) {
@@ -162,7 +178,7 @@ export function AlertRuleDialog({
<Label className="text-slate-300">数据源</Label>
<select
value={ruleForm.source}
onChange={(e) => setRuleForm({ ...ruleForm, source: e.target.value })}
onChange={(e) => setRuleForm({ ...ruleForm, source: e.target.value, datasource_id: undefined })}
className="w-full h-9 px-3 rounded-md bg-slate-800/50 border border-slate-700 text-slate-200 text-sm"
>
{ruleForm.type === 'log' ? (
@@ -181,6 +197,25 @@ export function AlertRuleDialog({
</div>
</div>
{ruleForm.type === 'log' && (
<div className="space-y-1.5">
<Label className="text-slate-300">日志数据源 <span className="text-red-400">*</span></Label>
<select
value={ruleForm.datasource_id || ''}
onChange={(e) => setRuleForm({ ...ruleForm, datasource_id: Number(e.target.value) || undefined })}
className="w-full h-9 px-3 rounded-md bg-slate-800/50 border border-slate-700 text-slate-200 text-sm"
>
<option value="">请选择数据源</option>
{logSources.map((s) => (
<option key={s.id} value={s.id}>{s.name}{s.is_default ? ' (默认)' : ''}</option>
))}
</select>
{logSources.length === 0 && (
<p className="text-xs text-amber-400/80">请先前往「日志库」添加 {ruleForm.source === 'loki' ? 'Loki' : 'Elasticsearch'} 数据源</p>
)}
</div>
)}
<div className="space-y-1.5">
<Label className="text-slate-300">触发表达式 <span className="text-red-400">*</span></Label>
<Input