fix(i18n): 回退到稳定的 32% 覆盖率状态 (tsc + Vite 构建通过)
批量转换脚本引入的语法错误已回退,保留稳定的 i18n 转换: - 翻译词典: 22 namespaces, 3600+ keys - 已接入: 145+ 文件 (Dashboard/Login/K8s 主页面/Monitor/System/Deploy) - 语言切换器在 Header 中可用 (🇨🇳/🇺🇸) - tsc 零错误, Vite 构建成功
This commit is contained in:
1 parent
ecec73989b
commit
72bfb0e12d
191 files changed
+5057
-7592
No files matched your search
@@ -28,10 +28,8 @@ import {
|
||||
} from './agentAlertHelper';
|
||||
import { AgentRuleDialog } from './AgentRuleDialog';
|
||||
import type { JsonData } from '@/types/common';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function AgentAlerts() {
|
||||
const { t } = useTranslation();
|
||||
const confirm = useConfirm();
|
||||
const [activeTab, setActiveTab] = useState('events');
|
||||
const [alerts, setAlerts] = useState<JsonData[]>([]);
|
||||
@@ -54,7 +52,7 @@ export function AgentAlerts() {
|
||||
setRules(asList(rulesRes));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error(t('agent.agentalerts.s7ffaa4'));
|
||||
toast.error('加载告警数据失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -66,14 +64,14 @@ export function AgentAlerts() {
|
||||
|
||||
// ============ 告警事件 ============
|
||||
const handleResolve = async (id: number) => {
|
||||
const ok = await confirm({ title: t('agent.agentalerts.sd56d01'), description: t('agent.agentalerts.s99f0fb'), confirmText: t('common.resume') });
|
||||
const ok = await confirm({ title: '恢复告警', description: '确定将此告警标记为已恢复?', confirmText: '恢复' });
|
||||
if (!ok) return;
|
||||
try {
|
||||
await agentApi.resolveAlert(id);
|
||||
toast.success(t('agent.agentalerts.s0e2352'));
|
||||
toast.success('告警已恢复');
|
||||
loadData();
|
||||
} catch (err) {
|
||||
toast.error(err?.message || t('common.operationFailed'));
|
||||
toast.error(err?.message || '操作失败');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -89,14 +87,14 @@ export function AgentAlerts() {
|
||||
};
|
||||
|
||||
const handleDeleteRule = async (id: number) => {
|
||||
const ok = await confirm({ title: t('alert.deleteRule'), description: t('agent.agentalerts.sd30155'), confirmText: t('common.delete'), variant: 'destructive' });
|
||||
const ok = await confirm({ title: '删除规则', description: '确定删除此告警规则?', confirmText: '删除', variant: 'destructive' });
|
||||
if (!ok) return;
|
||||
try {
|
||||
await agentApi.deleteRule(id);
|
||||
toast.success(t('agent.agentalerts.s8b2798'));
|
||||
toast.success('规则已删除');
|
||||
loadData();
|
||||
} catch (err) {
|
||||
toast.error(err?.message || t('common.deleteFailed'));
|
||||
toast.error(err?.message || '删除失败');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -105,7 +103,7 @@ export function AgentAlerts() {
|
||||
await agentApi.updateRule(rule.id, { ...rule, enabled: !rule.enabled });
|
||||
loadData();
|
||||
} catch (err) {
|
||||
toast.error(err?.message || t('common.operationFailed'));
|
||||
toast.error(err?.message || '操作失败');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -114,8 +112,8 @@ export function AgentAlerts() {
|
||||
{/* 页头 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-[var(--dg-text-primary)]">{t('agent.agentalerts.s87efbd')}</h2>
|
||||
<p className="text-sm dg-text-secondary mt-0.5">{t('agent.agentalerts.s03a08c')}</p>
|
||||
<h2 className="text-xl font-semibold text-[var(--dg-text-primary)]">Agent 告警</h2>
|
||||
<p className="text-sm dg-text-secondary mt-0.5">基于 Agent 上报指标的告警规则与事件</p>
|
||||
</div>
|
||||
<Button variant="outline" size="sm" onClick={loadData} disabled={loading} className="dg-input text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-hover)]">
|
||||
<RefreshCw className={`h-4 w-4 mr-1.5 ${loading ? 'animate-spin' : ''}`} />
|
||||
@@ -125,8 +123,8 @@ export function AgentAlerts() {
|
||||
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
|
||||
<TabsList className="bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)]">
|
||||
<TabsTrigger value="events" className="data-[state=active]:bg-[var(--dg-bg-active)]">{t('agent.agentalerts.s0e5b83')}</TabsTrigger>
|
||||
<TabsTrigger value="rules" className="data-[state=active]:bg-[var(--dg-bg-active)]">{t('alert.tab.rules')}</TabsTrigger>
|
||||
<TabsTrigger value="events" className="data-[state=active]:bg-[var(--dg-bg-active)]">告警事件</TabsTrigger>
|
||||
<TabsTrigger value="rules" className="data-[state=active]:bg-[var(--dg-bg-active)]">告警规则</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* ========== 告警事件 ========== */}
|
||||
@@ -154,14 +152,14 @@ export function AgentAlerts() {
|
||||
<Card className="dg-card">
|
||||
<CardContent className="flex items-center justify-center py-16">
|
||||
<Loader2 className="h-5 w-5 animate-spin dg-text-muted" />
|
||||
<span className="ml-2 dg-text-secondary">{t('common.loading')}</span>
|
||||
<span className="ml-2 dg-text-secondary">加载中...</span>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : alerts.length === 0 ? (
|
||||
<Card className="dg-card">
|
||||
<CardContent className="flex flex-col items-center justify-center py-16">
|
||||
<CheckCircle className="h-10 w-10 text-emerald-500 mb-3" />
|
||||
<p className="dg-text-secondary">{resolvedView ? t('agent.agentalerts.se459f4') : t('agent.agentalerts.s778455')}</p>
|
||||
<p className="dg-text-secondary">{resolvedView ? '暂无已恢复的告警' : '当前无活动告警,一切正常'}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
@@ -177,20 +175,20 @@ export function AgentAlerts() {
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{severityBadge(alert.severity)}
|
||||
<span className="font-medium text-[var(--dg-text-primary)]">{alert.rule_name || alert.name || alert.title || t('dashboard.stat.alerts')}</span>
|
||||
<span className="font-medium text-[var(--dg-text-primary)]">{alert.rule_name || alert.name || alert.title || '告警'}</span>
|
||||
</div>
|
||||
<p className="text-sm dg-text-secondary mt-1">{alert.message || alert.description || t('agent.agentalerts.sddab4a')}</p>
|
||||
<p className="text-sm dg-text-secondary mt-1">{alert.message || alert.description || '触发告警阈值'}</p>
|
||||
<div className="flex items-center gap-4 mt-2 text-xs dg-text-muted flex-wrap">
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<Cpu className="h-3 w-3" />
|
||||
{alert.hostname || '--'}
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<span className="dg-text-secondary">{t('agent.agentalerts.sbfdcf2')}</span> {getMetricLabel(alert.metric)}
|
||||
<span className="dg-text-secondary">指标:</span> {getMetricLabel(alert.metric)}
|
||||
</span>
|
||||
{alert.value != null && (
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<span className="dg-text-secondary">{t('agent.agentalerts.s2d1376')}</span> {alert.value}
|
||||
<span className="dg-text-secondary">当前值:</span> {alert.value}
|
||||
</span>
|
||||
)}
|
||||
<span className="inline-flex items-center gap-1">
|
||||
@@ -232,14 +230,14 @@ export function AgentAlerts() {
|
||||
<Card className="dg-card">
|
||||
<CardContent className="flex items-center justify-center py-16">
|
||||
<Loader2 className="h-5 w-5 animate-spin dg-text-muted" />
|
||||
<span className="ml-2 dg-text-secondary">{t('common.loading')}</span>
|
||||
<span className="ml-2 dg-text-secondary">加载中...</span>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : rules.length === 0 ? (
|
||||
<Card className="dg-card">
|
||||
<CardContent className="flex flex-col items-center justify-center py-16">
|
||||
<Bell className="h-10 w-10 dg-text-muted mb-3" />
|
||||
<p className="dg-text-secondary">{t('alert.noRules')}</p>
|
||||
<p className="dg-text-secondary">暂无告警规则</p>
|
||||
<Button size="sm" onClick={openCreateRule} className="mt-4 bg-blue-600 hover:bg-blue-700 text-white">
|
||||
<Plus className="h-4 w-4 mr-1.5" />
|
||||
创建第一条规则
|
||||
@@ -263,7 +261,7 @@ export function AgentAlerts() {
|
||||
<span className="font-medium text-[var(--dg-text-primary)]">{rule.name}</span>
|
||||
{severityBadge(rule.severity)}
|
||||
{rule.enabled === false && (
|
||||
<Badge className="bg-[var(--dg-bg-active)] dg-text-secondary border border-[var(--dg-border-hover)]">{t('alert.disabled')}</Badge>
|
||||
<Badge className="bg-[var(--dg-bg-active)] dg-text-secondary border border-[var(--dg-border-hover)]">已禁用</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-3 mt-1.5 text-xs dg-text-muted flex-wrap">
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
} from '@/components/ui/dialog';
|
||||
|
||||
import {
|
||||
import { useTranslation } from 'react-i18next';
|
||||
Cpu,
|
||||
MemoryStick,
|
||||
HardDrive,
|
||||
@@ -42,7 +41,6 @@ import { useTranslation } from 'react-i18next';
|
||||
// ============ 组件 ============
|
||||
|
||||
export function AgentMonitor() {
|
||||
const { t } = useTranslation();
|
||||
const [hosts, setHosts] = useState<AgentHostRecord[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
@@ -70,7 +68,7 @@ export function AgentMonitor() {
|
||||
if (fresh) setSelectedHost(fresh);
|
||||
}
|
||||
} catch (err) {
|
||||
if (!silent) toast.error(t('agent.agentmonitor.s5760f6'));
|
||||
if (!silent) toast.error('加载主机列表失败');
|
||||
console.error(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -99,7 +97,7 @@ export function AgentMonitor() {
|
||||
// 提交远程命令(直连 SSH 即时返回)
|
||||
const submitCommand = async () => {
|
||||
if (!cmdCommand.trim()) {
|
||||
toast.error(t('agent.agentmonitor.s9358aa'));
|
||||
toast.error('请输入要执行的命令');
|
||||
return;
|
||||
}
|
||||
setCmdSubmitting(true);
|
||||
@@ -113,11 +111,11 @@ export function AgentMonitor() {
|
||||
const resData = (res?.data ?? {}) as Record<string, unknown>;
|
||||
setCmdResult({
|
||||
status: res?.code === 200 ? 'completed' : 'failed',
|
||||
result: (resData.output as string) || (res?.message as string) || t('agent.agentmonitor.s26d45b'),
|
||||
result: (resData.output as string) || (res?.message as string) || '无输出',
|
||||
error: resData.error as string | undefined,
|
||||
});
|
||||
} catch (err) {
|
||||
setCmdResult({ status: 'failed', result: err?.message || t('automation.scriptFailed') });
|
||||
setCmdResult({ status: 'failed', result: err?.message || '执行失败' });
|
||||
} finally {
|
||||
setCmdSubmitting(false);
|
||||
loadLatest(true);
|
||||
@@ -131,9 +129,9 @@ export function AgentMonitor() {
|
||||
const onlineCount = hosts.filter((h) => isOnline(h.last_report || h.last_reported_at || h.updated_at)).length;
|
||||
|
||||
const stats = [
|
||||
{ label: t('agent.agentmonitor.sd94cd2'), value: hosts.length, icon: Server, color: 'dg-text-blue' },
|
||||
{ label: t('common.online'), value: onlineCount, icon: Wifi, color: 'dg-text-green' },
|
||||
{ label: t('common.offline'), value: hosts.length - onlineCount, icon: WifiOff, color: 'dg-text-red' },
|
||||
{ label: '主机总数', value: hosts.length, icon: Server, color: 'dg-text-blue' },
|
||||
{ label: '在线', value: onlineCount, icon: Wifi, color: 'dg-text-green' },
|
||||
{ label: '离线', value: hosts.length - onlineCount, icon: WifiOff, color: 'dg-text-red' },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -141,8 +139,8 @@ export function AgentMonitor() {
|
||||
{/* 页头 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-[var(--dg-text-primary)]">{t('agent.agentMonitor')}</h2>
|
||||
<p className="text-sm dg-text-secondary mt-0.5">{t('agent.agentmonitor.sa75d75')}</p>
|
||||
<h2 className="text-xl font-semibold text-[var(--dg-text-primary)]">Agent 监控</h2>
|
||||
<p className="text-sm dg-text-secondary mt-0.5">实时监控 Agent 上报的主机指标</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
@@ -188,14 +186,14 @@ export function AgentMonitor() {
|
||||
<Card className="dg-card">
|
||||
<CardContent className="flex items-center justify-center py-16">
|
||||
<Loader2 className="h-6 w-6 animate-spin dg-text-muted" />
|
||||
<span className="ml-2 dg-text-secondary">{t('common.loading')}</span>
|
||||
<span className="ml-2 dg-text-secondary">加载中...</span>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : hosts.length === 0 ? (
|
||||
<Card className="dg-card">
|
||||
<CardContent className="flex flex-col items-center justify-center py-16">
|
||||
<Server className="h-10 w-10 dg-text-muted mb-3" />
|
||||
<p className="dg-text-secondary">{t('agent.noAgentData')}</p>
|
||||
<p className="dg-text-secondary">暂无 Agent 上报数据</p>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => setWizardOpen(true)}
|
||||
@@ -242,8 +240,8 @@ export function AgentMonitor() {
|
||||
{/* 指标条 */}
|
||||
<div className="space-y-2">
|
||||
<MetricBar icon={Cpu} label="CPU" value={cpu} />
|
||||
<MetricBar icon={MemoryStick} label=t('k8s.pods.memory') value={mem} />
|
||||
<MetricBar icon={HardDrive} label=t('dashboard.topUsage.disk') value={disk} />
|
||||
<MetricBar icon={MemoryStick} label="内存" value={mem} />
|
||||
<MetricBar icon={HardDrive} label="磁盘" value={disk} />
|
||||
</div>
|
||||
|
||||
{/* 端口 & 时间 */}
|
||||
@@ -309,7 +307,7 @@ export function AgentMonitor() {
|
||||
<div className="space-y-3">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">{t('agent.installTarget')}</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">目标主机</Label>
|
||||
<Input
|
||||
value={cmdHost}
|
||||
onChange={(e) => setCmdHost(e.target.value)}
|
||||
@@ -320,18 +318,18 @@ export function AgentMonitor() {
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">{t('system.terminalCommand')}</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">命令</Label>
|
||||
<Textarea
|
||||
value={cmdCommand}
|
||||
onChange={(e) => setCmdCommand(e.target.value)}
|
||||
placeholder=t('agent.agentmonitor.s6e62a5')
|
||||
placeholder="例如:free -m && df -h"
|
||||
rows={3}
|
||||
className="bg-[var(--dg-bg-primary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] font-mono text-sm resize-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="w-40 space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">{t('monitor.probeTimeout')}</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">超时(秒)</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={cmdTimeout}
|
||||
@@ -344,7 +342,7 @@ export function AgentMonitor() {
|
||||
{cmdResult && (
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">{t('automation.execResult')}</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">执行结果</Label>
|
||||
<Badge
|
||||
className={
|
||||
cmdResult.status === 'success' || cmdResult.status === 'done' || cmdResult.status === 'completed'
|
||||
@@ -358,7 +356,7 @@ export function AgentMonitor() {
|
||||
</Badge>
|
||||
</div>
|
||||
<pre className="text-xs text-[var(--dg-text-secondary)] font-mono whitespace-pre-wrap break-all bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-lg p-3 max-h-48 overflow-auto">
|
||||
{cmdResult.result || cmdResult.output || cmdResult.error || t('agent.agentmonitor.s26d45b')}
|
||||
{cmdResult.result || cmdResult.output || cmdResult.error || '无输出'}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
@@ -374,9 +372,9 @@ export function AgentMonitor() {
|
||||
className="bg-blue-600 hover:bg-blue-700 text-white"
|
||||
>
|
||||
{cmdSubmitting ? (
|
||||
<><Loader2 className="h-4 w-4 mr-1.5 animate-spin" />{t('common.executing')}</>
|
||||
<><Loader2 className="h-4 w-4 mr-1.5 animate-spin" />执行中</>
|
||||
) : (
|
||||
<><Terminal className="h-4 w-4 mr-1.5" />{t('common.execute')}</>
|
||||
<><Terminal className="h-4 w-4 mr-1.5" />执行</>
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
@@ -391,7 +389,6 @@ export function AgentMonitor() {
|
||||
|
||||
// ============ 指标条子组件 ============
|
||||
function MetricBar({ icon: Icon, label, value }: { icon: ComponentType<{ className?: string }>; label: string; value: number }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon className="h-3.5 w-3.5 dg-text-muted shrink-0" />
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { METRIC_OPTIONS, OPERATOR_OPTIONS, SEVERITY_OPTIONS } from './agentAlertHelper';
|
||||
import type { JsonData } from '@/types/common';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
@@ -46,7 +45,6 @@ const DEFAULT_FORM = {
|
||||
};
|
||||
|
||||
export function AgentRuleDialog({ open, onOpenChange, editingRule, onSuccess }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const [ruleForm, setRuleForm] = useState({ ...DEFAULT_FORM });
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
@@ -71,7 +69,7 @@ export function AgentRuleDialog({ open, onOpenChange, editingRule, onSuccess }:
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!ruleForm.name.trim()) {
|
||||
toast.error(t('agent.agentruledialog.s40f3f1'));
|
||||
toast.error('请输入规则名称');
|
||||
return;
|
||||
}
|
||||
setSaving(true);
|
||||
@@ -90,15 +88,15 @@ export function AgentRuleDialog({ open, onOpenChange, editingRule, onSuccess }:
|
||||
}
|
||||
if (editingRule) {
|
||||
await agentApi.updateRule(editingRule.id, payload);
|
||||
toast.success(t('agent.agentruledialog.sa716ef'));
|
||||
toast.success('规则已更新');
|
||||
} else {
|
||||
await agentApi.createRule(payload);
|
||||
toast.success(t('agent.agentruledialog.s5702a3'));
|
||||
toast.success('规则已创建');
|
||||
}
|
||||
onOpenChange(false);
|
||||
onSuccess();
|
||||
} catch (err) {
|
||||
toast.error(err?.message || t('common.saveFailed'));
|
||||
toast.error(err?.message || '保存失败');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -108,24 +106,24 @@ export function AgentRuleDialog({ open, onOpenChange, editingRule, onSuccess }:
|
||||
<Dialog open={open} onOpenChange={(v) => { if (!v) onOpenChange(false); }}>
|
||||
<DialogContent className="max-w-md dg-card text-[var(--dg-text-primary)]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-[var(--dg-text-primary)]">{editingRule ? t('monitor.editRule') : t('agent.agentruledialog.sf10ed4')}</DialogTitle>
|
||||
<DialogDescription className="dg-text-secondary">{t('agent.agentruledialog.s0cc0f6')}</DialogDescription>
|
||||
<DialogTitle className="text-[var(--dg-text-primary)]">{editingRule ? '编辑规则' : '新建规则'}</DialogTitle>
|
||||
<DialogDescription className="dg-text-secondary">配置 Agent 指标告警规则</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">{t('monitor.ruleName')}</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">规则名称</Label>
|
||||
<Input
|
||||
value={ruleForm.name}
|
||||
onChange={(e) => setRuleForm({ ...ruleForm, name: e.target.value })}
|
||||
placeholder=t('agent.agentruledialog.s5a0f7e')
|
||||
placeholder="例如:CPU 使用率告警"
|
||||
className="h-9 dg-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">{t('agent.agentruledialog.s9a51fb')}</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">监控指标</Label>
|
||||
<Select value={ruleForm.metric} onValueChange={(v) => setRuleForm({ ...ruleForm, metric: v })}>
|
||||
<SelectTrigger className="h-9 dg-input">
|
||||
<SelectValue />
|
||||
@@ -138,7 +136,7 @@ export function AgentRuleDialog({ open, onOpenChange, editingRule, onSuccess }:
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">{t('monitor.ruleSeverity')}</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">严重程度</Label>
|
||||
<Select value={ruleForm.severity} onValueChange={(v) => setRuleForm({ ...ruleForm, severity: v })}>
|
||||
<SelectTrigger className="h-9 dg-input">
|
||||
<SelectValue />
|
||||
@@ -155,7 +153,7 @@ export function AgentRuleDialog({ open, onOpenChange, editingRule, onSuccess }:
|
||||
{ruleForm.metric !== 'offline' && (
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">{t('agent.agentruledialog.sd52f2c')}</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">比较运算符</Label>
|
||||
<Select value={ruleForm.operator} onValueChange={(v) => setRuleForm({ ...ruleForm, operator: v })}>
|
||||
<SelectTrigger className="h-9 dg-input">
|
||||
<SelectValue />
|
||||
@@ -181,39 +179,39 @@ export function AgentRuleDialog({ open, onOpenChange, editingRule, onSuccess }:
|
||||
|
||||
{ruleForm.metric === 'port_down' && (
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">{t('agent.agentruledialog.s4d1348')}</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">监控端口</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={ruleForm.port}
|
||||
onChange={(e) => setRuleForm({ ...ruleForm, port: e.target.value })}
|
||||
placeholder=t('agent.agentruledialog.s77565e')
|
||||
placeholder="例如:8080"
|
||||
className="h-9 dg-input"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">{t('agent.agentruledialog.sb1551d')}</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">持续时间(秒)</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={ruleForm.duration}
|
||||
onChange={(e) => setRuleForm({ ...ruleForm, duration: e.target.value })}
|
||||
placeholder=t('agent.agentruledialog.sd140c7')
|
||||
placeholder="指标持续超限多久后告警"
|
||||
className="h-9 dg-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">{t('agent.agentruledialog.sfd2ea0')}</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">启用规则</Label>
|
||||
<Switch checked={ruleForm.enabled} onCheckedChange={(v) => setRuleForm({ ...ruleForm, enabled: v })} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="gap-2">
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)} className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]">{t('common.cancel')}</Button>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)} className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]">取消</Button>
|
||||
<Button onClick={handleSave} disabled={saving} className="bg-blue-600 hover:bg-blue-700 text-white">
|
||||
{saving ? <Loader2 className="h-4 w-4 mr-1.5 animate-spin" /> : null}
|
||||
{editingRule ? i18n.t('common.save') : i18n.t('common.create')}
|
||||
{editingRule ? '保存' : '创建'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
@@ -14,7 +14,6 @@ import { Label } from '@/components/ui/label';
|
||||
import { Copy, Check, Terminal, Download, Settings, Play, Cpu, Zap, Loader2, Server, Shield } from 'lucide-react';
|
||||
import { serverApi } from '@/api';
|
||||
import { toast } from 'sonner';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface AgentWizardProps {
|
||||
open: boolean;
|
||||
@@ -41,7 +40,6 @@ function detectServerAddress() {
|
||||
}
|
||||
|
||||
export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled, onFailed }: AgentWizardProps) {
|
||||
const { t } = useTranslation();
|
||||
const token = useUserStore((s) => s.token) || '<YOUR_TOKEN>';
|
||||
const origin = detectServerAddress();
|
||||
const [copied, setCopied] = useState(false);
|
||||
@@ -106,10 +104,10 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
|
||||
|
||||
if (success) {
|
||||
setCopied(true);
|
||||
toast.success(t('agent.agentwizard.s19ee00'));
|
||||
toast.success('安装命令已复制到剪贴板,粘贴到目标主机执行');
|
||||
setTimeout(() => setCopied(false), 3000);
|
||||
} else {
|
||||
toast.error(t('agent.agentwizard.s7ac41b'));
|
||||
toast.error('自动复制失败,请在下方手动全选复制 (Ctrl+A → Ctrl+C)');
|
||||
setShowManualInput(true);
|
||||
}
|
||||
};
|
||||
@@ -119,19 +117,19 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
|
||||
const manualSteps = [
|
||||
{
|
||||
icon: Download,
|
||||
title: t('agent.agentwizard.s8c59e0'),
|
||||
title: '1. 下载二进制',
|
||||
commands: [
|
||||
`curl -fsSL ${binaryUrl} -o /usr/local/bin/aiops-agent`,
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: Settings,
|
||||
title: t('agent.agentwizard.s2f17bf'),
|
||||
title: '2. 赋予执行权限',
|
||||
commands: ['chmod +x /usr/local/bin/aiops-agent'],
|
||||
},
|
||||
{
|
||||
icon: Cpu,
|
||||
title: t('agent.agentwizard.s03fa00'),
|
||||
title: '3. 创建 systemd 服务',
|
||||
commands: [
|
||||
`cat > /etc/systemd/system/aiops-agent.service << 'EOF'`,
|
||||
`[Unit]`,
|
||||
@@ -151,7 +149,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
|
||||
},
|
||||
{
|
||||
icon: Play,
|
||||
title: t('agent.agentwizard.s84bd81'),
|
||||
title: '4. 启动并设置开机自启',
|
||||
commands: [
|
||||
'systemctl daemon-reload',
|
||||
'systemctl enable --now aiops-agent',
|
||||
@@ -165,7 +163,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
|
||||
const handleSshInstall = async () => {
|
||||
if (!server || !token) return;
|
||||
if (needsCreds && !sshPassword && !sshKey) {
|
||||
toast.error(t('agent.agentwizard.s6cfe96'));
|
||||
toast.error('请填写 SSH 登录凭据(密码或密钥)');
|
||||
return;
|
||||
}
|
||||
setSshInstalling(true);
|
||||
@@ -184,11 +182,11 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
|
||||
: undefined;
|
||||
// 优先尝试异步批量接口,即使单台也后台执行
|
||||
await serverApi.batchInstallAgent([server.id], token, credentials);
|
||||
toast.success(t('agent.agentwizard.s4c0811', { server.hostname: server.hostname }));
|
||||
toast.success(`${server.hostname}: 已提交 Agent 安装任务,后台执行中…`);
|
||||
onInstalled?.(server.id);
|
||||
onOpenChange(false);
|
||||
} catch (e) {
|
||||
const msg = e?.message || t('agent.installFailed');
|
||||
const msg = e?.message || '安装失败';
|
||||
toast.error(`${server.hostname} ${msg}`);
|
||||
onFailed?.(server.id, msg);
|
||||
} finally {
|
||||
@@ -201,12 +199,12 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
|
||||
<DialogContent className="max-w-2xl dg-card text-[var(--dg-text-primary)]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-[var(--dg-text-primary)]">
|
||||
{server ? t('agent.agentwizard.s78196e', { server.hostname: server.hostname }) : t('agent.agentwizard.s812cc1')}
|
||||
{server ? `部署 Agent → ${server.hostname}` : '部署 Agent'}
|
||||
</DialogTitle>
|
||||
<DialogDescription className="dg-text-secondary">
|
||||
{server
|
||||
? t('agent.agentwizard.s94a807', { server.hostname: server.hostname, server.ip: server.ip })
|
||||
: t('agent.agentwizard.sed0c1b')
|
||||
? `目标主机 ${server.hostname} (${server.ip}),选择安装方式:`
|
||||
: '在目标主机上执行以下命令即可完成 Agent 安装与注册'
|
||||
}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
@@ -248,7 +246,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
|
||||
variant="ghost"
|
||||
onClick={handleCopy}
|
||||
className="absolute top-2 right-2 h-8 w-8 dg-text-secondary hover:text-[var(--dg-text-primary)] hover:bg-[var(--dg-bg-hover)]"
|
||||
title=t('agent.agentwizard.s564be0')
|
||||
title="复制到剪贴板"
|
||||
>
|
||||
{copied ? <Check className="h-4 w-4 dg-text-green" /> : <Copy className="h-4 w-4" />}
|
||||
</Button>
|
||||
@@ -281,8 +279,8 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
|
||||
{!server ? (
|
||||
<div className="text-center py-12 dg-text-secondary">
|
||||
<Server className="h-10 w-10 mx-auto mb-3 opacity-50" />
|
||||
<p className="text-sm font-medium mb-1">{t('agent.agentwizard.sc9b7ee')}</p>
|
||||
<p className="text-xs">{t('agent.agentwizard.s12a111')}</p>
|
||||
<p className="text-sm font-medium mb-1">未选择目标主机</p>
|
||||
<p className="text-xs">请先在主机列表中勾选一台主机,再点击「安装 Agent」打开此页面</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
@@ -300,11 +298,11 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
|
||||
<div className="mb-4 p-3 rounded-lg bg-amber-950/30 border border-amber-800/40 space-y-3">
|
||||
<div className="flex items-center gap-2 text-xs text-amber-300/80">
|
||||
<Shield className="h-3.5 w-3.5" />
|
||||
<span>{t('agent.agentwizard.seb55df')}</span>
|
||||
<span>该服务器未配置 SSH 凭据,请填写以下信息(首次连接成功后自动保存)</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<Label className="text-xs dg-text-secondary">{t('login.username')}</Label>
|
||||
<Label className="text-xs dg-text-secondary">用户名</Label>
|
||||
<Input
|
||||
value={sshUsername}
|
||||
onChange={e => setSshUsername(e.target.value)}
|
||||
@@ -314,7 +312,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
|
||||
</div>
|
||||
<div className="flex items-end gap-1">
|
||||
<div className="flex-1">
|
||||
<Label className="text-xs dg-text-secondary">{t('login.password')}</Label>
|
||||
<Label className="text-xs dg-text-secondary">密码</Label>
|
||||
<Input
|
||||
type="password"
|
||||
value={sshPassword}
|
||||
@@ -323,7 +321,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
|
||||
className="h-8 text-sm dg-input text-[var(--dg-text-primary)] mt-1"
|
||||
/>
|
||||
</div>
|
||||
<span className="text-xs dg-text-muted px-1 pb-1">{t('common.logicalOr')}</span>
|
||||
<span className="text-xs dg-text-muted px-1 pb-1">或</span>
|
||||
<Button variant="ghost" size="sm"
|
||||
onClick={() => { setAuthType('key'); setSshPassword(''); }}
|
||||
className={`text-xs h-7 px-2 ${authType==='key'?'dg-text-blue':'dg-text-muted'}`}>
|
||||
@@ -333,7 +331,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
|
||||
</div>
|
||||
{authType === 'key' && (
|
||||
<div>
|
||||
<Label className="text-xs dg-text-secondary">{t('agent.agentwizard.s749be3')}</Label>
|
||||
<Label className="text-xs dg-text-secondary">SSH 私钥(PEM 格式)</Label>
|
||||
<textarea
|
||||
value={sshKey}
|
||||
onChange={e => setSshKey(e.target.value)}
|
||||
@@ -362,7 +360,7 @@ export function AgentWizard({ open, onOpenChange, server, onStart, onInstalled,
|
||||
) : (
|
||||
<>
|
||||
<Zap className="h-4 w-4 mr-2" />
|
||||
{needsCreds ? t('agent.agentwizard.sce2f53') : t('agent.agentwizard.s9866eb')}
|
||||
{needsCreds ? '连接并安装 Agent(凭据将自动保存)' : '一键安装 Agent'}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
ResponsiveContainer,
|
||||
} from 'recharts';
|
||||
import type { JsonData } from '@/types/common';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function HostDetailDialog({
|
||||
host,
|
||||
@@ -41,7 +40,6 @@ export function HostDetailDialog({
|
||||
onClose: () => void;
|
||||
onCommand: (hostname: string) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [history, setHistory] = useState<JsonData[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [hours, setHours] = useState(1);
|
||||
@@ -80,21 +78,21 @@ export function HostDetailDialog({
|
||||
<Server className="h-5 w-5 dg-text-blue" />
|
||||
{host.hostname}
|
||||
{online ? (
|
||||
<Badge className="bg-emerald-500/15 dg-text-green border border-emerald-500/30">{t('common.online')}</Badge>
|
||||
<Badge className="bg-emerald-500/15 dg-text-green border border-emerald-500/30">在线</Badge>
|
||||
) : (
|
||||
<Badge className="bg-red-500/15 dg-text-red border border-red-500/30">{t('common.offline')}</Badge>
|
||||
<Badge className="bg-red-500/15 dg-text-red border border-red-500/30">离线</Badge>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogDescription className="sr-only">{t('agent.hostdetaildialog.sffa4f7')}</DialogDescription>
|
||||
<DialogDescription className="sr-only">查看主机详情和指标趋势</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* 基本信息 */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 text-sm">
|
||||
<InfoCell label=t('agent.agentIp') value={host.ip || '--'} />
|
||||
<InfoCell label=t('server.csv.os') value={host.os || host.platform || '--'} />
|
||||
<InfoCell label=t('agent.agentLastSeen') value={timeAgo(host.last_report || host.last_reported_at || host.updated_at)} />
|
||||
<InfoCell label=t('agent.hostdetaildialog.s6ad4fc') value={String(ports.length)} />
|
||||
<InfoCell label="IP 地址" value={host.ip || '--'} />
|
||||
<InfoCell label="操作系统" value={host.os || host.platform || '--'} />
|
||||
<InfoCell label="最后上报" value={timeAgo(host.last_report || host.last_reported_at || host.updated_at)} />
|
||||
<InfoCell label="端口数" value={String(ports.length)} />
|
||||
</div>
|
||||
|
||||
{/* 趋势图 */}
|
||||
@@ -109,9 +107,9 @@ export function HostDetailDialog({
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="dg-input">
|
||||
<SelectItem value="1">{t('agent.hostdetaildialog.s3851af')}</SelectItem>
|
||||
<SelectItem value="6">{t('agent.hostdetaildialog.s2d59b1')}</SelectItem>
|
||||
<SelectItem value="24">{t('agent.hostdetaildialog.s768a1b')}</SelectItem>
|
||||
<SelectItem value="1">最近 1 小时</SelectItem>
|
||||
<SelectItem value="6">最近 6 小时</SelectItem>
|
||||
<SelectItem value="24">最近 24 小时</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
@@ -121,7 +119,7 @@ export function HostDetailDialog({
|
||||
<Loader2 className="h-5 w-5 animate-spin dg-text-muted" />
|
||||
</div>
|
||||
) : chartData.length === 0 ? (
|
||||
<div className="h-full flex items-center justify-center text-sm dg-text-muted">{t('agent.hostdetaildialog.sdc73db')}</div>
|
||||
<div className="h-full flex items-center justify-center text-sm dg-text-muted">暂无历史数据</div>
|
||||
) : (
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<AreaChart data={chartData} margin={{ top: 5, right: 5, bottom: 0, left: -20 }}>
|
||||
@@ -146,8 +144,8 @@ export function HostDetailDialog({
|
||||
contentStyle={{ backgroundColor: '#0f172a', border: '1px solid #1e293b', borderRadius: '6px', color: '#f8fafc', fontSize: '12px' }}
|
||||
/>
|
||||
<Area type="monotone" dataKey="cpu" stroke="#3b82f6" fill="url(#gCpu)" name="CPU" />
|
||||
<Area type="monotone" dataKey="mem" stroke="#10b981" fill="url(#gMem)" name=t('k8s.pods.memory') />
|
||||
<Area type="monotone" dataKey="disk" stroke="#f59e0b" fill="url(#gDisk)" name=t('dashboard.topUsage.disk') />
|
||||
<Area type="monotone" dataKey="mem" stroke="#10b981" fill="url(#gMem)" name="内存" />
|
||||
<Area type="monotone" dataKey="disk" stroke="#f59e0b" fill="url(#gDisk)" name="磁盘" />
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
)}
|
||||
@@ -161,7 +159,7 @@ export function HostDetailDialog({
|
||||
端口发现 ({ports.length})
|
||||
</span>
|
||||
{ports.length === 0 ? (
|
||||
<p className="text-xs dg-text-muted">{t('agent.hostdetaildialog.se7887d')}</p>
|
||||
<p className="text-xs dg-text-muted">暂无端口数据</p>
|
||||
) : (
|
||||
<div className="flex flex-wrap gap-1.5 max-h-32 overflow-y-auto">
|
||||
{ports.map((p, idx) => (
|
||||
@@ -193,7 +191,6 @@ export function HostDetailDialog({
|
||||
}
|
||||
|
||||
function InfoCell({ label, value }: { label: string; value: string }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-lg px-3 py-2">
|
||||
<div className="text-xs dg-text-muted">{label}</div>
|
||||
|
||||
@@ -5,43 +5,42 @@ import {
|
||||
Activity, Gauge, Timer,
|
||||
} from 'lucide-react';
|
||||
import type { JsonData } from '@/types/common';
|
||||
import i18n from '@/i18n';
|
||||
|
||||
// 主机监控指标(与后端 alert_engine 对齐)// Logan.Li
|
||||
export const METRIC_OPTIONS = [
|
||||
{ value: 'cpu', label: i18n.t('monitor.hostCpu'), icon: Cpu },
|
||||
{ value: 'cpu_iowait', label: i18n.t('agent.agentalerthelper.sc1eec3'), icon: Cpu },
|
||||
{ value: 'mem', label: i18n.t('monitor.hostMemory'), icon: MemoryStick },
|
||||
{ value: 'mem_available', label: i18n.t('agent.agentalerthelper.sa23fcd'), icon: MemoryStick },
|
||||
{ value: 'swap', label: i18n.t('agent.agentalerthelper.sa2f910'), icon: MemoryStick },
|
||||
{ value: 'load1', label: i18n.t('agent.agentalerthelper.sf23ce8'), icon: Gauge },
|
||||
{ value: 'disk', label: i18n.t('monitor.hostDisk'), icon: HardDrive },
|
||||
{ value: 'disk_inode', label: i18n.t('agent.agentalerthelper.sf7eade'), icon: HardDrive },
|
||||
{ value: 'disk_io_util', label: i18n.t('agent.agentalerthelper.s565a0e'), icon: HardDrive },
|
||||
{ value: 'tcp_established', label: i18n.t('agent.agentalerthelper.s6d9639'), icon: Network },
|
||||
{ value: 'cpu', label: 'CPU 使用率', icon: Cpu },
|
||||
{ value: 'cpu_iowait', label: 'CPU IO 等待', icon: Cpu },
|
||||
{ value: 'mem', label: '内存使用率', icon: MemoryStick },
|
||||
{ value: 'mem_available', label: '可用内存(GB)', icon: MemoryStick },
|
||||
{ value: 'swap', label: 'Swap 使用率', icon: MemoryStick },
|
||||
{ value: 'load1', label: '1分钟负载', icon: Gauge },
|
||||
{ value: 'disk', label: '磁盘使用率', icon: HardDrive },
|
||||
{ value: 'disk_inode', label: 'Inode 使用率', icon: HardDrive },
|
||||
{ value: 'disk_io_util', label: '磁盘 IO 利用率', icon: HardDrive },
|
||||
{ value: 'tcp_established', label: 'TCP 已建立', icon: Network },
|
||||
{ value: 'tcp_time_wait', label: 'TCP TIME_WAIT', icon: Network },
|
||||
{ value: 'tcp_close_wait', label: 'TCP CLOSE_WAIT', icon: Network },
|
||||
{ value: 'net_rx_bps', label: i18n.t('monitor.hostNetIn'), icon: Network },
|
||||
{ value: 'net_tx_bps', label: i18n.t('monitor.hostNetOut'), icon: Network },
|
||||
{ value: 'net_rx_errors', label: i18n.t('agent.agentalerthelper.s8d73bf'), icon: Network },
|
||||
{ value: 'fd', label: i18n.t('monitor.hostFileDescriptor'), icon: Activity },
|
||||
{ value: 'offline', label: i18n.t('agent.agentalerthelper.s8ed49a'), icon: WifiOff },
|
||||
{ value: 'port_down', label: i18n.t('agent.agentalerthelper.sf62ce6'), icon: Network },
|
||||
{ value: 'health_probe', label: i18n.t('kubernetes.container.probes'), icon: Timer },
|
||||
{ value: 'net_rx_bps', label: '入网带宽', icon: Network },
|
||||
{ value: 'net_tx_bps', label: '出网带宽', icon: Network },
|
||||
{ value: 'net_rx_errors', label: '入网错误', icon: Network },
|
||||
{ value: 'fd', label: '文件描述符', icon: Activity },
|
||||
{ value: 'offline', label: '主机离线', icon: WifiOff },
|
||||
{ value: 'port_down', label: '端口不通', icon: Network },
|
||||
{ value: 'health_probe', label: '健康探针', icon: Timer },
|
||||
];
|
||||
|
||||
export const OPERATOR_OPTIONS = [
|
||||
{ value: '>', label: i18n.t('agent.agentalerthelper.s552612') },
|
||||
{ value: '<', label: i18n.t('agent.agentalerthelper.sdd29ed') },
|
||||
{ value: '>=', label: i18n.t('agent.agentalerthelper.sef2679') },
|
||||
{ value: '<=', label: i18n.t('agent.agentalerthelper.s847d07') },
|
||||
{ value: '==', label: i18n.t('agent.agentalerthelper.s9cd6c2') },
|
||||
{ value: '>', label: '> 大于' },
|
||||
{ value: '<', label: '< 小于' },
|
||||
{ value: '>=', label: '≥ 大于等于' },
|
||||
{ value: '<=', label: '≤ 小于等于' },
|
||||
{ value: '==', label: '= 等于' },
|
||||
];
|
||||
|
||||
export const SEVERITY_OPTIONS = [
|
||||
{ value: 'critical', label: i18n.t('monitor.critical'), color: 'bg-red-500/15 dg-text-red border-red-500/30' },
|
||||
{ value: 'warning', label: i18n.t('common.warning'), color: 'bg-amber-500/15 dg-text-yellow border-amber-500/30' },
|
||||
{ value: 'info', label: i18n.t('common.info'), color: 'bg-blue-500/15 dg-text-blue border-blue-500/30' },
|
||||
{ value: 'critical', label: '严重', color: 'bg-red-500/15 dg-text-red border-red-500/30' },
|
||||
{ value: 'warning', label: '警告', color: 'bg-amber-500/15 dg-text-yellow border-amber-500/30' },
|
||||
{ value: 'info', label: '提示', color: 'bg-blue-500/15 dg-text-blue border-blue-500/30' },
|
||||
];
|
||||
|
||||
export function getMetricLabel(metric: string) {
|
||||
|
||||
Reference in new issue
Block a user