Files
aiops/frontend/src/components/automation/script-helpers.tsx
T
attacker c68aef4444 feat(alert): A2 告警引擎分布式安全 — Redis 分布式锁(SetNX)
- Cache 接口新增 SetNX 方法(go-redis 原子 / redka check-then-set 单副本安全)
- AlertEngine 改造为支持分布式锁: tryAcquireLock/releaseLock/GetEngineStatus
- 多副本部署时仅 Leader 执行告警检查, 60s TTL 防死锁, fail-open 容错
- 新增 GET /alerts/engine/status 监控端点(is_leader/instance_id/lock_holder)
- 修复 GrafanaDashboard.tsx Panel.options 类型不匹配导致构建中断
- 同步未提交的前端类型安全迁移(140+ 文件)
- ROADMAP/TDD_PLAN/PROJECT_EVALUATION 三文档同步更新

验证: go build 零错误, Docker 部署 healthy, 引擎状态端点正常,
锁竞争测试通过(非 Leader 跳过检查), 5/5 核心 API 回归通过
2026-06-26 21:13:15 +08:00

97 lines
3.3 KiB
TypeScript

// ScriptManager 共享类型与工具函数 // Logan.Li
import React from 'react';
import { Clock, Loader2, CheckCircle, XCircle } from 'lucide-react';
import { Badge } from '@/components/ui/badge';
import type { JsonData } from '@/types/common';
// ---- 类型定义 ----
export interface Script {
id: number;
name: string;
code: string;
description: string;
type: string; // shell | python | powershell | yaml
content: string;
timeout: number;
version: number;
run_count: number;
created_at: string;
updated_at: string;
}
export interface ExecutionRecord {
id: number;
task_type: string;
task_id: number;
task_name: string;
status: string;
output: string;
duration: number;
started_at: string | null;
finished_at: string | null;
created_at: string;
}
// 将后端返回的脚本原始对象映射为前端结构
export function mapScript(raw: JsonData): Script {
return {
id: raw.id,
name: raw.name,
code: raw.code || '',
description: raw.description || '',
type: raw.type || 'shell',
content: raw.content || '',
timeout: raw.timeout || 300,
version: raw.version || 1,
run_count: raw.run_count || 0,
created_at: raw.created_at,
updated_at: raw.updated_at,
};
}
export const scriptTypes = [
{ value: 'shell', label: 'Shell', sample: '#!/bin/bash\n# 系统巡检脚本\necho "===== 系统信息 ====="\nuname -a\necho ""\necho "===== 磁盘使用 ====="\ndf -h\necho ""\necho "===== 内存使用 ====="\nfree -h\n' },
{ value: 'python', label: 'Python', sample: '#!/usr/bin/env python3\n# 主机信息采集\nimport platform, socket\nprint("主机名:", socket.gethostname())\nprint("系统:", platform.platform())\nprint("Python:", platform.python_version())\n' },
];
const typeColors: Record<string, string> = {
shell: 'border-green-500/30 text-green-500 bg-green-500/10',
bash: 'border-green-500/30 text-green-500 bg-green-500/10',
python: 'border-blue-500/30 text-blue-500 bg-blue-500/10',
powershell: 'border-cyan-500/30 text-cyan-500 bg-cyan-500/10',
yaml: 'border-purple-500/30 text-purple-500 bg-purple-500/10',
};
export const getTypeBadge = (type: string) => (
<Badge variant="outline" className={typeColors[type] || typeColors.shell}>
{(type || 'shell').toUpperCase()}
</Badge>
);
// 生成唯一脚本编码
export const genCode = () => `sc-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
// 执行状态 Badge
export function getStatusBadge(status: string) {
switch (status) {
case 'pending':
return <Badge variant="outline" className="gap-1"><Clock className="w-3 h-3" />等待中</Badge>;
case 'running':
return <Badge variant="default" className="bg-blue-500 gap-1"><Loader2 className="w-3 h-3 animate-spin" />执行中</Badge>;
case 'success':
return <Badge variant="default" className="bg-green-600 gap-1"><CheckCircle className="w-3 h-3" />成功</Badge>;
case 'failed':
return <Badge variant="destructive" className="gap-1"><XCircle className="w-3 h-3" />失败</Badge>;
default:
return <Badge variant="secondary">{status || '未知'}</Badge>;
}
}
// 日期格式化
export const formatDate = (s: string | null) => {
if (!s) return '-';
const d = new Date(s);
return Number.isNaN(d.getTime()) ? '-' : d.toLocaleString('zh-CN', { hour12: false });
};