mirror of
https://github.com/Awuqing/BackupX.git
synced 2026-05-11 18:10:23 +08:00
* 修复: #46 Agent 一键安装脚本在 Debian dash 下执行失败 根因(多因素,任何一个都可能导致用户复现的 "sh: 2: Syntax error: newline unexpected"): - Debian/Ubuntu 默认 /bin/sh → dash;pipe 方式下 shebang 被忽略 - Content-Type: text/x-shellscript 会触发部分 CDN/反向代理的脚本识别与改写 - 如果响应被改写为 HTML,sh 在第 2 行(<html>)即报此语法错误 修复: 1. 前端命令改为 `curl -fsSL URL | sudo bash`(避开 dash) 2. 命令面板增加"先下载再执行"备用命令(代理过滤场景兜底) 3. install handler Content-Type 改为 text/plain;加 nosniff / no-store / Content-Disposition 三头,减少中间层改写的概率 4. 脚本模板加 magic marker `BACKUPX_AGENT_INSTALL_V1`,用户可通过 `head -3` 自查响应完整性;加 bash 自举段,文件执行时优先切到 bash 测试: - installscript/issue46_test.go 断言 magic + bash-bootstrap 存在于三种模式 - install_flow_test.go 断言新 headers 与 marker - go test ./... 全绿,前端 build 通过 * 修复: #46 用户截图证实 nginx SPA fallback 返回 index.html 用户反馈截图显示 curl 下载到的是 BackupX 前端 HTML,而非 shell 脚本—— 说明 /install/:token 未被反向代理转发到后端,nginx 按 try_files fallback 到 /index.html,sh 读第 2 行 <html> 报语法错误。 真正的根因修复: 1. 后端 install 端点额外暴露 /api/install/:token 别名,让反向代理 已有的 /api/ 转发规则自动接管 2. 节点创建时返回的 url/composeUrl 统一使用 /api/install/ 前缀 3. 更新 deploy/nginx.conf 模板: - 新增 location /install/ 转发(兼容旧版本生成的命令) - 新增 /health /ready /metrics 单独转发,避免 SPA fallback 测试: - install_flow_test.go 新增 TestInstallScriptAliasUnderAPI 断言 /api/install/:token 路径可用 + 新生成的 url 用 /api/install/ 前缀
117 lines
4.1 KiB
Bash
117 lines
4.1 KiB
Bash
#!/bin/sh
|
||
# BackupX Agent 一键安装脚本(由 Master 动态渲染)
|
||
# Magic: BACKUPX_AGENT_INSTALL_V1 —— 若 `head -3 脚本` 看不到此行,说明反向代理/CDN 改写了响应
|
||
# 模式: {{.Mode}} | 架构: {{.Arch}} | 版本: {{.AgentVersion}}
|
||
set -eu
|
||
|
||
# 自举到 bash(文件执行模式下生效;管道模式 $0 不是文件,exec 会静默失败,继续用 sh)。
|
||
# 动机:部分 Debian/Ubuntu 用户通过 `curl | sudo sh` 触发时,dash 对本脚本报语法错误;
|
||
# 若目标机装有 bash,优先切换到 bash 获得更一致的行为。
|
||
if [ -z "${BASH_VERSION:-}" ] && command -v bash >/dev/null 2>&1 && [ -f "$0" ]; then
|
||
exec bash "$0" "$@"
|
||
fi
|
||
|
||
MASTER_URL="{{.MasterURL}}"
|
||
AGENT_TOKEN="{{.AgentToken}}"
|
||
AGENT_VERSION="{{.AgentVersion}}"
|
||
DOWNLOAD_BASE="{{.DownloadBase}}"
|
||
INSTALL_PREFIX="{{.InstallPrefix}}"
|
||
ARCH="{{.Arch}}"
|
||
|
||
# 1. 前置检查
|
||
[ "$(id -u)" -eq 0 ] || { echo "请使用 root 或 sudo 执行" >&2; exit 1; }
|
||
command -v curl >/dev/null || command -v wget >/dev/null \
|
||
|| { echo "需要 curl 或 wget" >&2; exit 1; }
|
||
{{if eq .Mode "systemd"}}command -v systemctl >/dev/null || { echo "不支持非 systemd 系统" >&2; exit 1; }
|
||
{{end}}{{if eq .Mode "docker"}}command -v docker >/dev/null || { echo "需要先安装 docker" >&2; exit 1; }
|
||
{{end}}
|
||
# 2. 架构检测
|
||
if [ "$ARCH" = "auto" ]; then
|
||
case "$(uname -m)" in
|
||
x86_64|amd64) ARCH=amd64 ;;
|
||
aarch64|arm64) ARCH=arm64 ;;
|
||
*) echo "不支持的架构: $(uname -m)" >&2; exit 1 ;;
|
||
esac
|
||
fi
|
||
|
||
{{if ne .Mode "docker"}}
|
||
# 3. 下载二进制(systemd / foreground 模式)
|
||
ARCHIVE="backupx-${AGENT_VERSION}-linux-${ARCH}.tar.gz"
|
||
URL="${DOWNLOAD_BASE}/${AGENT_VERSION}/${ARCHIVE}"
|
||
TMPDIR="$(mktemp -d)"; trap 'rm -rf "$TMPDIR"' EXIT
|
||
echo "[1/4] 下载 ${URL}"
|
||
if command -v curl >/dev/null; then
|
||
curl -fsSL "$URL" -o "$TMPDIR/pkg.tar.gz"
|
||
else
|
||
wget -qO "$TMPDIR/pkg.tar.gz" "$URL"
|
||
fi
|
||
tar xzf "$TMPDIR/pkg.tar.gz" -C "$TMPDIR"
|
||
|
||
# 4. 安装二进制 + 用户
|
||
echo "[2/4] 安装到 ${INSTALL_PREFIX}"
|
||
id backupx >/dev/null 2>&1 || useradd --system --home-dir "$INSTALL_PREFIX" --shell /usr/sbin/nologin backupx
|
||
install -d -o backupx -g backupx "$INSTALL_PREFIX" /var/lib/backupx-agent
|
||
install -m 0755 "$TMPDIR/backupx-${AGENT_VERSION}-linux-${ARCH}/backupx" "$INSTALL_PREFIX/backupx"
|
||
{{end}}
|
||
|
||
{{if eq .Mode "systemd"}}
|
||
# 5. systemd unit
|
||
echo "[3/4] 配置 systemd"
|
||
cat > /etc/systemd/system/backupx-agent.service <<UNIT
|
||
[Unit]
|
||
Description=BackupX Agent
|
||
After=network-online.target
|
||
Wants=network-online.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=backupx
|
||
Environment="BACKUPX_AGENT_MASTER=${MASTER_URL}"
|
||
Environment="BACKUPX_AGENT_TOKEN=${AGENT_TOKEN}"
|
||
ExecStart=${INSTALL_PREFIX}/backupx agent --temp-dir /var/lib/backupx-agent
|
||
Restart=on-failure
|
||
RestartSec=10s
|
||
NoNewPrivileges=true
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
UNIT
|
||
systemctl daemon-reload
|
||
systemctl enable --now backupx-agent
|
||
|
||
# 6. 等待上线
|
||
echo "[4/4] 等待节点上线"
|
||
for i in $(seq 1 15); do
|
||
sleep 2
|
||
if curl -fsSL -H "X-Agent-Token: ${AGENT_TOKEN}" "${MASTER_URL}/api/v1/agent/self" 2>/dev/null \
|
||
| grep -q '"status":"online"'; then
|
||
echo "✓ 节点已上线"
|
||
exit 0
|
||
fi
|
||
done
|
||
echo "⚠ 30s 内未收到上线心跳,请检查防火墙或 journalctl -u backupx-agent"
|
||
exit 2
|
||
{{end}}
|
||
|
||
{{if eq .Mode "foreground"}}
|
||
# 5. 前台运行
|
||
echo "[3/3] 前台启动 agent(Ctrl+C 退出)"
|
||
export BACKUPX_AGENT_MASTER="${MASTER_URL}"
|
||
export BACKUPX_AGENT_TOKEN="${AGENT_TOKEN}"
|
||
exec "${INSTALL_PREFIX}/backupx" agent --temp-dir /var/lib/backupx-agent
|
||
{{end}}
|
||
|
||
{{if eq .Mode "docker"}}
|
||
# Docker 模式:直接用镜像启动容器
|
||
echo "[1/2] 拉取镜像 awuqing/backupx:${AGENT_VERSION}"
|
||
docker pull "awuqing/backupx:${AGENT_VERSION}"
|
||
echo "[2/2] 启动容器 backupx-agent"
|
||
docker rm -f backupx-agent >/dev/null 2>&1 || true
|
||
docker run -d --name backupx-agent --restart=unless-stopped \
|
||
-e "BACKUPX_AGENT_MASTER=${MASTER_URL}" \
|
||
-e "BACKUPX_AGENT_TOKEN=${AGENT_TOKEN}" \
|
||
-v /var/lib/backupx-agent:/tmp/backupx-agent \
|
||
"awuqing/backupx:${AGENT_VERSION}" agent
|
||
echo "✓ 容器已启动"
|
||
{{end}}
|