#!/usr/bin/env bash set -Eeuo pipefail REPO="${NG_REPO:-rogue/NexusGate}" BRANCH="${NG_BRANCH:-main}" # 自建 Gitea 分发基址(raw / release 均由此拼装) GIT_BASE="https://git.881218.xyz/${REPO}" # Go agent 二进制所在的 Gitea Release tag(与 agent-install.sh 保持一致) AGENT_RELEASE="${NG_AGENT_RELEASE:-agent-v0.6.8}" die() { printf '错误:%s\n' "$*" >&2; exit 1; } info() { printf '\033[1;36m[NexusGate Agent]\033[0m %s\n' "$*"; } [[ "${EUID}" -eq 0 ]] || die "请使用 root 运行" [[ -f /etc/nexusgate/agent.env ]] || die "未检测到已安装的 NexusGate Agent" command -v curl >/dev/null || die "缺少 curl" tmp_dir="$(mktemp -d /tmp/nexusgate-agent-update.XXXXXX)" trap 'rm -rf -- "$tmp_dir"' EXIT # 按已安装的程序判断当前实现:Go 二进制优先,其次是旧版 Node agent.js if [[ -x /opt/nexusgate-agent/nexusgate-agent ]]; then CURRENT_IMPL="go" elif [[ -f /opt/nexusgate-agent/agent.js ]]; then CURRENT_IMPL="node" else die "未找到 Agent 程序(/opt/nexusgate-agent/nexusgate-agent 或 agent.js)" fi # 目标实现:更新默认迁移到 Go;显式 NG_AGENT_IMPL=node 时保留 Node TARGET_IMPL="go" [[ "${NG_AGENT_IMPL:-}" == "node" ]] && TARGET_IMPL="node" info "当前 Agent 实现:${CURRENT_IMPL};更新目标:${TARGET_IMPL}" raw_fetch() { curl -fL --retry 3 "${GIT_BASE}/raw/branch/${BRANCH}/$1" -o "$2" } agent_arch() { case "$(uname -m)" in x86_64|amd64) printf 'amd64' ;; aarch64|arm64) printf 'arm64' ;; *) return 1 ;; esac } # 下载 Go agent 二进制到 $1(NG_LOCAL_SRC 时从 agent-go/ 取已构建产物) fetch_agent_binary() { local arch out="$1" arch="$(agent_arch)" || die "不支持的架构:$(uname -m)" if [[ -n "${NG_LOCAL_SRC:-}" && -f "${NG_LOCAL_SRC}/agent-go/nexusgate-agent-linux-${arch}" ]]; then cp -a "${NG_LOCAL_SRC}/agent-go/nexusgate-agent-linux-${arch}" "$out" else curl -fL --retry 3 "${GIT_BASE}/releases/download/${AGENT_RELEASE}/nexusgate-agent-linux-${arch}" -o "$out" fi [[ -s "$out" ]] || die 'Agent 二进制下载不完整' } raw_fetch scripts/agent-uninstall.sh "$tmp_dir/uninstall.sh" raw_fetch scripts/agent-doctor.sh "$tmp_dir/doctor.sh" raw_fetch scripts/agent-cert.sh "$tmp_dir/cert.sh" raw_fetch scripts/agent-update.sh "$tmp_dir/update.sh" raw_fetch scripts/agent-singbox.sh "$tmp_dir/singbox.sh" raw_fetch scripts/agent-logrotate.conf "$tmp_dir/logrotate.conf" raw_fetch scripts/agent-logrotate-setup.sh "$tmp_dir/logrotate-setup.sh" [[ -s "$tmp_dir/update.sh" ]] || die 'Agent 升级脚本下载不完整' if [[ "$TARGET_IMPL" == "go" ]]; then fetch_agent_binary "$tmp_dir/nexusgate-agent" else raw_fetch agent/agent.js "$tmp_dir/agent.js" raw_fetch agent/run.sh "$tmp_dir/run.sh" node --check "$tmp_dir/agent.js" fi # Updating restarts sing-box. Persist its current bidirectional counters # before the running process is replaced. ( set -a; source /etc/nexusgate/agent.env; set +a if [[ "$CURRENT_IMPL" == "go" ]]; then /opt/nexusgate-agent/nexusgate-agent flush-usage else node /opt/nexusgate-agent/agent.js flush-usage fi ) || die '更新前流量统计上报失败,保留原 Agent;检查控制面连接和 ng-agent doctor 后重试' if [[ "$TARGET_IMPL" == "go" ]]; then install -m 0755 "$tmp_dir/nexusgate-agent" /opt/nexusgate-agent/nexusgate-agent else install -m 0644 "$tmp_dir/agent.js" /opt/nexusgate-agent/agent.js install -m 0755 "$tmp_dir/run.sh" /opt/nexusgate-agent/run.sh fi install -m 0755 "$tmp_dir/uninstall.sh" /usr/local/sbin/ng-agent-uninstall install -m 0755 "$tmp_dir/doctor.sh" /usr/local/sbin/ng-agent-doctor install -m 0755 "$tmp_dir/cert.sh" /usr/local/sbin/ng-agent-cert install -m 0755 "$tmp_dir/singbox.sh" /usr/local/sbin/ng-agent-singbox install -m 0644 "$tmp_dir/logrotate.conf" /etc/nexusgate/agent-logrotate.conf install -m 0755 "$tmp_dir/logrotate-setup.sh" /usr/local/sbin/ng-agent-logrotate-setup install -m 0755 "$tmp_dir/update.sh" /usr/local/sbin/ng-agent-update.next mv -f -- /usr/local/sbin/ng-agent-update.next /usr/local/sbin/ng-agent-update cat > /usr/local/sbin/ng-agent <<'EOF' #!/usr/bin/env bash set -Eeuo pipefail case "${1:-}" in update) exec ng-agent-update ;; uninstall) shift; exec ng-agent-uninstall "$@" ;; doctor|status) exec ng-agent-doctor ;; cert) shift; exec ng-agent-cert "$@" ;; engine) shift; exec ng-agent-singbox "$@" ;; *) printf 'NexusGate Agent: ng-agent doctor | ng-agent cert | ng-agent engine install | ng-agent update | ng-agent uninstall\n' ;; esac EOF chmod 0755 /usr/local/sbin/ng-agent # sing-box 是唯一的入口引擎;缺失时直接安装。 if [[ ! -x /usr/local/bin/nexusgate-sing-box || ! -x /usr/local/bin/nexusgate-sing-box-stats ]]; then NG_REPO="$REPO" NG_BRANCH="$BRANCH" ng-agent-singbox install \ || die 'sing-box 引擎安装失败;修复后运行 ng-agent engine install 重试' fi if command -v systemctl >/dev/null && [[ -d /run/systemd/system ]]; then raw_fetch systemd/nexusgate-agent.service "$tmp_dir/nexusgate-agent.service" raw_fetch systemd/nexusgate-sing-box.service "$tmp_dir/nexusgate-sing-box.service" install -m 0644 "$tmp_dir/nexusgate-agent.service" /etc/systemd/system/nexusgate-agent.service install -m 0644 "$tmp_dir/nexusgate-sing-box.service" /etc/systemd/system/nexusgate-sing-box.service if [[ "$TARGET_IMPL" == "node" ]]; then # 仓库里的 unit 默认指向 Go 二进制;Node 实现改为经 run.sh 启动 sed -i 's|^ExecStart=.*|ExecStart=/opt/nexusgate-agent/run.sh|' /etc/systemd/system/nexusgate-agent.service fi systemctl daemon-reload rm -f -- /etc/nexusgate/last-heartbeat.json systemctl restart nexusgate-agent.service systemctl is-active --quiet nexusgate-agent.service || die "Agent 重启失败" elif command -v rc-service >/dev/null; then raw_fetch openrc/nexusgate-agent "$tmp_dir/openrc-agent" raw_fetch openrc/nexusgate-sing-box "$tmp_dir/openrc-sing-box" install -m 0755 "$tmp_dir/openrc-agent" /etc/init.d/nexusgate-agent install -m 0755 "$tmp_dir/openrc-sing-box" /etc/init.d/nexusgate-sing-box if [[ "$TARGET_IMPL" == "node" ]]; then # 仓库里的 OpenRC 服务默认指向 Go 二进制;Node 实现改为经 run.sh 启动 sed -i 's|^command=.*|command="/opt/nexusgate-agent/run.sh"|' /etc/init.d/nexusgate-agent fi if [[ -e /etc/init.d/crond ]]; then rc-update add crond default >/dev/null 2>&1 || true rc-service crond start >/dev/null 2>&1 || true fi rm -f -- /etc/nexusgate/last-heartbeat.json rc-service nexusgate-agent restart rc-service nexusgate-agent status >/dev/null || die "Agent 重启失败" else die "未检测到 systemd 或 OpenRC" fi ng-agent-logrotate-setup for _ in {1..35}; do [[ -s /etc/nexusgate/last-heartbeat.json ]] && break sleep 1 done [[ -s /etc/nexusgate/last-heartbeat.json ]] || die 'Agent 已重启,但尚未连接控制面;请执行 ng-agent doctor' info "Agent 更新完成"