Files
aiops/frontend/src/components/agent/AgentWizard.tsx
T

251 lines
9.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from 'react';
import { toast } from 'sonner';
import { useUserStore } from '@/store';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Button } from '@/components/ui/button';
import { Copy, Check, Terminal, Download, Settings, Play, Cpu } from 'lucide-react';
interface AgentWizardProps {
open: boolean;
onOpenChange: (open: boolean) => void;
}
/** 自动检测服务器地址:使用当前浏览器访问的地址(协议+主机+端口) */
function detectServerAddress(): string {
if (typeof window !== 'undefined' && window.location) {
// 直接用当前浏览器的 origin(如 http://10.10.10.150),
// Nginx 会将 /agent/ 请求代理到后端
return window.location.origin;
}
return 'http://127.0.0.1:8080';
}
export function AgentWizard({ open, onOpenChange }: AgentWizardProps) {
const token = useUserStore((s) => s.token) || '<YOUR_TOKEN>';
const serverUrl = detectServerAddress();
const [copied, setCopied] = useState(false);
const [showManualInput, setShowManualInput] = useState(false);
const installCommand = `curl -fsSL ${serverUrl.replace(/^http:/, 'http:')}/agent/install.sh | bash -s -- --server ${serverUrl} --token ${token}`;
const fallbackCopy = (text: string): boolean => {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.left = '-9999px';
textArea.style.top = '-9999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
let success = false;
try {
<<<<<<< HEAD
// 优先使用现代 Clipboard API(需要 HTTPS 或 localhost)
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(installCommand);
} else {
// 降级方案:用隐藏 textarea + execCommand('copy'),兼容 HTTP 环境
const textarea = document.createElement('textarea');
textarea.value = installCommand;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
textarea.style.pointerEvents = 'none';
document.body.appendChild(textarea);
textarea.select();
const ok = document.execCommand('copy');
document.body.removeChild(textarea);
if (!ok) throw new Error('execCommand failed');
}
setCopied(true);
toast.success('安装命令已复制');
setTimeout(() => setCopied(false), 2000);
} catch {
toast.error('复制失败,请手动选中复制');
=======
success = document.execCommand('copy');
} catch {
// execCommand also failed
}
document.body.removeChild(textArea);
return success;
};
const handleCopy = async () => {
const cmd = installCommand;
let success = false;
// 方式1: Clipboard API(HTTPS / localhost)
try {
await navigator.clipboard.writeText(cmd);
success = true;
} catch {
// Clipboard API 不可用
}
// 方式2: execCommand fallback(HTTP 也可用)
if (!success) {
success = fallbackCopy(cmd);
}
if (success) {
setCopied(true);
toast.success('安装命令已复制到剪贴板,粘贴到目标主机执行');
setTimeout(() => setCopied(false), 3000);
} else {
toast.error('自动复制失败,请在下方代码框中手动全选复制 (Ctrl+A → Ctrl+C)');
// 切换到手选模式,展示可编辑文本框
setShowManualInput(true);
>>>>>>> ca18ead (feat: run.sh四模式(dev/docker/build/binary) + CLI flags + Redka内嵌Redis + Agent复制修复 + 服务器编辑IP字段 + 安装Agent按钮)
}
};
const binaryUrl = `${serverUrl}/agent/aiops-agent-linux-amd64`;
const manualSteps = [
{
icon: Download,
title: '1. 下载二进制',
commands: [
`curl -fsSL ${binaryUrl} -o /usr/local/bin/aiops-agent`,
],
},
{
icon: Settings,
title: '2. 赋予执行权限',
commands: ['chmod +x /usr/local/bin/aiops-agent'],
},
{
icon: Cpu,
title: '3. 创建 systemd 服务',
commands: [
`cat > /etc/systemd/system/aiops-agent.service << 'EOF'`,
`[Unit]`,
`Description=AIOps Agent`,
`After=network.target`,
``,
`[Service]`,
`Type=simple`,
`ExecStart=/usr/local/bin/aiops-agent --server ${serverUrl} --token ${token}`,
`Restart=always`,
`RestartSec=5`,
``,
`[Install]`,
`WantedBy=multi-user.target`,
`EOF`,
],
},
{
icon: Play,
title: '4. 启动并设置开机自启',
commands: [
'systemctl daemon-reload',
'systemctl enable --now aiops-agent',
'systemctl status aiops-agent',
],
},
];
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-2xl bg-slate-900 border-slate-800 text-slate-100">
<DialogHeader>
<DialogTitle className="text-slate-100">部署 Agent</DialogTitle>
<DialogDescription className="text-slate-400">
在目标主机上执行以下命令即可完成 Agent 安装与注册
</DialogDescription>
</DialogHeader>
<Tabs defaultValue="auto" className="w-full">
<TabsList className="bg-slate-800 border border-slate-700">
<TabsTrigger value="auto" className="data-[state=active]:bg-slate-700">
<Terminal className="h-4 w-4 mr-1.5" />
安装Agent
</TabsTrigger>
<TabsTrigger value="manual" className="data-[state=active]:bg-slate-700">
手动安装
</TabsTrigger>
</TabsList>
{/* 自动安装 */}
<TabsContent value="auto" className="mt-4 space-y-3">
<p className="text-sm text-slate-400">
在目标主机执行以下命令,Agent 将自动下载、安装并启动:
</p>
<div className="relative rounded-lg bg-slate-950 border border-slate-800 p-4">
<pre className="text-xs text-emerald-400 font-mono whitespace-pre-wrap break-all pr-10">
{installCommand}
</pre>
<Button
size="icon"
variant="ghost"
onClick={handleCopy}
className="absolute top-2 right-2 h-8 w-8 text-slate-400 hover:text-slate-100 hover:bg-slate-800"
title="复制到剪贴板"
>
{copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
</Button>
</div>
{/* 手动复制模式:当自动复制失败时展示可全选的文本域 */}
{showManualInput && (
<div className="rounded-lg bg-amber-950/30 border border-amber-800/50 p-4">
<p className="text-xs text-amber-400 mb-2">
⚠️ 自动复制失败。请点击下方输入框全选 (Ctrl+A) 后复制 (Ctrl+C):
</p>
<textarea
readOnly
value={installCommand}
className="w-full h-24 bg-slate-950 border border-amber-800/30 text-emerald-400 font-mono text-xs p-3 rounded resize-none focus:outline-none focus:border-amber-600"
onClick={(e) => (e.target as HTMLTextAreaElement).select()}
/>
</div>
)}
<div className="flex items-start gap-2 text-xs text-slate-500">
<Terminal className="h-3.5 w-3.5 mt-0.5 shrink-0" />
<span>
服务器地址已自动检测为当前访问地址({serverUrl})。Agent 安装成功后将自动连接并开始上报指标。
</span>
</div>
</TabsContent>
{/* 手动安装 */}
<TabsContent value="manual" className="mt-4 space-y-4">
<div className="max-h-[420px] overflow-y-auto space-y-3 pr-1">
{manualSteps.map((step) => (
<div key={step.title} className="rounded-lg bg-slate-950 border border-slate-800 overflow-hidden">
<div className="flex items-center gap-2 px-4 py-2.5 bg-slate-800/50">
<step.icon className="h-4 w-4 text-blue-400" />
<span className="text-sm font-medium text-slate-200">{step.title}</span>
</div>
<div className="p-3 space-y-1">
{step.commands.map((cmd, idx) => (
<div key={idx} className="flex items-start gap-2">
<span className="text-slate-600 font-mono text-xs select-none mt-0.5">$</span>
<code className="text-xs text-slate-300 font-mono whitespace-pre-wrap break-all flex-1">
{cmd}
</code>
</div>
))}
</div>
</div>
))}
</div>
<p className="text-xs text-slate-500">
完成上述步骤后,Agent 将作为 systemd 服务运行并自动上报指标。
</p>
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
);
}
export default AgentWizard;