From 48b2d2281bf0ddf4d3197a0a5492d9dfb6310d15 Mon Sep 17 00:00:00 2001 From: attacker Date: Thu, 18 Jun 2026 07:14:19 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20AgentWizard=20URL=E6=8B=BC=E6=8E=A5?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3(origin/backend=E5=88=86=E7=A6=BB)=20+=20?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=E5=85=BC=E5=AE=B9HTTP/=E7=A7=BB=E5=8A=A8?= =?UTF-8?q?=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 10 +++ backend/.env.example | 2 +- backend/cmd/main.go | 2 +- backend/docker-compose.yml | 6 +- docker-compose.yml | 8 +- frontend/src/components/agent/AgentWizard.tsx | 76 ++++++++++--------- run.sh | 2 +- 7 files changed, 60 insertions(+), 46 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9a44757 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +# AIOps Docker Compose 环境变量 +# 复制为 .env 并修改密码 + +# MySQL 数据库密码(生产环境务必修改) +MYSQL_ROOT_PASSWORD=CHANGE_ME_ROOT_PASSWORD +MYSQL_PASSWORD=CHANGE_ME_USER_PASSWORD + +# MySQL 数据库名和用户名(通常无需修改) +MYSQL_DATABASE=aiops +MYSQL_USER=aiops diff --git a/backend/.env.example b/backend/.env.example index 9c87fe2..26630ad 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -21,5 +21,5 @@ DB_NAME=data/aiops.db # DB_NAME=aiops # JWT 配置 -JWT_SECRET=aiops-secret-key-change-in-production +JWT_SECRET=CHANGE_ME # 生产环境必须替换为随机生成的 32+ 字符密钥 JWT_EXPIRE_HOURS=24 diff --git a/backend/cmd/main.go b/backend/cmd/main.go index 294bde6..be8057f 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -225,7 +225,7 @@ func loadConfig() *config.Config { viper.SetDefault("redis.password", "") viper.SetDefault("redis.db", 0) - viper.SetDefault("jwt.secret", "aiops-jwt-secret-change-in-production") + viper.SetDefault("jwt.secret", "") // 生产环境必须通过配置或 JWT_SECRET 环境变量设置 viper.SetDefault("jwt.expire_hour", 24) viper.SetDefault("websocket.read_buffer_size", 1024) diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml index 7eccf67..0fe46e2 100644 --- a/backend/docker-compose.yml +++ b/backend/docker-compose.yml @@ -7,10 +7,10 @@ services: container_name: ezops-mysql restart: unless-stopped environment: - MYSQL_ROOT_PASSWORD: rootpassword + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-CHANGE_ME} MYSQL_DATABASE: ezops MYSQL_USER: ezops - MYSQL_PASSWORD: ezops123 + MYSQL_PASSWORD: ${MYSQL_PASSWORD:-CHANGE_ME} ports: - "3306:3306" volumes: @@ -54,7 +54,7 @@ services: - DATABASE_MYSQL_HOST=mysql - DATABASE_MYSQL_PORT=3306 - DATABASE_MYSQL_USERNAME=ezops - - DATABASE_MYSQL_PASSWORD=ezops123 + - DATABASE_MYSQL_PASSWORD=${MYSQL_PASSWORD:-CHANGE_ME} - DATABASE_MYSQL_DATABASE=ezops - REDIS_ENABLED=true - REDIS_HOST=redis diff --git a/docker-compose.yml b/docker-compose.yml index b3213c0..17f8da4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,10 +7,10 @@ services: container_name: aiops-mysql restart: unless-stopped environment: - MYSQL_ROOT_PASSWORD: rootpassword - MYSQL_DATABASE: aiops - MYSQL_USER: aiops - MYSQL_PASSWORD: aiops123 + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-CHANGE_ME} + MYSQL_DATABASE: ${MYSQL_DATABASE:-aiops} + MYSQL_USER: ${MYSQL_USER:-aiops} + MYSQL_PASSWORD: ${MYSQL_PASSWORD:-CHANGE_ME} TZ: Asia/Shanghai ports: - "3306:3306" diff --git a/frontend/src/components/agent/AgentWizard.tsx b/frontend/src/components/agent/AgentWizard.tsx index a5540bd..5c8b9f2 100644 --- a/frontend/src/components/agent/AgentWizard.tsx +++ b/frontend/src/components/agent/AgentWizard.tsx @@ -17,60 +17,64 @@ interface AgentWizardProps { onOpenChange: (open: boolean) => void; } -/** 自动检测服务器地址:使用当前浏览器访问的主机名,端口固定 8080 */ -function detectServerAddress(): string { +/** 自动检测地址:origin(通过 Nginx 80 代理)用于安装脚本下载,backend 8080 用于 agent 直连 */ +function detectServerAddress() { if (typeof window !== 'undefined' && window.location) { - const host = window.location.hostname; - return host ? `http://${host}:8080` : 'http://127.0.0.1:8080'; + return { + origin: window.location.origin, // http://10.10.10.75 → Nginx + backend: `${window.location.protocol}//${window.location.hostname}:8080`, // http://10.10.10.75:8080 → 后端直达 + }; } - return 'http://127.0.0.1:8080'; + return { origin: 'http://127.0.0.1', backend: 'http://127.0.0.1:8080' }; } export function AgentWizard({ open, onOpenChange }: AgentWizardProps) { const token = useUserStore((s) => s.token) || ''; - const serverUrl = detectServerAddress(); + const { origin, backend } = 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}`; + // install.sh 通过 Nginx 代理下载(origin:80 → /agent/ → backend:8080) + const installCommand = `curl -fsSL ${origin}/agent/install.sh | bash -s -- --server ${backend} --token ${token}`; - // 剪贴板 fallback:execCommand('copy') 兼容 HTTP 环境 // Logan.Li - const fallbackCopy = (text: string): boolean => { + const handleCopy = () => { const textArea = document.createElement('textarea'); - textArea.value = text; + textArea.value = installCommand; + // 设置样式让其可见但不可交互,确保浏览器能选中 textArea.style.position = 'fixed'; - textArea.style.left = '-9999px'; - textArea.style.top = '-9999px'; + textArea.style.top = '0'; + textArea.style.left = '0'; + textArea.style.width = '2em'; + textArea.style.height = '2em'; + textArea.style.padding = '0'; + textArea.style.border = 'none'; + textArea.style.outline = 'none'; + textArea.style.boxShadow = 'none'; + textArea.style.background = 'transparent'; + textArea.style.opacity = '0'; + textArea.style.pointerEvents = 'none'; + textArea.style.zIndex = '-1'; + textArea.style.fontSize = '16px'; + // iOS 需要 contentEditable + readOnly 兼容 + textArea.contentEditable = 'true'; + textArea.readOnly = true; document.body.appendChild(textArea); + + // 选中范围 textArea.focus(); textArea.select(); + // 兼容移动端 + if (textArea.setSelectionRange) { + textArea.setSelectionRange(0, 999999); + } + let success = false; try { success = document.execCommand('copy'); } catch { - // execCommand also failed + // execCommand failed } document.body.removeChild(textArea); - return success; - }; - - const handleCopy = async () => { - let success = false; - - // 方式1: Clipboard API(HTTPS / localhost 可用) - try { - if (navigator.clipboard && window.isSecureContext) { - await navigator.clipboard.writeText(installCommand); - success = true; - } - } catch { - // Clipboard API 不可用,尝试 fallback - } - - // 方式2: execCommand fallback(HTTP 环境也可用) - if (!success) { - success = fallbackCopy(installCommand); - } if (success) { setCopied(true); @@ -82,7 +86,7 @@ export function AgentWizard({ open, onOpenChange }: AgentWizardProps) { } }; - const binaryUrl = `${serverUrl}/agent/aiops-agent-linux-amd64`; + const binaryUrl = `${origin}/agent/aiops-agent-linux-amd64`; const manualSteps = [ { @@ -108,7 +112,7 @@ export function AgentWizard({ open, onOpenChange }: AgentWizardProps) { ``, `[Service]`, `Type=simple`, - `ExecStart=/usr/local/bin/aiops-agent --server ${serverUrl} --token ${token}`, + `ExecStart=/usr/local/bin/aiops-agent --server ${backend} --token ${token}`, `Restart=always`, `RestartSec=5`, ``, @@ -186,7 +190,7 @@ export function AgentWizard({ open, onOpenChange }: AgentWizardProps) {
- 服务器地址已自动检测为当前访问地址({serverUrl})。Agent 安装成功后将自动连接并开始上报指标。 + 服务器地址已自动检测 — 安装脚本通过 Nginx 代理 ({origin}),Agent 直连后端 ({backend})。安装成功后将自动连接并开始上报指标。
diff --git a/run.sh b/run.sh index ee72f5b..7c5ff39 100755 --- a/run.sh +++ b/run.sh @@ -379,7 +379,7 @@ redis: db: 0 jwt: - secret: "aiops-jwt-secret-change-in-production" + secret: "CHANGE_ME_JWT_SECRET" # ⚠️ 生产环境必须替换 expire_hour: 24 websocket: