97 lines
3.8 KiB
TypeScript
97 lines
3.8 KiB
TypeScript
import { Mail, Phone, MessageSquare, Bell } from 'lucide-react';
|
|
|
|
// 将 notify_channels (可能是数组/JSON字符串/逗号分隔) 解析为数组
|
|
export function parseChannels(notifyChannels: any): string[] {
|
|
if (!notifyChannels) return [];
|
|
if (Array.isArray(notifyChannels)) return notifyChannels;
|
|
if (typeof notifyChannels === 'string') {
|
|
try {
|
|
const parsed = JSON.parse(notifyChannels);
|
|
if (Array.isArray(parsed)) return parsed;
|
|
} catch {
|
|
// 非JSON,尝试逗号分隔
|
|
}
|
|
return notifyChannels.split(',').map((s) => s.trim()).filter(Boolean);
|
|
}
|
|
return [];
|
|
}
|
|
|
|
// 已知的渠道类型(用于旧数据兼容 — 旧规则存储类型字符串而非渠道 ID)
|
|
export const KNOWN_CHANNEL_TYPES = ['email', 'sms', 'dingtalk', 'wechat', 'webhook'];
|
|
|
|
// 根据渠道类型获取图标组件名(用字符串映射,组件内用 getChannelIcon 返回实际组件)
|
|
export const CHANNEL_TYPE_LABELS: Record<string, string> = {
|
|
dingtalk: '钉钉',
|
|
wechat: '企业微信',
|
|
email: '邮件',
|
|
sms: '短信',
|
|
webhook: 'Webhook',
|
|
};
|
|
|
|
// 解析单个 notify_channels 引用值 — 返回 {id?, type, name, isLegacy}
|
|
// 新格式存渠道 ID (string),旧格式存类型字符串 (email/dingtalk 等)
|
|
export function resolveChannelRef(value: string, channels: any[]): { type: string; name: string; isLegacy: boolean } | null {
|
|
if (!value) return null;
|
|
// 尝试按 ID 匹配已配置渠道
|
|
const ch = channels.find((c) => String(c.id) === String(value));
|
|
if (ch) return { type: ch.type || 'unknown', name: ch.name || ch.type || '未命名', isLegacy: false };
|
|
// 兼容旧格式:值本身是类型字符串
|
|
if (KNOWN_CHANNEL_TYPES.includes(value)) {
|
|
return { type: value, name: CHANNEL_TYPE_LABELS[value] || value, isLegacy: true };
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// 渠道类型选项(供下拉选择)
|
|
export const CHANNEL_TYPES: { key: string; label: string; placeholder: string }[] = [
|
|
{ key: 'dingtalk', label: '钉钉', placeholder: '{\n "webhook": "https://oapi.dingtalk.com/robot/send?access_token=***",\n "secret": "SECxxx"\n}' },
|
|
{ key: 'wechat', label: '企业微信', placeholder: '{\n "webhook": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=***"\n}' },
|
|
{ key: 'email', label: '邮件', placeholder: '{\n "to": "ops@ezops.io",\n "subject": "告警通知"\n}' },
|
|
{ key: 'sms', label: '短信', placeholder: '{\n "phones": "13800138000",\n "sign": "AIOps"\n}' },
|
|
{ key: 'webhook', label: 'Webhook', placeholder: '{\n "url": "https://example.com/alert"\n}' },
|
|
];
|
|
|
|
export function getChannelTypeLabel(type: string) {
|
|
return CHANNEL_TYPES.find((t) => t.key === type)?.label || type;
|
|
}
|
|
|
|
// 根据渠道类型返回对应图标组件
|
|
export function getChannelIcon(type: string) {
|
|
switch (type) {
|
|
case 'email':
|
|
return Mail;
|
|
case 'sms':
|
|
return Phone;
|
|
case 'dingtalk':
|
|
case 'wechat':
|
|
return MessageSquare;
|
|
default:
|
|
return Bell;
|
|
}
|
|
}
|
|
|
|
// 渲染规则的通知渠道引用列表(兼容新格式=渠道ID 和 旧格式=类型字符串)
|
|
export function renderRuleChannels(rule: any, notifyChannels: any[]) {
|
|
const refs = parseChannels(rule.notify_channels);
|
|
if (refs.length === 0) return <span className="text-xs text-slate-600">--</span>;
|
|
return (
|
|
<div className="flex flex-wrap gap-1">
|
|
{refs.map((ref) => {
|
|
const resolved = resolveChannelRef(ref, notifyChannels);
|
|
if (!resolved) return null;
|
|
const Icon = getChannelIcon(resolved.type);
|
|
return (
|
|
<span
|
|
key={ref}
|
|
title={`${resolved.name} (${getChannelTypeLabel(resolved.type)})`}
|
|
className="inline-flex items-center gap-1 text-xs px-1.5 py-0.5 rounded bg-slate-800 text-slate-300"
|
|
>
|
|
<Icon className="h-3 w-3 text-slate-400" />
|
|
{resolved.name}
|
|
</span>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|