1485 lines
59 KiB
Bash
Executable File
1485 lines
59 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# ============================================
|
||
# LinkMaster 节点端一键安装脚本
|
||
# 使用方法: curl -fsSL https://gitee.nas.cpolar.top/yoyo/linkmaster-node/raw/branch/main/install.sh | bash -s -- <后端地址>
|
||
# 示例: curl -fsSL https://gitee.nas.cpolar.top/yoyo/linkmaster-node/raw/branch/main/install.sh | bash -s -- http://192.168.1.100:8080
|
||
# ============================================
|
||
|
||
set -e
|
||
|
||
# 颜色输出
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m'
|
||
|
||
# 配置
|
||
# 尝试从脚本URL自动提取仓库信息(如果通过curl下载)
|
||
SCRIPT_URL="${SCRIPT_URL:-}"
|
||
if [ -z "$SCRIPT_URL" ] && [ -n "${BASH_SOURCE[0]}" ]; then
|
||
# 如果脚本是通过 curl 下载的,尝试从环境变量获取
|
||
SCRIPT_URL="${SCRIPT_URL:-}"
|
||
fi
|
||
|
||
# 默认配置(如果无法自动提取,使用这些默认值)
|
||
GITHUB_REPO="${GITHUB_REPO:-yoyo/linkmaster-node}" # 默认仓库(独立的 node 项目)
|
||
GITHUB_BRANCH="${GITHUB_BRANCH:-main}" # 默认分支
|
||
SOURCE_DIR="/opt/linkmaster-node" # 源码目录
|
||
BINARY_NAME="linkmaster-node"
|
||
INSTALL_DIR="/usr/local/bin"
|
||
SERVICE_NAME="linkmaster-node"
|
||
|
||
# 获取后端地址参数
|
||
BACKEND_URL="${1:-}"
|
||
if [ -z "$BACKEND_URL" ]; then
|
||
echo -e "${RED}错误: 请提供后端服务器地址${NC}"
|
||
echo -e "${YELLOW}使用方法:${NC}"
|
||
echo " curl -fsSL https://gitee.nas.cpolar.top/${GITHUB_REPO}/raw/branch/${GITHUB_BRANCH}/install.sh | bash -s -- http://your-backend-server:8080"
|
||
echo ""
|
||
echo -e "${YELLOW}注意:${NC}"
|
||
echo " - 节点端需要直接连接后端服务器,不是前端地址"
|
||
echo " - 后端默认端口: 8080"
|
||
echo " - 如果节点和后端在同一服务器: http://localhost:8080"
|
||
echo " - 如果节点和后端在不同服务器: http://backend-ip:8080 或 http://backend-domain:8080"
|
||
exit 1
|
||
fi
|
||
|
||
# 检测系统类型和架构
|
||
detect_system() {
|
||
if [ -f /etc/os-release ]; then
|
||
. /etc/os-release
|
||
OS=$ID
|
||
OS_VERSION=$VERSION_ID
|
||
else
|
||
echo -e "${RED}无法检测系统类型${NC}"
|
||
exit 1
|
||
fi
|
||
|
||
ARCH=$(uname -m)
|
||
case $ARCH in
|
||
x86_64)
|
||
ARCH="amd64"
|
||
;;
|
||
aarch64|arm64)
|
||
ARCH="arm64"
|
||
;;
|
||
*)
|
||
echo -e "${RED}不支持的架构: $ARCH${NC}"
|
||
exit 1
|
||
;;
|
||
esac
|
||
|
||
echo -e "${BLUE}检测到系统: $OS $OS_VERSION ($ARCH)${NC}"
|
||
}
|
||
|
||
# 检测并选择最快的镜像源
|
||
detect_fastest_mirror() {
|
||
echo -e "${BLUE}检测最快的镜像源...${NC}"
|
||
|
||
# Ubuntu/Debian 镜像源列表
|
||
UBUNTU_MIRRORS=(
|
||
"mirrors.tuna.tsinghua.edu.cn"
|
||
"mirrors.huaweicloud.com"
|
||
"mirrors.163.com"
|
||
"archive.ubuntu.com"
|
||
)
|
||
|
||
# CentOS/RHEL 镜像源列表
|
||
CENTOS_MIRRORS=(
|
||
"mirrors.tuna.tsinghua.edu.cn"
|
||
"mirrors.huaweicloud.com"
|
||
)
|
||
|
||
FASTEST_MIRROR=""
|
||
FASTEST_TIME=999999
|
||
|
||
# 根据系统类型选择镜像源列表
|
||
if [ "$OS" = "ubuntu" ] || [ "$OS" = "debian" ]; then
|
||
MIRRORS=("${UBUNTU_MIRRORS[@]}")
|
||
MIRROR_TYPE="ubuntu"
|
||
elif [ "$OS" = "centos" ] || [ "$OS" = "rhel" ] || [ "$OS" = "rocky" ] || [ "$OS" = "almalinux" ]; then
|
||
MIRRORS=("${CENTOS_MIRRORS[@]}")
|
||
MIRROR_TYPE="centos"
|
||
else
|
||
echo -e "${YELLOW}⚠ 未知系统类型,跳过镜像源检测${NC}"
|
||
return
|
||
fi
|
||
|
||
# 检查是否有测试工具
|
||
if ! command -v ping > /dev/null 2>&1 && ! command -v curl > /dev/null 2>&1; then
|
||
echo -e "${YELLOW}⚠ 未找到 ping 或 curl 命令,跳过镜像源检测,使用默认源${NC}"
|
||
return
|
||
fi
|
||
|
||
# 测试每个镜像源的速度
|
||
echo -e "${BLUE}正在测试镜像源速度...${NC}"
|
||
for mirror in "${MIRRORS[@]}"; do
|
||
echo -n " 测试 ${mirror}... "
|
||
|
||
# 优先使用 ping 测试延迟(取平均值)
|
||
if command -v ping > /dev/null 2>&1; then
|
||
# ping 3次,取平均时间(macOS 使用 -W,Linux 使用 -w)
|
||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||
PING_RESULT=$(ping -c 3 -W 2000 "$mirror" 2>/dev/null | grep "avg" | awk -F'/' '{print $5}')
|
||
else
|
||
PING_RESULT=$(ping -c 3 -w 2 "$mirror" 2>/dev/null | grep "avg" | awk -F'/' '{print $5}')
|
||
fi
|
||
|
||
if [ -n "$PING_RESULT" ] && [ "$PING_RESULT" != "0" ]; then
|
||
TIME=$(echo "$PING_RESULT" | awk '{print int($1)}')
|
||
echo -e "${GREEN}${TIME}ms${NC}"
|
||
|
||
if [ "$TIME" -lt "$FASTEST_TIME" ] 2>/dev/null; then
|
||
FASTEST_TIME=$TIME
|
||
FASTEST_MIRROR=$mirror
|
||
fi
|
||
else
|
||
echo -e "${RED}超时${NC}"
|
||
fi
|
||
elif command -v curl > /dev/null 2>&1; then
|
||
# 如果没有 ping 命令,使用 curl 测试连接时间
|
||
TIME=$(curl -o /dev/null -s -w "%{time_total}" --connect-timeout 3 "http://${mirror}" 2>/dev/null | awk '{print int($1*1000)}')
|
||
if [ -n "$TIME" ] && [ "$TIME" -gt 0 ] && [ "$TIME" -lt 10000 ]; then
|
||
echo -e "${GREEN}${TIME}ms${NC}"
|
||
if [ "$TIME" -lt "$FASTEST_TIME" ]; then
|
||
FASTEST_TIME=$TIME
|
||
FASTEST_MIRROR=$mirror
|
||
fi
|
||
else
|
||
echo -e "${RED}超时${NC}"
|
||
fi
|
||
fi
|
||
done
|
||
|
||
if [ -z "$FASTEST_MIRROR" ]; then
|
||
echo -e "${YELLOW}⚠ 无法检测到可用镜像源,使用默认源${NC}"
|
||
return
|
||
fi
|
||
|
||
echo -e "${GREEN}✓ 最快镜像源: ${FASTEST_MIRROR} (${FASTEST_TIME}ms)${NC}"
|
||
|
||
# 配置镜像源
|
||
if [ "$MIRROR_TYPE" = "ubuntu" ]; then
|
||
configure_ubuntu_mirror "$FASTEST_MIRROR"
|
||
elif [ "$MIRROR_TYPE" = "centos" ]; then
|
||
configure_centos_mirror "$FASTEST_MIRROR"
|
||
fi
|
||
}
|
||
|
||
# 配置 Ubuntu/Debian 镜像源
|
||
configure_ubuntu_mirror() {
|
||
local mirror="$1"
|
||
local sources_file="/etc/apt/sources.list"
|
||
local sources_backup="/etc/apt/sources.list.backup.$(date +%Y%m%d_%H%M%S)"
|
||
|
||
# 备份原始源配置
|
||
if [ -f "$sources_file" ] && [ ! -f "$sources_backup" ]; then
|
||
echo -e "${BLUE}备份原始源配置...${NC}"
|
||
sudo cp "$sources_file" "$sources_backup"
|
||
fi
|
||
|
||
# 检测 Ubuntu 版本代号
|
||
if [ -f /etc/os-release ]; then
|
||
. /etc/os-release
|
||
CODENAME=$(echo "$VERSION_CODENAME" | tr '[:upper:]' '[:lower:]')
|
||
if [ -z "$CODENAME" ]; then
|
||
# 尝试从版本号推断
|
||
case "$VERSION_ID" in
|
||
"22.04") CODENAME="jammy" ;;
|
||
"20.04") CODENAME="focal" ;;
|
||
"18.04") CODENAME="bionic" ;;
|
||
*) CODENAME="jammy" ;; # 默认使用 jammy
|
||
esac
|
||
fi
|
||
else
|
||
CODENAME="jammy" # 默认
|
||
fi
|
||
|
||
# 检测是 Ubuntu 还是 Debian
|
||
if [ "$OS" = "ubuntu" ]; then
|
||
echo -e "${BLUE}配置 Ubuntu 镜像源: ${mirror}${NC}"
|
||
sudo tee "$sources_file" > /dev/null <<EOF
|
||
# LinkMaster Node 自动配置的镜像源
|
||
deb https://${mirror}/ubuntu/ ${CODENAME} main restricted universe multiverse
|
||
deb https://${mirror}/ubuntu/ ${CODENAME}-updates main restricted universe multiverse
|
||
deb https://${mirror}/ubuntu/ ${CODENAME}-backports main restricted universe multiverse
|
||
deb https://${mirror}/ubuntu/ ${CODENAME}-security main restricted universe multiverse
|
||
EOF
|
||
elif [ "$OS" = "debian" ]; then
|
||
# Debian 版本检测
|
||
if [ -f /etc/debian_version ]; then
|
||
DEBIAN_VERSION=$(cat /etc/debian_version | cut -d'.' -f1)
|
||
case "$DEBIAN_VERSION" in
|
||
"12") CODENAME="bookworm" ;;
|
||
"11") CODENAME="bullseye" ;;
|
||
"10") CODENAME="buster" ;;
|
||
*) CODENAME="bookworm" ;;
|
||
esac
|
||
else
|
||
CODENAME="bookworm"
|
||
fi
|
||
|
||
echo -e "${BLUE}配置 Debian 镜像源: ${mirror}${NC}"
|
||
sudo tee "$sources_file" > /dev/null <<EOF
|
||
# LinkMaster Node 自动配置的镜像源
|
||
deb https://${mirror}/debian/ ${CODENAME} main contrib non-free
|
||
deb https://${mirror}/debian/ ${CODENAME}-updates main contrib non-free
|
||
deb https://${mirror}/debian-security ${CODENAME}-security main contrib non-free
|
||
EOF
|
||
fi
|
||
|
||
echo -e "${GREEN}✓ 镜像源配置完成${NC}"
|
||
}
|
||
|
||
# 配置 CentOS/RHEL 镜像源
|
||
configure_centos_mirror() {
|
||
local mirror="$1"
|
||
local repo_dir="/etc/yum.repos.d"
|
||
local backup_dir="/etc/yum.repos.d/backup.$(date +%Y%m%d_%H%M%S)"
|
||
|
||
# 备份原始源配置
|
||
if [ -d "$repo_dir" ] && [ ! -d "$backup_dir" ]; then
|
||
echo -e "${BLUE}备份原始源配置...${NC}"
|
||
sudo mkdir -p "$backup_dir"
|
||
sudo cp -r "$repo_dir"/*.repo "$backup_dir/" 2>/dev/null || true
|
||
fi
|
||
|
||
# 检测系统类型和版本
|
||
local release_file="/etc/redhat-release"
|
||
local os_version=""
|
||
local os_name="centos"
|
||
|
||
if [ -f "$release_file" ]; then
|
||
RELEASE_CONTENT=$(cat "$release_file")
|
||
os_version=$(echo "$RELEASE_CONTENT" | grep -oE '[0-9]+' | head -1)
|
||
|
||
# 检测系统类型
|
||
if echo "$RELEASE_CONTENT" | grep -qi "rocky"; then
|
||
os_name="rocky"
|
||
elif echo "$RELEASE_CONTENT" | grep -qi "almalinux"; then
|
||
os_name="almalinux"
|
||
elif echo "$RELEASE_CONTENT" | grep -qi "centos"; then
|
||
os_name="centos"
|
||
fi
|
||
else
|
||
os_version="7"
|
||
fi
|
||
|
||
echo -e "${BLUE}配置 ${os_name} ${os_version} 镜像源: ${mirror}${NC}"
|
||
|
||
# 备份并禁用所有现有仓库
|
||
sudo find "$repo_dir" -name "*.repo" -not -name "*.backup" -exec sudo mv {} {}.backup \; 2>/dev/null || true
|
||
|
||
# 根据系统类型创建新的仓库配置
|
||
if [ "$os_name" = "rocky" ]; then
|
||
# Rocky Linux
|
||
sudo tee "$repo_dir/Rocky-Base.repo" > /dev/null <<EOF
|
||
[baseos]
|
||
name=Rocky Linux \$releasever - BaseOS - ${mirror}
|
||
baseurl=https://${mirror}/rocky/\$releasever/BaseOS/\$basearch/os/
|
||
gpgcheck=1
|
||
enabled=1
|
||
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rocky
|
||
|
||
[appstream]
|
||
name=Rocky Linux \$releasever - AppStream - ${mirror}
|
||
baseurl=https://${mirror}/rocky/\$releasever/AppStream/\$basearch/os/
|
||
gpgcheck=1
|
||
enabled=1
|
||
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rocky
|
||
EOF
|
||
elif [ "$os_name" = "almalinux" ]; then
|
||
# AlmaLinux
|
||
sudo tee "$repo_dir/AlmaLinux-Base.repo" > /dev/null <<EOF
|
||
[baseos]
|
||
name=AlmaLinux \$releasever - BaseOS - ${mirror}
|
||
baseurl=https://${mirror}/almalinux/\$releasever/BaseOS/\$basearch/os/
|
||
gpgcheck=1
|
||
enabled=1
|
||
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux
|
||
|
||
[appstream]
|
||
name=AlmaLinux \$releasever - AppStream - ${mirror}
|
||
baseurl=https://${mirror}/almalinux/\$releasever/AppStream/\$basearch/os/
|
||
gpgcheck=1
|
||
enabled=1
|
||
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux
|
||
EOF
|
||
elif [ "$os_version" = "7" ]; then
|
||
# CentOS 7
|
||
sudo tee "$repo_dir/CentOS-Base.repo" > /dev/null <<EOF
|
||
[base]
|
||
name=CentOS-\$releasever - Base - ${mirror}
|
||
baseurl=https://${mirror}/centos/\$releasever/os/\$basearch/
|
||
gpgcheck=1
|
||
enabled=1
|
||
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
|
||
|
||
[updates]
|
||
name=CentOS-\$releasever - Updates - ${mirror}
|
||
baseurl=https://${mirror}/centos/\$releasever/updates/\$basearch/
|
||
gpgcheck=1
|
||
enabled=1
|
||
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
|
||
|
||
[extras]
|
||
name=CentOS-\$releasever - Extras - ${mirror}
|
||
baseurl=https://${mirror}/centos/\$releasever/extras/\$basearch/
|
||
gpgcheck=1
|
||
enabled=1
|
||
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
|
||
EOF
|
||
else
|
||
# CentOS Stream 8/9 或其他版本
|
||
sudo tee "$repo_dir/CentOS-Base.repo" > /dev/null <<EOF
|
||
[baseos]
|
||
name=CentOS-\$releasever - BaseOS - ${mirror}
|
||
baseurl=https://${mirror}/centos-stream/\$stream/BaseOS/\$basearch/os/
|
||
gpgcheck=1
|
||
enabled=1
|
||
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
|
||
|
||
[appstream]
|
||
name=CentOS-\$releasever - AppStream - ${mirror}
|
||
baseurl=https://${mirror}/centos-stream/\$stream/AppStream/\$basearch/os/
|
||
gpgcheck=1
|
||
enabled=1
|
||
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
|
||
EOF
|
||
fi
|
||
|
||
echo -e "${GREEN}✓ 镜像源配置完成${NC}"
|
||
}
|
||
|
||
# 安装系统依赖
|
||
install_dependencies() {
|
||
echo -e "${BLUE}安装系统依赖...${NC}"
|
||
|
||
if [ "$OS" = "ubuntu" ] || [ "$OS" = "debian" ]; then
|
||
echo -e "${BLUE}更新 apt 软件包列表...${NC}"
|
||
sudo apt-get update
|
||
echo -e "${BLUE}安装系统依赖包...${NC}"
|
||
sudo apt-get install -y curl wget ping traceroute dnsutils git
|
||
elif [ "$OS" = "centos" ] || [ "$OS" = "rhel" ] || [ "$OS" = "rocky" ] || [ "$OS" = "almalinux" ]; then
|
||
echo -e "${BLUE}安装系统依赖包...${NC}"
|
||
sudo yum install -y curl wget iputils traceroute bind-utils git
|
||
else
|
||
echo -e "${YELLOW}警告: 未知系统类型,跳过依赖安装${NC}"
|
||
fi
|
||
|
||
echo -e "${GREEN}✓ 系统依赖安装完成${NC}"
|
||
}
|
||
|
||
# 从官网下载安装 Go
|
||
install_go_from_official() {
|
||
echo -e "${BLUE}从 Go 官网下载安装...${NC}"
|
||
|
||
# 检测架构
|
||
local arch=""
|
||
case "$ARCH" in
|
||
amd64)
|
||
arch="amd64"
|
||
;;
|
||
arm64)
|
||
arch="arm64"
|
||
;;
|
||
*)
|
||
echo -e "${RED}不支持的架构: $ARCH${NC}"
|
||
return 1
|
||
;;
|
||
esac
|
||
|
||
# Go 版本(可以根据需要修改)
|
||
local go_version="1.21.5"
|
||
local go_tar="go${go_version}.linux-${arch}.tar.gz"
|
||
local go_url="https://golang.google.cn/dl/${go_tar}"
|
||
|
||
# 如果国内镜像访问失败,尝试官方源
|
||
local download_success=false
|
||
|
||
# 尝试从国内镜像下载
|
||
echo -e "${BLUE}尝试从 Go 国内镜像下载...${NC}"
|
||
if curl -fsSL -o "/tmp/${go_tar}" "${go_url}" 2>/dev/null; then
|
||
download_success=true
|
||
else
|
||
# 尝试从官方源下载
|
||
echo -e "${BLUE}尝试从 Go 官方源下载...${NC}"
|
||
go_url="https://go.dev/dl/${go_tar}"
|
||
if curl -fsSL -o "/tmp/${go_tar}" "${go_url}" 2>/dev/null; then
|
||
download_success=true
|
||
fi
|
||
fi
|
||
|
||
if [ "$download_success" = false ]; then
|
||
echo -e "${RED}下载 Go 失败,请检查网络连接${NC}"
|
||
return 1
|
||
fi
|
||
|
||
# 删除旧版本(如果存在)
|
||
if [ -d "/usr/local/go" ]; then
|
||
echo -e "${BLUE}删除旧版本 Go...${NC}"
|
||
sudo rm -rf /usr/local/go
|
||
fi
|
||
|
||
# 解压安装
|
||
echo -e "${BLUE}解压并安装 Go...${NC}"
|
||
sudo tar -C /usr/local -xzf "/tmp/${go_tar}"
|
||
rm -f "/tmp/${go_tar}"
|
||
echo -e "${GREEN}✓ Go 解压完成${NC}"
|
||
|
||
# 添加到 PATH(如果还没有)
|
||
if ! grep -q "/usr/local/go/bin" /etc/profile 2>/dev/null; then
|
||
echo 'export PATH=$PATH:/usr/local/go/bin' | sudo tee -a /etc/profile > /dev/null
|
||
fi
|
||
|
||
# 配置 Go 代理(使用国内镜像)
|
||
if ! grep -q "GOPROXY" /etc/profile 2>/dev/null; then
|
||
echo '' | sudo tee -a /etc/profile > /dev/null
|
||
echo '# Go 代理配置(使用国内镜像加速)' | sudo tee -a /etc/profile > /dev/null
|
||
echo 'export GOPROXY=https://goproxy.cn,direct' | sudo tee -a /etc/profile > /dev/null
|
||
echo 'export GOSUMDB=off' | sudo tee -a /etc/profile > /dev/null
|
||
fi
|
||
|
||
# 设置当前会话的 PATH 和 GOPROXY
|
||
export PATH=$PATH:/usr/local/go/bin
|
||
export GOPROXY=https://goproxy.cn,direct
|
||
export GOSUMDB=off
|
||
|
||
# 验证安装
|
||
if command -v go > /dev/null 2>&1; then
|
||
GO_VERSION=$(go version 2>/dev/null | head -1)
|
||
echo -e "${GREEN}✓ Go 安装完成: ${GO_VERSION}${NC}"
|
||
return 0
|
||
else
|
||
echo -e "${YELLOW}⚠ Go 已安装但未在 PATH 中,请重新登录或执行: export PATH=\$PATH:/usr/local/go/bin${NC}"
|
||
# 临时添加到 PATH 继续执行
|
||
export PATH=$PATH:/usr/local/go/bin
|
||
if command -v go > /dev/null 2>&1; then
|
||
GO_VERSION=$(go version 2>/dev/null | head -1)
|
||
echo -e "${GREEN}✓ Go 安装完成: ${GO_VERSION}${NC}"
|
||
return 0
|
||
fi
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# 安装 Go 环境
|
||
install_go() {
|
||
echo -e "${BLUE}检查 Go 环境...${NC}"
|
||
|
||
# 先检查是否已安装且可用
|
||
if command -v go > /dev/null 2>&1; then
|
||
GO_VERSION=$(go version 2>/dev/null | head -1)
|
||
if [ -n "$GO_VERSION" ]; then
|
||
echo -e "${GREEN}✓ Go 已安装: ${GO_VERSION}${NC}"
|
||
# 检查 Go 版本是否可用(尝试运行 go version)
|
||
if go version > /dev/null 2>&1; then
|
||
echo -e "${BLUE}Go 环境正常,跳过安装流程${NC}"
|
||
# 确保 GOPROXY 已配置(如果未配置则添加)
|
||
if ! grep -q "GOPROXY" /etc/profile 2>/dev/null; then
|
||
echo -e "${BLUE}配置 Go 代理环境变量...${NC}"
|
||
echo '' | sudo tee -a /etc/profile > /dev/null
|
||
echo '# Go 代理配置(使用国内镜像加速)' | sudo tee -a /etc/profile > /dev/null
|
||
echo 'export GOPROXY=https://goproxy.cn,direct' | sudo tee -a /etc/profile > /dev/null
|
||
echo 'export GOSUMDB=off' | sudo tee -a /etc/profile > /dev/null
|
||
fi
|
||
# 设置当前会话的环境变量
|
||
export GOPROXY=${GOPROXY:-https://goproxy.cn,direct}
|
||
export GOSUMDB=${GOSUMDB:-off}
|
||
return 0
|
||
else
|
||
echo -e "${YELLOW}⚠ Go 已安装但无法正常运行,尝试重新安装...${NC}"
|
||
fi
|
||
else
|
||
echo -e "${YELLOW}⚠ Go 已安装但无法获取版本信息,尝试重新安装...${NC}"
|
||
fi
|
||
fi
|
||
|
||
# 如果 Go 未安装或不可用,开始安装流程
|
||
echo -e "${BLUE}开始安装 Go 环境...${NC}"
|
||
|
||
# 尝试从系统包管理器安装
|
||
local install_success=false
|
||
|
||
if [ "$OS" = "ubuntu" ] || [ "$OS" = "debian" ]; then
|
||
echo -e "${BLUE}尝试从 apt 安装 Go...${NC}"
|
||
sudo apt-get update
|
||
if sudo apt-get install -y golang-go; then
|
||
install_success=true
|
||
else
|
||
echo -e "${YELLOW}⚠ apt 安装 Go 失败${NC}"
|
||
fi
|
||
elif [ "$OS" = "centos" ] || [ "$OS" = "rhel" ] || [ "$OS" = "rocky" ] || [ "$OS" = "almalinux" ]; then
|
||
echo -e "${BLUE}尝试从 yum 安装 Go...${NC}"
|
||
# 检查 yum 源中是否有 golang 包
|
||
if yum list available golang > /dev/null 2>&1; then
|
||
if sudo yum install -y golang; then
|
||
install_success=true
|
||
else
|
||
echo -e "${YELLOW}⚠ yum 安装 Go 失败${NC}"
|
||
fi
|
||
else
|
||
echo -e "${YELLOW}⚠ yum 源中未找到 golang 包,尝试从官网下载安装${NC}"
|
||
fi
|
||
fi
|
||
|
||
# 检查安装是否成功
|
||
if [ "$install_success" = true ] && command -v go > /dev/null 2>&1; then
|
||
GO_VERSION=$(go version 2>/dev/null | head -1)
|
||
echo -e "${GREEN}✓ Go 安装完成: ${GO_VERSION}${NC}"
|
||
|
||
# 配置 Go 代理(使用国内镜像)
|
||
if ! grep -q "GOPROXY" /etc/profile 2>/dev/null; then
|
||
echo '' | sudo tee -a /etc/profile > /dev/null
|
||
echo '# Go 代理配置(使用国内镜像加速)' | sudo tee -a /etc/profile > /dev/null
|
||
echo 'export GOPROXY=https://goproxy.cn,direct' | sudo tee -a /etc/profile > /dev/null
|
||
echo 'export GOSUMDB=off' | sudo tee -a /etc/profile > /dev/null
|
||
fi
|
||
|
||
# 设置当前会话的 GOPROXY
|
||
export GOPROXY=https://goproxy.cn,direct
|
||
export GOSUMDB=off
|
||
|
||
return 0
|
||
fi
|
||
|
||
# 如果包管理器安装失败,从官网下载安装
|
||
echo -e "${BLUE}包管理器安装失败,尝试从官网下载安装...${NC}"
|
||
if install_go_from_official; then
|
||
return 0
|
||
fi
|
||
|
||
# 所有方法都失败
|
||
echo -e "${RED}Go 安装失败${NC}"
|
||
show_build_alternatives
|
||
exit 1
|
||
}
|
||
|
||
# 显示替代方案
|
||
show_build_alternatives() {
|
||
echo ""
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
echo -e "${YELLOW} 安装失败,请使用以下替代方案:${NC}"
|
||
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
echo -e "${GREEN}手动编译安装:${NC}"
|
||
echo " git clone https://gitee.nas.cpolar.top/${GITHUB_REPO}.git ${SOURCE_DIR}"
|
||
echo " cd ${SOURCE_DIR}"
|
||
echo " go build -o agent ./cmd/agent"
|
||
echo " sudo cp agent /usr/local/bin/linkmaster-node"
|
||
echo " sudo chmod +x /usr/local/bin/linkmaster-node"
|
||
echo ""
|
||
}
|
||
|
||
# 检查是否已安装
|
||
check_installed() {
|
||
# 检查服务文件是否存在
|
||
if [ -f "/etc/systemd/system/${SERVICE_NAME}.service" ]; then
|
||
return 0
|
||
fi
|
||
# 检查二进制文件是否存在
|
||
if [ -f "$INSTALL_DIR/$BINARY_NAME" ]; then
|
||
return 0
|
||
fi
|
||
# 检查源码目录是否存在
|
||
if [ -d "$SOURCE_DIR" ]; then
|
||
return 0
|
||
fi
|
||
return 1
|
||
}
|
||
|
||
# 卸载已安装的服务
|
||
uninstall_service() {
|
||
echo -e "${BLUE}检测到已安装的服务,开始卸载...${NC}"
|
||
|
||
# 停止服务
|
||
if systemctl is-active --quiet ${SERVICE_NAME} 2>/dev/null; then
|
||
echo -e "${BLUE}停止服务...${NC}"
|
||
sudo systemctl stop ${SERVICE_NAME} 2>/dev/null || true
|
||
sleep 2
|
||
fi
|
||
|
||
# 禁用服务
|
||
if systemctl is-enabled --quiet ${SERVICE_NAME} 2>/dev/null; then
|
||
echo -e "${BLUE}禁用服务...${NC}"
|
||
sudo systemctl disable ${SERVICE_NAME} 2>/dev/null || true
|
||
fi
|
||
|
||
# 删除 systemd 服务文件
|
||
if [ -f "/etc/systemd/system/${SERVICE_NAME}.service" ]; then
|
||
echo -e "${BLUE}删除服务文件...${NC}"
|
||
sudo rm -f /etc/systemd/system/${SERVICE_NAME}.service
|
||
fi
|
||
|
||
# 删除可能的 override 配置目录(包含 Environment 等配置)
|
||
if [ -d "/etc/systemd/system/${SERVICE_NAME}.service.d" ]; then
|
||
echo -e "${BLUE}删除服务配置目录...${NC}"
|
||
sudo rm -rf /etc/systemd/system/${SERVICE_NAME}.service.d
|
||
fi
|
||
|
||
# 重新加载 systemd daemon
|
||
sudo systemctl daemon-reload
|
||
|
||
# 删除二进制文件
|
||
if [ -f "$INSTALL_DIR/$BINARY_NAME" ]; then
|
||
echo -e "${BLUE}删除二进制文件...${NC}"
|
||
sudo rm -f "$INSTALL_DIR/$BINARY_NAME"
|
||
fi
|
||
|
||
# 删除源码目录
|
||
if [ -d "$SOURCE_DIR" ]; then
|
||
echo -e "${BLUE}删除源码目录...${NC}"
|
||
sudo rm -rf "$SOURCE_DIR"
|
||
fi
|
||
|
||
# 清理进程(如果还在运行)
|
||
if pgrep -f "$BINARY_NAME" > /dev/null 2>&1; then
|
||
echo -e "${BLUE}清理残留进程...${NC}"
|
||
sudo pkill -f "$BINARY_NAME" 2>/dev/null || true
|
||
sleep 1
|
||
fi
|
||
|
||
echo -e "${GREEN}✓ 卸载完成${NC}"
|
||
echo ""
|
||
}
|
||
|
||
# 从源码编译安装
|
||
build_from_source() {
|
||
echo -e "${BLUE}从源码编译安装节点端...${NC}"
|
||
|
||
# 检查 Go 环境(如果已安装则跳过安装流程)
|
||
if ! command -v go > /dev/null 2>&1; then
|
||
echo -e "${BLUE}未检测到 Go 环境,开始安装...${NC}"
|
||
install_go
|
||
else
|
||
# Go 已安装,验证是否可用
|
||
GO_VERSION=$(go version 2>/dev/null | head -1 || echo "")
|
||
if [ -n "$GO_VERSION" ] && go version > /dev/null 2>&1; then
|
||
echo -e "${GREEN}✓ Go 已安装: ${GO_VERSION}${NC}"
|
||
echo -e "${BLUE}跳过 Go 安装流程,直接使用现有环境${NC}"
|
||
else
|
||
echo -e "${YELLOW}⚠ Go 已安装但无法正常运行,尝试重新安装...${NC}"
|
||
install_go
|
||
fi
|
||
fi
|
||
|
||
# 确保 Go 在 PATH 中(如果从官网安装的)
|
||
if [ -d "/usr/local/go/bin" ] && ! echo "$PATH" | grep -q "/usr/local/go/bin"; then
|
||
export PATH=$PATH:/usr/local/go/bin
|
||
fi
|
||
|
||
# 再次检查 Go 版本(确保安装成功)
|
||
GO_VERSION=$(go version 2>/dev/null | head -1 || echo "")
|
||
if [ -z "$GO_VERSION" ]; then
|
||
echo -e "${RED}无法获取 Go 版本信息${NC}"
|
||
show_build_alternatives
|
||
exit 1
|
||
fi
|
||
|
||
echo -e "${BLUE}使用 Go 版本: ${GO_VERSION}${NC}"
|
||
|
||
# 确定 Go 的完整路径(用于 sudo 执行)
|
||
GO_BIN=$(command -v go)
|
||
if [ -z "$GO_BIN" ]; then
|
||
echo -e "${RED}无法找到 Go 可执行文件${NC}"
|
||
show_build_alternatives
|
||
exit 1
|
||
fi
|
||
|
||
# 如果源码目录已存在,删除(卸载函数应该已经删除,这里作为保险)
|
||
if [ -d "$SOURCE_DIR" ]; then
|
||
echo -e "${YELLOW}清理旧的源码目录...${NC}"
|
||
sudo rm -rf "$SOURCE_DIR"
|
||
fi
|
||
|
||
# 克隆仓库到源码目录
|
||
echo -e "${BLUE}克隆仓库到 ${SOURCE_DIR}...${NC}"
|
||
if ! sudo git clone --branch "${GITHUB_BRANCH}" "https://gitee.nas.cpolar.top/${GITHUB_REPO}.git" "$SOURCE_DIR" 2>&1; then
|
||
echo -e "${RED}克隆仓库失败,请检查网络连接和仓库地址${NC}"
|
||
echo -e "${YELLOW}仓库地址: https://gitee.nas.cpolar.top/${GITHUB_REPO}.git${NC}"
|
||
show_build_alternatives
|
||
exit 1
|
||
fi
|
||
|
||
# 设置目录权限(允许当前用户访问,但服务运行时是 root)
|
||
sudo chown -R root:root "$SOURCE_DIR" 2>/dev/null || true
|
||
|
||
cd "$SOURCE_DIR"
|
||
|
||
# 配置 Git safe.directory,解决所有权问题
|
||
sudo git config --global --add safe.directory "$SOURCE_DIR" 2>/dev/null || true
|
||
git config --global --add safe.directory "$SOURCE_DIR" 2>/dev/null || true
|
||
|
||
# 配置 Git 使用代理(加速 GitHub 访问)
|
||
echo -e "${BLUE}配置 Git 代理(加速 GitHub 访问)...${NC}"
|
||
|
||
# GitHub 代理镜像列表(按优先级排序)
|
||
GITHUB_PROXIES=(
|
||
"https://gh-proxy.com"
|
||
"https://githubproxy.cc"
|
||
"https://ghproxy.com"
|
||
"https://mirror.ghproxy.com"
|
||
"https://github.com.cnpmjs.org"
|
||
)
|
||
|
||
# 测试并选择可用的 GitHub 代理
|
||
GITHUB_PROXY=""
|
||
for proxy in "${GITHUB_PROXIES[@]}"; do
|
||
echo -n " 测试 ${proxy}... "
|
||
# 测试代理是否可用(尝试访问 GitHub 主页)
|
||
if curl -sf --connect-timeout 3 --max-time 5 "${proxy}/https://github.com" > /dev/null 2>&1 || \
|
||
curl -sf --connect-timeout 3 --max-time 5 "${proxy}" > /dev/null 2>&1; then
|
||
GITHUB_PROXY="$proxy"
|
||
echo -e "${GREEN}可用${NC}"
|
||
break
|
||
else
|
||
echo -e "${RED}不可用${NC}"
|
||
fi
|
||
done
|
||
|
||
if [ -n "$GITHUB_PROXY" ]; then
|
||
# 配置 Git 使用代理
|
||
echo -e "${GREEN}✓ 使用 GitHub 代理: ${GITHUB_PROXY}${NC}"
|
||
|
||
# 根据不同的代理服务配置不同的 URL 格式
|
||
if echo "$GITHUB_PROXY" | grep -q "gh-proxy.com\|githubproxy.cc"; then
|
||
# gh-proxy.com 和 githubproxy.cc 使用 /https://github.com/ 格式
|
||
GIT_PROXY_URL="${GITHUB_PROXY}/https://github.com/"
|
||
sudo git config --global url."${GIT_PROXY_URL}".insteadOf "https://github.com/" 2>/dev/null || true
|
||
git config --global url."${GIT_PROXY_URL}".insteadOf "https://github.com/" 2>/dev/null || true
|
||
|
||
# 配置 Git HTTP/HTTPS 代理
|
||
sudo git config --global http.https://github.com.proxy "${GITHUB_PROXY}/" 2>/dev/null || true
|
||
git config --global http.https://github.com.proxy "${GITHUB_PROXY}/" 2>/dev/null || true
|
||
else
|
||
# 其他代理使用标准格式
|
||
sudo git config --global url."${GITHUB_PROXY}/https://github.com/".insteadOf "https://github.com/" 2>/dev/null || true
|
||
git config --global url."${GITHUB_PROXY}/https://github.com/".insteadOf "https://github.com/" 2>/dev/null || true
|
||
|
||
sudo git config --global http.https://github.com.proxy "${GITHUB_PROXY}/" 2>/dev/null || true
|
||
git config --global http.https://github.com.proxy "${GITHUB_PROXY}/" 2>/dev/null || true
|
||
fi
|
||
|
||
# 设置环境变量(供 Go 使用)
|
||
export HTTP_PROXY="${GITHUB_PROXY}/"
|
||
export HTTPS_PROXY="${GITHUB_PROXY}/"
|
||
export http_proxy="${GITHUB_PROXY}/"
|
||
export https_proxy="${GITHUB_PROXY}/"
|
||
|
||
# 添加到 Go 环境变量中
|
||
GO_ENV="$GO_ENV HTTP_PROXY=${GITHUB_PROXY}/ HTTPS_PROXY=${GITHUB_PROXY}/ http_proxy=${GITHUB_PROXY}/ https_proxy=${GITHUB_PROXY}/"
|
||
|
||
echo -e "${BLUE}已配置 Git 和 Go 使用 GitHub 代理加速${NC}"
|
||
else
|
||
echo -e "${YELLOW}⚠ 未找到可用的 GitHub 代理,将直接访问 GitHub${NC}"
|
||
echo -e "${YELLOW}提示: 如果 GitHub 访问慢,可以手动设置代理${NC}"
|
||
fi
|
||
|
||
# 配置 Go 代理(使用国内镜像)
|
||
echo -e "${BLUE}配置 Go 代理(使用国内镜像加速)...${NC}"
|
||
# Go 代理镜像列表(按优先级排序,使用多个镜像以提高成功率)
|
||
# 注意:不包含 proxy.golang.org,因为在中国大陆无法访问
|
||
GO_PROXY_MIRRORS=(
|
||
"https://goproxy.cn,https://goproxy.io,https://mirrors.aliyun.com/go-proxy/,direct"
|
||
"https://goproxy.io,https://goproxy.cn,https://mirrors.aliyun.com/go-proxy/,direct"
|
||
"https://mirrors.aliyun.com/go-proxy/,https://goproxy.cn,https://goproxy.io,direct"
|
||
)
|
||
|
||
# 测试并选择最快的 Go 代理(使用多个镜像作为备选)
|
||
GO_PROXY=""
|
||
for proxy_list in "${GO_PROXY_MIRRORS[@]}"; do
|
||
# 取第一个镜像进行测试
|
||
first_proxy=$(echo "$proxy_list" | cut -d',' -f1)
|
||
echo -n " 测试代理链: ${first_proxy}... "
|
||
if curl -sf --connect-timeout 3 --max-time 5 "${first_proxy}" > /dev/null 2>&1; then
|
||
GO_PROXY="$proxy_list"
|
||
echo -e "${GREEN}可用${NC}"
|
||
break
|
||
else
|
||
echo -e "${RED}不可用${NC}"
|
||
fi
|
||
done
|
||
|
||
# 如果所有镜像都不可用,使用 direct 模式(直接从源码仓库下载)
|
||
if [ -z "$GO_PROXY" ]; then
|
||
GO_PROXY="direct"
|
||
echo -e "${YELLOW}⚠ 所有国内镜像不可用,使用 direct 模式(直接从 GitHub/GitLab 等下载)${NC}"
|
||
echo -e "${YELLOW}⚠ 注意: direct 模式可能较慢,建议检查网络连接${NC}"
|
||
else
|
||
echo -e "${GREEN}✓ 使用 Go 代理链: ${GO_PROXY}${NC}"
|
||
echo -e "${BLUE}说明: Go 会按顺序尝试代理列表中的镜像,最后使用 direct 模式${NC}"
|
||
echo -e "${BLUE}注意: 已禁用 GOSUMDB,避免连接 proxy.golang.org${NC}"
|
||
fi
|
||
|
||
# 下载依赖(使用 sudo 以 root 用户执行,确保 PATH 包含 Go,并设置 GOPROXY)
|
||
echo -e "${BLUE}下载 Go 依赖...${NC}"
|
||
echo -e "${BLUE}使用代理: ${GO_PROXY}${NC}"
|
||
|
||
# 构建包含 Go PATH 和 GOPROXY 的环境变量
|
||
GO_PATH_ENV="PATH=\$PATH:/usr/local/go/bin"
|
||
if [ -d "/usr/local/go/bin" ]; then
|
||
GO_PATH_ENV="PATH=/usr/local/go/bin:\$PATH"
|
||
fi
|
||
|
||
# 禁用 GOSUMDB 以避免连接 proxy.golang.org(在中国大陆通常无法访问)
|
||
# 如果必须启用校验和,可以使用 GOSUMDB=sum.golang.google.cn,但可能仍有问题
|
||
GO_ENV="$GO_PATH_ENV GOPROXY=${GO_PROXY} GOSUMDB=off GOTIMEOUT=60 GONOPROXY= GONOSUMDB= GOPRIVATE=*"
|
||
|
||
# 使用 go env -w 永久设置环境变量(避免被其他配置覆盖)
|
||
echo -e "${BLUE}永久设置 Go 环境变量(避免连接 proxy.golang.org)...${NC}"
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOPROXY=${GO_PROXY}" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOSUMDB=off" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOPRIVATE=*" 2>/dev/null || true
|
||
|
||
# 显示当前 Go 环境信息(验证设置是否生效)
|
||
echo -e "${BLUE}Go 环境信息:${NC}"
|
||
echo -e "${BLUE} 设置的 GOPROXY=${GO_PROXY}${NC}"
|
||
echo -e "${BLUE} 设置的 GOSUMDB=off (已禁用,避免连接 proxy.golang.org)${NC}"
|
||
echo -e "${BLUE} 实际生效的环境变量:${NC}"
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_ENV && go env GOPROXY GOSUMDB GOPRIVATE" || true
|
||
echo ""
|
||
|
||
# 验证是否仍然会连接 proxy.golang.org
|
||
ACTUAL_GOPROXY=$(sudo bash -c "cd '$SOURCE_DIR' && $GO_ENV && go env GOPROXY" 2>/dev/null || echo "")
|
||
if echo "$ACTUAL_GOPROXY" | grep -q "proxy.golang.org"; then
|
||
echo -e "${RED}⚠ 警告: 检测到 GOPROXY 仍包含 proxy.golang.org${NC}"
|
||
echo -e "${YELLOW}正在强制移除 proxy.golang.org...${NC}"
|
||
CLEAN_PROXY=$(echo "$ACTUAL_GOPROXY" | sed 's|https://proxy.golang.org,||g' | sed 's|,https://proxy.golang.org||g' | sed 's|https://proxy.golang.org||g')
|
||
GO_PROXY="$CLEAN_PROXY"
|
||
GO_ENV="$GO_PATH_ENV GOPROXY=${GO_PROXY} GOSUMDB=off GOTIMEOUT=60 GONOPROXY= GONOSUMDB= GOPRIVATE=*"
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOPROXY=${GO_PROXY}" 2>/dev/null || true
|
||
echo -e "${GREEN}✓ 已更新 GOPROXY: ${GO_PROXY}${NC}"
|
||
fi
|
||
|
||
# 先尝试整理依赖(显示详细信息)
|
||
echo -e "${BLUE}整理 Go 模块依赖...${NC}"
|
||
# 再次确认环境变量(防止被覆盖)
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOPROXY=${GO_PROXY}" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOSUMDB=off" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOPRIVATE=*" 2>/dev/null || true
|
||
|
||
if ! sudo bash -c "cd '$SOURCE_DIR' && $GO_ENV && go mod tidy -v" 2>&1; then
|
||
echo -e "${YELLOW}⚠ go mod tidy 失败,继续尝试下载依赖...${NC}"
|
||
# 检查错误日志中是否包含 proxy.golang.org
|
||
TIDY_LOG=$(sudo bash -c "cd '$SOURCE_DIR' && $GO_ENV && go mod tidy -v" 2>&1 || true)
|
||
if echo "$TIDY_LOG" | grep -q "proxy.golang.org"; then
|
||
echo -e "${RED}错误: 检测到仍尝试连接 proxy.golang.org${NC}"
|
||
echo -e "${YELLOW}尝试清理 Go 模块缓存并重新设置...${NC}"
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go clean -modcache" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOPROXY=${GO_PROXY}" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOSUMDB=off" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOPRIVATE=*" 2>/dev/null || true
|
||
fi
|
||
fi
|
||
|
||
# 下载依赖(显示详细进度和速度)
|
||
echo -e "${BLUE}开始下载 Go 依赖包(这可能需要一些时间)...${NC}"
|
||
echo -e "${YELLOW}提示: 如果下载速度慢,请检查网络连接和代理设置${NC}"
|
||
echo ""
|
||
|
||
# 设置更长的超时时间和启用详细输出
|
||
DOWNLOAD_START_TIME=$(date +%s)
|
||
DOWNLOAD_LOG="/tmp/go_download_$$.log"
|
||
DOWNLOAD_PROGRESS_LOG="/tmp/go_progress_$$.log"
|
||
|
||
# 在下载前再次确认环境变量设置(强制所有包都通过代理)
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOPROXY=${GO_PROXY}" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOSUMDB=off" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GONOPROXY=" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GONOSUMDB=" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOPRIVATE=*" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOTIMEOUT=60" 2>/dev/null || true
|
||
|
||
# 使用命名管道来实时监控输出
|
||
PIPE_FILE="/tmp/go_download_pipe_$$"
|
||
mkfifo "$PIPE_FILE" 2>/dev/null || true
|
||
|
||
# 后台进程显示实时进度
|
||
(
|
||
DOWNLOADED_COUNT=0
|
||
LAST_COUNT=0
|
||
LAST_PACKAGE=""
|
||
PACKAGES=()
|
||
|
||
# 从管道读取输出并实时显示
|
||
while IFS= read -r line || [ -n "$line" ]; do
|
||
# 检查是否结束
|
||
if [ -f "${DOWNLOAD_PROGRESS_LOG}.done" ]; then
|
||
break
|
||
fi
|
||
|
||
# 检测下载消息
|
||
if echo "$line" | grep -q "go: downloading"; then
|
||
DOWNLOADED_COUNT=$((DOWNLOADED_COUNT + 1))
|
||
CURRENT_TIME=$(date +%s)
|
||
ELAPSED=$((CURRENT_TIME - DOWNLOAD_START_TIME))
|
||
|
||
# 提取包名
|
||
PACKAGE_NAME=$(echo "$line" | sed 's/go: downloading //' | cut -d' ' -f1)
|
||
if [ -n "$PACKAGE_NAME" ] && [ "$PACKAGE_NAME" != "$LAST_PACKAGE" ]; then
|
||
PACKAGES+=("$PACKAGE_NAME")
|
||
LAST_PACKAGE=$PACKAGE_NAME
|
||
|
||
# 计算速度
|
||
if [ "$ELAPSED" -gt 0 ]; then
|
||
SPEED=$(echo "scale=2; $DOWNLOADED_COUNT / $ELAPSED" | bc 2>/dev/null || echo "0")
|
||
else
|
||
SPEED="0"
|
||
fi
|
||
|
||
# 显示进度(使用 \r 在同一行更新)
|
||
echo -ne "\r${BLUE}进度: 已下载 ${GREEN}${DOWNLOADED_COUNT}${NC} 个包 | 耗时: ${ELAPSED}秒 | 速度: ${GREEN}${SPEED}${NC} 包/秒 | 当前: ${GREEN}${PACKAGE_NAME}${NC} "
|
||
fi
|
||
fi
|
||
done < "$PIPE_FILE" &
|
||
|
||
# 等待主进程结束
|
||
while [ ! -f "${DOWNLOAD_PROGRESS_LOG}.done" ]; do
|
||
sleep 0.5
|
||
done
|
||
) &
|
||
PROGRESS_PID=$!
|
||
|
||
# 使用 -x 标志下载,显示详细输出,同时写入日志和管道
|
||
# 设置较短的超时时间,避免长时间卡住
|
||
echo -e "${BLUE}开始下载(超时时间:300秒)...${NC}"
|
||
|
||
# 确保环境变量中包含代理设置
|
||
if [ -n "$GITHUB_PROXY" ]; then
|
||
GO_ENV="$GO_ENV HTTP_PROXY=${GITHUB_PROXY}/ HTTPS_PROXY=${GITHUB_PROXY}/ http_proxy=${GITHUB_PROXY}/ https_proxy=${GITHUB_PROXY}/"
|
||
fi
|
||
|
||
# 使用 timeout 命令设置总超时时间
|
||
if timeout 300 sudo bash -c "cd '$SOURCE_DIR' && $GO_ENV && go mod download -x" 2>&1 | tee "$DOWNLOAD_LOG" "$PIPE_FILE"; then
|
||
# 停止进度显示进程
|
||
touch "${DOWNLOAD_PROGRESS_LOG}.done"
|
||
sleep 2
|
||
kill "$PROGRESS_PID" 2>/dev/null || true
|
||
wait "$PROGRESS_PID" 2>/dev/null || true
|
||
|
||
# 清理管道文件
|
||
rm -f "$PIPE_FILE" 2>/dev/null || true
|
||
|
||
DOWNLOAD_END_TIME=$(date +%s)
|
||
DOWNLOAD_DURATION=$((DOWNLOAD_END_TIME - DOWNLOAD_START_TIME))
|
||
|
||
# 统计下载信息(从输出中统计)
|
||
TOTAL_PACKAGES=$(grep -c "go: downloading" "$DOWNLOAD_LOG" 2>/dev/null || echo "0")
|
||
SUCCESS_COUNT=$(grep -c "go: downloading" "$DOWNLOAD_LOG" 2>/dev/null || echo "0")
|
||
|
||
echo ""
|
||
echo -e "${GREEN}✓ 依赖下载完成${NC}"
|
||
echo -e "${BLUE}下载统计:${NC}"
|
||
echo -e " 总包数: ${GREEN}${TOTAL_PACKAGES}${NC}"
|
||
echo -e " 成功下载: ${GREEN}${SUCCESS_COUNT}${NC}"
|
||
echo -e " 总耗时: ${GREEN}${DOWNLOAD_DURATION}${NC}秒"
|
||
|
||
if [ "$DOWNLOAD_DURATION" -gt 0 ] && [ "$SUCCESS_COUNT" -gt 0 ]; then
|
||
AVG_SPEED=$(echo "scale=2; $SUCCESS_COUNT / $DOWNLOAD_DURATION" | bc 2>/dev/null || echo "0")
|
||
echo -e " 平均速度: ${GREEN}${AVG_SPEED}${NC} 包/秒"
|
||
fi
|
||
|
||
# 显示最后下载的几个包
|
||
echo -e "${BLUE}最后下载的包:${NC}"
|
||
grep "go: downloading" "$DOWNLOAD_LOG" 2>/dev/null | tail -10 | sed 's/go: downloading \([^ ]*\).*/ ✓ \1/' | while IFS= read -r line; do
|
||
echo "$line"
|
||
done || true
|
||
else
|
||
# 停止进度显示进程
|
||
touch "${DOWNLOAD_PROGRESS_LOG}.done"
|
||
sleep 2
|
||
kill "$PROGRESS_PID" 2>/dev/null || true
|
||
wait "$PROGRESS_PID" 2>/dev/null || true
|
||
|
||
# 清理管道文件
|
||
rm -f "$PIPE_FILE" 2>/dev/null || true
|
||
|
||
DOWNLOAD_EXIT_CODE=${DOWNLOAD_EXIT_CODE:-$?}
|
||
DOWNLOAD_END_TIME=$(date +%s)
|
||
DOWNLOAD_DURATION=$((DOWNLOAD_END_TIME - DOWNLOAD_START_TIME))
|
||
|
||
# 统计已下载的包
|
||
DOWNLOADED_COUNT=$(grep -c "go: downloading" "$DOWNLOAD_LOG" 2>/dev/null || echo "0")
|
||
|
||
if [ "$DOWNLOAD_EXIT_CODE" = "124" ]; then
|
||
echo ""
|
||
echo -e "${YELLOW}⚠ 下载超时 (${DOWNLOAD_DURATION}秒)${NC}"
|
||
echo -e "${BLUE}已下载: ${GREEN}${DOWNLOADED_COUNT}${NC} 个包"
|
||
echo -e "${YELLOW}继续尝试编译,如果失败请检查网络连接${NC}"
|
||
else
|
||
echo ""
|
||
echo -e "${YELLOW}⚠ 使用国内镜像下载失败 (耗时: ${DOWNLOAD_DURATION}秒,退出码: ${DOWNLOAD_EXIT_CODE})${NC}"
|
||
echo -e "${BLUE}已下载: ${GREEN}${DOWNLOADED_COUNT}${NC} 个包"
|
||
echo -e "${YELLOW}最后30行日志:${NC}"
|
||
tail -30 "$DOWNLOAD_LOG" 2>/dev/null || true
|
||
echo ""
|
||
echo -e "${YELLOW}尝试使用 direct 模式(直接从 GitHub/GitLab 等下载)...${NC}"
|
||
# 如果失败,尝试使用 direct 模式,直接从源码仓库下载
|
||
# 注意:不使用 proxy.golang.org,因为在中国大陆无法访问
|
||
GO_ENV="$GO_PATH_ENV GOPROXY=direct GOSUMDB=off GOTIMEOUT=60 GONOPROXY= GONOSUMDB= GOPRIVATE=*"
|
||
|
||
# 如果配置了 GitHub 代理,添加到环境变量
|
||
if [ -n "$GITHUB_PROXY" ]; then
|
||
GO_ENV="$GO_ENV HTTP_PROXY=${GITHUB_PROXY}/ HTTPS_PROXY=${GITHUB_PROXY}/ http_proxy=${GITHUB_PROXY}/ https_proxy=${GITHUB_PROXY}/"
|
||
fi
|
||
|
||
# 重新设置环境变量
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOPROXY=direct" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOSUMDB=off" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GONOPROXY=" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GONOSUMDB=" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOPRIVATE=*" 2>/dev/null || true
|
||
sudo bash -c "cd '$SOURCE_DIR' && $GO_PATH_ENV && go env -w GOTIMEOUT=60" 2>/dev/null || true
|
||
|
||
# 重新启动进度显示
|
||
(
|
||
DOWNLOADED_COUNT=0
|
||
LAST_UPDATE_TIME=$(date +%s)
|
||
LAST_COUNT=0
|
||
|
||
while [ ! -f "$DOWNLOAD_PROGRESS_LOG.done2" ]; do
|
||
sleep 1
|
||
|
||
CURRENT_COUNT=$(grep -c "go: downloading" "$DOWNLOAD_LOG" 2>/dev/null || echo "0")
|
||
|
||
if [ "$CURRENT_COUNT" -gt "$LAST_COUNT" ]; then
|
||
DOWNLOADED_COUNT=$CURRENT_COUNT
|
||
CURRENT_TIME=$(date +%s)
|
||
ELAPSED=$((CURRENT_TIME - DOWNLOAD_START_TIME))
|
||
|
||
if [ "$ELAPSED" -gt 0 ]; then
|
||
SPEED=$(echo "scale=2; $DOWNLOADED_COUNT / $ELAPSED" | bc 2>/dev/null || echo "0")
|
||
echo -ne "\r${BLUE}进度: 已下载 ${GREEN}${DOWNLOADED_COUNT}${NC} 个包 | 耗时: ${ELAPSED}秒 | 速度: ${GREEN}${SPEED}${NC} 包/秒"
|
||
fi
|
||
|
||
LAST_COUNT=$CURRENT_COUNT
|
||
fi
|
||
|
||
CURRENT_PACKAGE=$(grep "go: downloading" "$DOWNLOAD_LOG" | tail -1 | sed 's/go: downloading //' | cut -d' ' -f1)
|
||
if [ -n "$CURRENT_PACKAGE" ] && [ "$CURRENT_PACKAGE" != "$LAST_PACKAGE" ]; then
|
||
echo ""
|
||
echo -e "${BLUE} → 正在下载: ${GREEN}${CURRENT_PACKAGE}${NC}"
|
||
LAST_PACKAGE=$CURRENT_PACKAGE
|
||
fi
|
||
done
|
||
) &
|
||
PROGRESS_PID2=$!
|
||
|
||
# 重新创建管道
|
||
rm -f "$PIPE_FILE" 2>/dev/null || true
|
||
mkfifo "$PIPE_FILE" 2>/dev/null || true
|
||
|
||
# 重新启动进度显示
|
||
(
|
||
DOWNLOADED_COUNT=0
|
||
LAST_COUNT=0
|
||
LAST_PACKAGE=""
|
||
|
||
while IFS= read -r line || [ -n "$line" ]; do
|
||
if [ -f "${DOWNLOAD_PROGRESS_LOG}.done2" ]; then
|
||
break
|
||
fi
|
||
|
||
if echo "$line" | grep -q "go: downloading"; then
|
||
DOWNLOADED_COUNT=$((DOWNLOADED_COUNT + 1))
|
||
CURRENT_TIME=$(date +%s)
|
||
ELAPSED=$((CURRENT_TIME - DOWNLOAD_START_TIME))
|
||
|
||
PACKAGE_NAME=$(echo "$line" | sed 's/go: downloading //' | cut -d' ' -f1)
|
||
if [ -n "$PACKAGE_NAME" ] && [ "$PACKAGE_NAME" != "$LAST_PACKAGE" ]; then
|
||
LAST_PACKAGE=$PACKAGE_NAME
|
||
|
||
if [ "$ELAPSED" -gt 0 ]; then
|
||
SPEED=$(echo "scale=2; $DOWNLOADED_COUNT / $ELAPSED" | bc 2>/dev/null || echo "0")
|
||
else
|
||
SPEED="0"
|
||
fi
|
||
|
||
echo -ne "\r${BLUE}进度: 已下载 ${GREEN}${DOWNLOADED_COUNT}${NC} 个包 | 耗时: ${ELAPSED}秒 | 速度: ${GREEN}${SPEED}${NC} 包/秒 | 当前: ${GREEN}${PACKAGE_NAME}${NC} "
|
||
fi
|
||
fi
|
||
done < "$PIPE_FILE" &
|
||
|
||
while [ ! -f "${DOWNLOAD_PROGRESS_LOG}.done2" ]; do
|
||
sleep 0.5
|
||
done
|
||
) &
|
||
PROGRESS_PID2=$!
|
||
|
||
DOWNLOAD_START_TIME=$(date +%s)
|
||
if sudo bash -c "cd '$SOURCE_DIR' && $GO_ENV && timeout 600 go mod download -x" 2>&1 | tee "$DOWNLOAD_LOG" "$PIPE_FILE"; then
|
||
# 停止进度显示进程
|
||
touch "${DOWNLOAD_PROGRESS_LOG}.done2"
|
||
sleep 2
|
||
kill "$PROGRESS_PID2" 2>/dev/null || true
|
||
wait "$PROGRESS_PID2" 2>/dev/null || true
|
||
rm -f "$PIPE_FILE" 2>/dev/null || true
|
||
# 停止进度显示进程
|
||
touch "$DOWNLOAD_PROGRESS_LOG.done2"
|
||
sleep 1
|
||
kill "$PROGRESS_PID2" 2>/dev/null || true
|
||
wait "$PROGRESS_PID2" 2>/dev/null || true
|
||
|
||
DOWNLOAD_END_TIME=$(date +%s)
|
||
DOWNLOAD_DURATION=$((DOWNLOAD_END_TIME - DOWNLOAD_START_TIME))
|
||
|
||
# 统计下载信息
|
||
TOTAL_PACKAGES=$(grep -c '"Path":' "$DOWNLOAD_LOG" 2>/dev/null || echo "0")
|
||
SUCCESS_COUNT=$(grep -c '"Version":' "$DOWNLOAD_LOG" 2>/dev/null || echo "0")
|
||
|
||
echo ""
|
||
echo -e "${GREEN}✓ 依赖下载完成${NC}"
|
||
echo -e "${BLUE}下载统计:${NC}"
|
||
echo -e " 总包数: ${GREEN}${TOTAL_PACKAGES}${NC}"
|
||
echo -e " 成功下载: ${GREEN}${SUCCESS_COUNT}${NC}"
|
||
echo -e " 总耗时: ${GREEN}${DOWNLOAD_DURATION}${NC}秒"
|
||
|
||
if [ "$DOWNLOAD_DURATION" -gt 0 ] && [ "$SUCCESS_COUNT" -gt 0 ]; then
|
||
AVG_SPEED=$(echo "scale=2; $SUCCESS_COUNT / $DOWNLOAD_DURATION" | bc 2>/dev/null || echo "0")
|
||
echo -e " 平均速度: ${GREEN}${AVG_SPEED}${NC} 包/秒"
|
||
fi
|
||
else
|
||
echo -e "${RED}下载依赖失败,最后100行日志:${NC}"
|
||
tail -100 "$DOWNLOAD_LOG" 2>/dev/null || true
|
||
rm -f "$DOWNLOAD_LOG"
|
||
show_build_alternatives
|
||
exit 1
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
rm -f "$DOWNLOAD_LOG" 2>/dev/null || true
|
||
|
||
# 编译到临时文件(在用户有权限的目录),然后移动到目标位置
|
||
echo -e "${BLUE}编译二进制文件...${NC}"
|
||
TEMP_BINARY=$(mktemp)
|
||
BINARY_PATH="$SOURCE_DIR/agent"
|
||
|
||
# 使用 sudo 以 root 用户编译,直接输出到目标位置,确保 PATH 包含 Go,并设置 GOPROXY
|
||
echo -e "${BLUE}开始编译(架构: ${ARCH})...${NC}"
|
||
if sudo bash -c "cd '$SOURCE_DIR' && $GO_ENV && GOOS=linux GOARCH=${ARCH} CGO_ENABLED=0 go build -v -buildvcs=false -ldflags='-w -s' -o '$BINARY_PATH' ./cmd/agent" 2>&1 | tee /tmp/go_build.log; then
|
||
if [ -f "$BINARY_PATH" ] && [ -s "$BINARY_PATH" ]; then
|
||
sudo chmod +x "$BINARY_PATH"
|
||
BINARY_SIZE=$(du -h "$BINARY_PATH" | cut -f1)
|
||
echo -e "${GREEN}✓ 编译成功 (文件大小: ${BINARY_SIZE})${NC}"
|
||
rm -f /tmp/go_build.log 2>/dev/null || true
|
||
else
|
||
echo -e "${RED}编译失败:未生成二进制文件${NC}"
|
||
echo -e "${YELLOW}编译日志:${NC}"
|
||
cat /tmp/go_build.log 2>/dev/null || true
|
||
show_build_alternatives
|
||
exit 1
|
||
fi
|
||
else
|
||
echo -e "${RED}编译失败,编译日志:${NC}"
|
||
cat /tmp/go_build.log 2>/dev/null || true
|
||
rm -f /tmp/go_build.log 2>/dev/null || true
|
||
show_build_alternatives
|
||
exit 1
|
||
fi
|
||
|
||
# 清理临时文件
|
||
rm -f "$TEMP_BINARY" 2>/dev/null || true
|
||
|
||
# 复制到安装目录(可选,保留在源码目录供 run.sh 使用)
|
||
sudo mkdir -p "$INSTALL_DIR"
|
||
sudo cp "$BINARY_PATH" "$INSTALL_DIR/$BINARY_NAME"
|
||
sudo chmod +x "$INSTALL_DIR/$BINARY_NAME"
|
||
|
||
echo -e "${GREEN}✓ 编译安装完成${NC}"
|
||
echo -e "${BLUE}源码目录: ${SOURCE_DIR}${NC}"
|
||
echo -e "${BLUE}二进制文件: ${INSTALL_DIR}/${BINARY_NAME}${NC}"
|
||
}
|
||
|
||
# 创建 systemd 服务
|
||
create_service() {
|
||
echo -e "${BLUE}创建 systemd 服务...${NC}"
|
||
|
||
# 确保启动脚本有执行权限
|
||
sudo chmod +x "$SOURCE_DIR/run.sh"
|
||
sudo chmod +x "$SOURCE_DIR/start-systemd.sh"
|
||
|
||
sudo tee /etc/systemd/system/${SERVICE_NAME}.service > /dev/null <<EOF
|
||
[Unit]
|
||
Description=LinkMaster Node Service
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=root
|
||
WorkingDirectory=$SOURCE_DIR
|
||
ExecStart=$SOURCE_DIR/start-systemd.sh
|
||
Restart=always
|
||
RestartSec=5
|
||
Environment="BACKEND_URL=$BACKEND_URL"
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
sudo systemctl daemon-reload
|
||
echo -e "${GREEN}✓ 服务创建完成${NC}"
|
||
}
|
||
|
||
# 配置防火墙规则
|
||
configure_firewall() {
|
||
echo -e "${BLUE}配置防火墙规则(开放2200端口)...${NC}"
|
||
|
||
PORT=2200
|
||
FIREWALL_CONFIGURED=false
|
||
|
||
# 检测 firewalld (CentOS/RHEL 7+, Fedora)
|
||
if command -v firewall-cmd > /dev/null 2>&1; then
|
||
if sudo systemctl is-active --quiet firewalld 2>/dev/null; then
|
||
echo -e "${BLUE}检测到 firewalld,添加端口规则...${NC}"
|
||
if sudo firewall-cmd --permanent --add-port=${PORT}/tcp > /dev/null 2>&1; then
|
||
sudo firewall-cmd --reload > /dev/null 2>&1
|
||
echo -e "${GREEN}✓ firewalld 规则已添加${NC}"
|
||
FIREWALL_CONFIGURED=true
|
||
else
|
||
echo -e "${YELLOW}⚠ firewalld 规则添加失败(可能需要手动配置)${NC}"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# 检测 ufw (Ubuntu/Debian)
|
||
if command -v ufw > /dev/null 2>&1; then
|
||
if sudo ufw status > /dev/null 2>&1; then
|
||
echo -e "${BLUE}检测到 ufw,添加端口规则...${NC}"
|
||
if sudo ufw allow ${PORT}/tcp > /dev/null 2>&1; then
|
||
echo -e "${GREEN}✓ ufw 规则已添加${NC}"
|
||
FIREWALL_CONFIGURED=true
|
||
else
|
||
echo -e "${YELLOW}⚠ ufw 规则添加失败(可能需要手动配置)${NC}"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# 检测 iptables(较老的系统)
|
||
if command -v iptables > /dev/null 2>&1 && [ "$FIREWALL_CONFIGURED" = false ]; then
|
||
# 检查是否有iptables规则(说明防火墙可能在使用)
|
||
if sudo iptables -L -n 2>/dev/null | grep -q "Chain INPUT"; then
|
||
echo -e "${BLUE}检测到 iptables,添加端口规则...${NC}"
|
||
# 检查规则是否已存在
|
||
if ! sudo iptables -C INPUT -p tcp --dport ${PORT} -j ACCEPT > /dev/null 2>&1; then
|
||
if sudo iptables -I INPUT -p tcp --dport ${PORT} -j ACCEPT > /dev/null 2>&1; then
|
||
echo -e "${GREEN}✓ iptables 规则已添加${NC}"
|
||
# 尝试保存规则(不同系统保存方式不同)
|
||
if command -v iptables-save > /dev/null 2>&1; then
|
||
if [ -f /etc/redhat-release ]; then
|
||
sudo iptables-save > /etc/sysconfig/iptables 2>/dev/null || true
|
||
elif [ -f /etc/debian_version ]; then
|
||
sudo iptables-save > /etc/iptables/rules.v4 2>/dev/null || true
|
||
fi
|
||
fi
|
||
FIREWALL_CONFIGURED=true
|
||
else
|
||
echo -e "${YELLOW}⚠ iptables 规则添加失败(可能需要手动配置)${NC}"
|
||
fi
|
||
else
|
||
echo -e "${GREEN}✓ iptables 规则已存在${NC}"
|
||
FIREWALL_CONFIGURED=true
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
if [ "$FIREWALL_CONFIGURED" = false ]; then
|
||
echo -e "${YELLOW}⚠ 未检测到活动的防火墙,或防火墙未启用${NC}"
|
||
echo -e "${YELLOW} 如果节点无法远程访问,请手动开放端口 ${PORT}/tcp${NC}"
|
||
fi
|
||
}
|
||
|
||
# 登记节点(调用心跳API获取节点信息)
|
||
register_node() {
|
||
echo -e "${BLUE}登记节点到后端服务器...${NC}"
|
||
|
||
# 创建临时配置文件
|
||
CONFIG_FILE="$SOURCE_DIR/config.yaml"
|
||
sudo mkdir -p "$SOURCE_DIR"
|
||
|
||
# 创建基础配置文件
|
||
sudo tee "$CONFIG_FILE" > /dev/null <<EOF
|
||
server:
|
||
port: 2200
|
||
backend:
|
||
url: ${BACKEND_URL}
|
||
heartbeat:
|
||
interval: 60
|
||
debug: false
|
||
node:
|
||
id: 0
|
||
ip: ""
|
||
country: ""
|
||
province: ""
|
||
city: ""
|
||
isp: ""
|
||
EOF
|
||
|
||
# 调用心跳API获取节点信息
|
||
echo -e "${BLUE}发送心跳请求获取节点信息...${NC}"
|
||
RESPONSE=$(curl -s -X POST "${BACKEND_URL}/api/node/heartbeat" \
|
||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||
-d "type=pingServer" 2>&1)
|
||
|
||
if [ $? -eq 0 ]; then
|
||
# 尝试解析JSON响应
|
||
NODE_ID=$(echo "$RESPONSE" | grep -o '"node_id":[0-9]*' | grep -o '[0-9]*' | head -1)
|
||
NODE_IP=$(echo "$RESPONSE" | grep -o '"node_ip":"[^"]*"' | cut -d'"' -f4 | head -1)
|
||
COUNTRY=$(echo "$RESPONSE" | grep -o '"country":"[^"]*"' | cut -d'"' -f4 | head -1)
|
||
PROVINCE=$(echo "$RESPONSE" | grep -o '"province":"[^"]*"' | cut -d'"' -f4 | head -1)
|
||
CITY=$(echo "$RESPONSE" | grep -o '"city":"[^"]*"' | cut -d'"' -f4 | head -1)
|
||
ISP=$(echo "$RESPONSE" | grep -o '"isp":"[^"]*"' | cut -d'"' -f4 | head -1)
|
||
|
||
if [ -n "$NODE_ID" ] && [ "$NODE_ID" != "0" ] && [ -n "$NODE_IP" ]; then
|
||
# 更新配置文件
|
||
sudo tee "$CONFIG_FILE" > /dev/null <<EOF
|
||
server:
|
||
port: 2200
|
||
backend:
|
||
url: ${BACKEND_URL}
|
||
heartbeat:
|
||
interval: 60
|
||
debug: false
|
||
node:
|
||
id: ${NODE_ID}
|
||
ip: ${NODE_IP}
|
||
country: ${COUNTRY:-""}
|
||
province: ${PROVINCE:-""}
|
||
city: ${CITY:-""}
|
||
isp: ${ISP:-""}
|
||
EOF
|
||
echo -e "${GREEN}✓ 节点登记成功${NC}"
|
||
echo -e "${BLUE} 节点ID: ${NODE_ID}${NC}"
|
||
echo -e "${BLUE} 节点IP: ${NODE_IP}${NC}"
|
||
if [ -n "$COUNTRY" ] || [ -n "$PROVINCE" ] || [ -n "$CITY" ]; then
|
||
echo -e "${BLUE} 位置: ${COUNTRY:-""}/${PROVINCE:-""}/${CITY:-""}${NC}"
|
||
fi
|
||
else
|
||
echo -e "${YELLOW}⚠ 无法从响应中解析节点信息,将在服务启动时重试${NC}"
|
||
echo -e "${YELLOW} 响应: ${RESPONSE}${NC}"
|
||
fi
|
||
else
|
||
echo -e "${YELLOW}⚠ 心跳请求失败,将在服务启动时重试${NC}"
|
||
echo -e "${YELLOW} 错误: ${RESPONSE}${NC}"
|
||
fi
|
||
|
||
# 设置配置文件权限
|
||
sudo chmod 644 "$CONFIG_FILE"
|
||
}
|
||
|
||
# 启动服务
|
||
start_service() {
|
||
echo -e "${BLUE}启动服务...${NC}"
|
||
|
||
sudo systemctl enable ${SERVICE_NAME} > /dev/null 2>&1
|
||
sudo systemctl restart ${SERVICE_NAME}
|
||
|
||
# 等待服务启动
|
||
sleep 3
|
||
|
||
# 检查服务状态
|
||
if sudo systemctl is-active --quiet ${SERVICE_NAME}; then
|
||
echo -e "${GREEN}✓ 服务启动成功${NC}"
|
||
else
|
||
echo -e "${RED}✗ 服务启动失败${NC}"
|
||
echo -e "${YELLOW}查看日志: sudo journalctl -u ${SERVICE_NAME} -n 50${NC}"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 验证安装
|
||
verify_installation() {
|
||
echo -e "${BLUE}验证安装...${NC}"
|
||
|
||
# 检查进程
|
||
if pgrep -f "$BINARY_NAME" > /dev/null; then
|
||
echo -e "${GREEN}✓ 进程运行中${NC}"
|
||
else
|
||
echo -e "${YELLOW}⚠ 进程未运行${NC}"
|
||
fi
|
||
|
||
# 检查端口
|
||
if command -v netstat > /dev/null 2>&1; then
|
||
if netstat -tlnp 2>/dev/null | grep -q ":2200"; then
|
||
echo -e "${GREEN}✓ 端口 2200 已监听${NC}"
|
||
fi
|
||
elif command -v ss > /dev/null 2>&1; then
|
||
if ss -tlnp 2>/dev/null | grep -q ":2200"; then
|
||
echo -e "${GREEN}✓ 端口 2200 已监听${NC}"
|
||
fi
|
||
fi
|
||
|
||
# 健康检查(重试多次,给服务启动时间)
|
||
echo -e "${BLUE}等待服务启动并检查健康状态...${NC}"
|
||
HEALTH_CHECK_PASSED=false
|
||
for i in {1..10}; do
|
||
sleep 2
|
||
if curl -sf http://localhost:2200/api/health > /dev/null 2>&1; then
|
||
HEALTH_RESPONSE=$(curl -s http://localhost:2200/api/health 2>/dev/null || echo "")
|
||
if echo "$HEALTH_RESPONSE" | grep -q '"status":"ok"'; then
|
||
HEALTH_CHECK_PASSED=true
|
||
echo -e "${GREEN}✓ 健康检查通过${NC}"
|
||
break
|
||
fi
|
||
fi
|
||
if [ $i -lt 10 ]; then
|
||
echo -e "${BLUE}等待服务启动... ($i/10)${NC}"
|
||
fi
|
||
done
|
||
|
||
if [ "$HEALTH_CHECK_PASSED" = false ]; then
|
||
echo -e "${YELLOW}⚠ 健康检查未通过${NC}"
|
||
echo -e "${YELLOW}请检查服务日志: sudo journalctl -u ${SERVICE_NAME} -n 50${NC}"
|
||
echo -e "${YELLOW}或手动测试: curl http://localhost:2200/api/health${NC}"
|
||
fi
|
||
}
|
||
|
||
# 主安装流程
|
||
main() {
|
||
echo -e "${GREEN}========================================${NC}"
|
||
echo -e "${GREEN} LinkMaster 节点端安装程序${NC}"
|
||
echo -e "${GREEN}========================================${NC}"
|
||
echo ""
|
||
|
||
detect_system
|
||
|
||
# 检查是否已安装,如果已安装则先卸载
|
||
if check_installed; then
|
||
uninstall_service
|
||
fi
|
||
|
||
# 检测并配置最快的镜像源(在安装依赖之前)
|
||
detect_fastest_mirror
|
||
|
||
install_dependencies
|
||
build_from_source
|
||
create_service
|
||
configure_firewall
|
||
register_node
|
||
start_service
|
||
verify_installation
|
||
|
||
echo ""
|
||
echo -e "${GREEN}========================================${NC}"
|
||
echo -e "${GREEN} 安装完成!${NC}"
|
||
echo -e "${GREEN}========================================${NC}"
|
||
echo ""
|
||
echo -e "${BLUE}服务管理命令:${NC}"
|
||
echo " 查看状态: sudo systemctl status ${SERVICE_NAME}"
|
||
echo " 查看日志: sudo journalctl -u ${SERVICE_NAME} -f"
|
||
echo " 重启服务: sudo systemctl restart ${SERVICE_NAME}"
|
||
echo " 停止服务: sudo systemctl stop ${SERVICE_NAME}"
|
||
echo ""
|
||
echo -e "${BLUE}后端地址: ${BACKEND_URL}${NC}"
|
||
echo -e "${BLUE}节点端口: 2200${NC}"
|
||
echo ""
|
||
echo -e "${YELLOW}重要提示:${NC}"
|
||
echo " - 节点端直接连接后端服务器,不使用前端代理"
|
||
echo " - 确保后端地址可访问: curl ${BACKEND_URL}/api/public/nodes/online"
|
||
}
|
||
|
||
# 执行安装
|
||
main
|