# LinkMaster Node

LinkMaster 节点服务，用于执行网络测试任务。

## 功能

- HTTP GET/POST 测试
- Ping 测试
- DNS 查询
- Traceroute 路由追踪
- Socket 连接测试
- TCPing 端口延迟测试
- FindPing IP段批量ping检测
- 持续 Ping/TCPing 测试
- 心跳上报
- 日志文件输出（支持配置日志文件路径和级别）
- 心跳故障排查工具

## 安装

### 方式一：一键安装（推荐）

从 GitHub 一键安装，只需指定后端服务器地址。安装脚本会自动克隆源码并编译安装：

```bash
# 基本用法（替换为实际的 GitHub 仓库地址）
curl -fsSL https://raw.githubusercontent.com/yourbask/linkmaster-node/main/install.sh | bash -s -- http://your-backend-server:8080

# 示例
# 节点和后端在同一服务器
curl -fsSL https://raw.githubusercontent.com/yourbask/linkmaster-node/main/install.sh | bash -s -- http://localhost:8080

# 节点和后端在不同服务器
curl -fsSL https://raw.githubusercontent.com/yourbask/linkmaster-node/main/install.sh | bash -s -- http://192.168.1.100:8080
```

**重要说明：**
- 节点端需要直接连接后端服务器（端口 8080），不是前端地址
- 前端通过 `/api` 路径代理到后端，但节点端不使用代理
- 如果后端在公网，使用公网 IP 或域名
- 如果后端在内网，使用内网 IP
- **本项目是独立的 GitHub 仓库**，与前后端项目分离

**安装说明：**
- 自动检测系统类型和架构
- 自动安装系统依赖（包括 Git 和 Go）
- 从 GitHub 克隆源码到 `/opt/linkmaster-node`
- 自动编译并安装二进制文件
- 自动创建 systemd 服务
- 自动启动服务并验证

**指定分支安装：**
```bash
GITHUB_BRANCH=develop curl -fsSL https://raw.githubusercontent.com/yourbask/linkmaster-node/main/install.sh | bash -s -- http://your-backend-server:8080
```

### 方式二：手动编译安装

```bash
# 克隆仓库
git clone https://github.com/yourbask/linkmaster-node.git
cd linkmaster-node

# 编译
go build -o agent ./cmd/agent

# 安装（需要root权限）
sudo cp agent /usr/local/bin/linkmaster-node
sudo chmod +x /usr/local/bin/linkmaster-node

# 或使用安装脚本
./install.sh http://your-backend-server:8080
```

### 方式三：手动运行

```bash
# 克隆仓库
git clone https://github.com/yourbask/linkmaster-node.git
cd linkmaster-node

# 运行（会自动拉取最新代码并编译）
BACKEND_URL=http://your-backend-server:8080 ./run.sh start
```

## 配置

### 配置优先级

配置按以下优先级加载（高优先级会覆盖低优先级）：

1. **环境变量**（最高优先级）
2. **配置文件** `config.yaml`
3. **默认值**

### 环境变量

- `BACKEND_URL`: 后端服务地址（**优先级最高**，会覆盖配置文件中的设置）
- `CONFIG_PATH`: 配置文件路径（可选，默认: config.yaml）
- `LOG_FILE`: 日志文件路径（可选，默认: node.log）

**重要说明：**
- `BACKEND_URL` 环境变量会**覆盖**配置文件中的 `backend.url` 设置
- 即使配置文件存在，设置环境变量后也会优先使用环境变量的值
- 这确保了编译后的二进制文件不会硬编码后端地址

### 配置文件（可选）

创建 `config.yaml` 文件：

```yaml
server:
  port: 2200
backend:
  url: http://your-backend-server:8080  # 会被 BACKEND_URL 环境变量覆盖
heartbeat:
  interval: 60
log:
  file: node.log        # 日志文件路径（默认: node.log，空则输出到标准错误）
  level: info           # 日志级别: debug, info, warn, error（默认: info）
debug: false
node:
  id: 0                 # 节点ID（通过心跳自动获取）
  ip: ""                # 节点IP（通过心跳自动获取）
  country: ""           # 国家（通过心跳自动获取）
  province: ""          # 省份（通过心跳自动获取）
  city: ""              # 城市（通过心跳自动获取）
  isp: ""               # ISP（通过心跳自动获取）
```

**配置说明：**
- `backend.url`: 后端服务地址，会被 `BACKEND_URL` 环境变量覆盖
- `log.file`: 日志文件路径。如果为空，日志将输出到标准错误（stderr）
- `log.level`: 日志级别，支持 `debug`、`info`、`warn`、`error`
- `node.*`: 节点信息通过心跳自动获取并保存，无需手动配置
- 配置文件不会被编译进二进制文件，是运行时读取的

## 运行脚本

使用 `run.sh` 脚本管理节点端。**每次启动时会自动拉取最新代码并重新编译**：

```bash
# 启动服务（会自动拉取最新代码并编译）
./run.sh start

# 停止服务
./run.sh stop

# 重启服务（会拉取最新代码并重新编译）
./run.sh restart

# 查看状态
./run.sh status

# 查看日志
./run.sh logs

# 查看完整日志
./run.sh logs-all

# 指定后端地址启动
BACKEND_URL=http://192.168.1.100:8080 ./run.sh start
```

**重要提示：**
- 启动脚本会自动执行 `git pull` 拉取最新代码
- 拉取代码后会自动执行 `go mod download` 更新依赖
- 然后自动编译生成新的二进制文件
- 如果 Git 拉取失败（如网络问题），会使用当前代码继续编译
- 如果编译失败，服务不会启动

## 脚本工具

项目提供了多个脚本工具，方便安装、卸载、运行、编译和发布。

### 1. install.sh - 一键安装脚本

自动安装 LinkMaster 节点端到系统，包括依赖安装、源码编译、systemd 服务配置等。

**使用方法：**

```bash
# 通过 curl 下载并安装（推荐）
curl -fsSL https://gitee.nas.cpolar.cn/yoyo/linkmaster-node/raw/branch/main/install.sh | bash -s -- http://your-backend-server:8080

# 本地运行安装脚本
./install.sh http://your-backend-server:8080

# 指定分支安装
GITHUB_BRANCH=develop curl -fsSL https://gitee.nas.cpolar.cn/yoyo/linkmaster-node/raw/branch/main/install.sh | bash -s -- http://your-backend-server:8080
```

**功能特性：**
- 自动检测系统类型和架构（Linux/macOS, amd64/arm64）
- 自动检测并配置最快的镜像源（Ubuntu/Debian/CentOS）
- 自动安装系统依赖（curl, wget, git, ping, traceroute 等）
- 自动安装 Go 环境（优先使用系统包管理器，失败则从官网下载）
- 优先从 Releases 下载预编译二进制文件，失败则从源码编译
- **发布包包含所有必要文件**：二进制文件、安装脚本、运行脚本等，无需从 Git 拉取
- 自动创建 systemd 服务并配置自启动
- 自动配置防火墙规则（开放 2200 端口）
- 自动登记节点到后端服务器
- 自动启动服务并验证安装

**安装位置：**
- 二进制文件：`/usr/local/bin/linkmaster-node`
- 源码目录：`/opt/linkmaster-node`
- 服务文件：`/etc/systemd/system/linkmaster-node.service`
- 配置文件：`/opt/linkmaster-node/config.yaml`

### 2. uninstall.sh - 一键卸载脚本

完全卸载 LinkMaster 节点端，包括停止服务、删除文件、清理配置等。

**使用方法：**

```bash
# 通过 curl 下载并运行
curl -fsSL https://gitee.nas.cpolar.cn/yoyo/linkmaster-node/raw/branch/main/uninstall.sh | bash

# 本地运行卸载脚本
./uninstall.sh

# 卸载并删除防火墙规则
./uninstall.sh --remove-firewall
```

**功能特性：**
- 停止并禁用 systemd 服务
- 删除 systemd 服务文件和配置目录
- 删除二进制文件（`/usr/local/bin/linkmaster-node`）
- 删除源码目录（`/opt/linkmaster-node`）
- 清理所有残留进程
- 重新加载 systemd daemon
- 可选：删除防火墙规则（默认保留）

**注意事项：**
- 卸载前会询问确认（交互式环境）
- 默认保留防火墙规则，避免影响其他服务
- 使用 `--remove-firewall` 参数可删除防火墙规则

### 3. run.sh - 运行管理脚本

用于管理节点端的启动、停止、重启、状态查看和日志查看。**每次启动时会自动拉取最新代码并重新编译**。

**使用方法：**

```bash
# 启动服务（会自动拉取最新代码并编译）
./run.sh start

# 停止服务
./run.sh stop

# 重启服务（会拉取最新代码并重新编译）
./run.sh restart

# 查看运行状态
./run.sh status

# 实时查看日志
./run.sh logs

# 查看完整日志
./run.sh logs-all

# 显示帮助信息
./run.sh help

# 指定后端地址启动
BACKEND_URL=http://192.168.1.100:8080 ./run.sh start
```

**功能特性：**
- 启动时自动执行 `git pull` 拉取最新代码
- 自动执行 `go mod download` 更新依赖
- 自动编译生成新的二进制文件
- 自动检测端口占用并提示处理
- 后台运行并保存 PID 文件
- 健康检查验证服务状态
- 支持通过环境变量 `BACKEND_URL` 指定后端地址

**环境变量：**
- `BACKEND_URL`: 后端服务地址（默认: `http://localhost:8080`）

### 4. start-systemd.sh - systemd 启动脚本

用于 systemd 服务启动，直接运行二进制文件。如果二进制文件不存在，会自动拉取代码并编译。

**使用场景：**
- 由 systemd 服务自动调用
- 不需要手动运行

**功能特性：**
- 检查二进制文件是否存在且有效
- 如果二进制文件不存在，自动拉取代码并编译
- 使用 vendor 目录编译（无需网络连接）
- 直接运行二进制文件（systemd 管理进程）

**注意事项：**
- 此脚本由 systemd 服务调用，通常不需要手动运行
- 需要确保源码目录存在且是 Git 仓库
- 需要 Go 环境已安装并在 PATH 中

### 5. all-build.sh - 跨平台编译脚本

编译多个操作系统和架构的二进制文件，支持并行编译。**版本号自动从 `version.json` 读取**。

**使用方法：**

```bash
# 编译所有平台（自动使用 version.json 中的版本号）
./all-build.sh

# 只编译指定平台
./all-build.sh -p linux/amd64

# 编译前清理输出目录
./all-build.sh -c

# 设置并行编译数量
./all-build.sh -j 2

# 覆盖版本号（覆盖 version.json 中的版本）
./all-build.sh -v 1.0.0

# 只生成不带版本号的文件
./all-build.sh -s

# 列出所有支持的平台
./all-build.sh -l

# 显示帮助信息
./all-build.sh -h
```

**支持的平台：**
- `linux/amd64` - Linux x86_64
- `linux/arm64` - Linux ARM64
- `darwin/amd64` - macOS Intel
- `darwin/arm64` - macOS Apple Silicon
- `windows/amd64` - Windows x86_64
- `windows/arm64` - Windows ARM64

**功能特性：**
- ✅ **自动从 `version.json` 读取版本号**（无需手动指定）
- ✅ 支持并行编译（默认 4 个任务）
- ✅ 自动生成带版本号和不带版本号的文件
- ✅ 输出到 `bin/` 目录
- ✅ 显示编译进度和结果
- ✅ 支持清理输出目录

**输出文件：**
- `bin/agent-{os}-{arch}` - 不带版本号的二进制文件
- `bin/agent-{os}-{arch}-{version}` - 带版本号的二进制文件
- Windows 平台会自动添加 `.exe` 扩展名

**版本管理：**
版本号统一从 `version.json` 文件读取：
```json
{
  "version": "1.1.3",
  "tag": "v1.1.3"
}
```

### 6. all-upload-release.sh - 发布上传脚本

将编译好的二进制文件上传到 Releases 或通过其他方式发布。**版本号和标签自动从 `version.json` 读取，Token 已硬编码**。

**使用方法：**

```bash
# 上传到 Gitea Releases（自动从 version.json 和 .git/config 读取信息）
./all-upload-release.sh -m gitea

# 上传到 Gitea Releases（覆盖版本号和标签）
./all-upload-release.sh -m gitea -t v1.2.0 -v 1.2.0

# 上传到 GitHub Releases
./all-upload-release.sh -m github -r owner/repo -t v1.0.0 -v 1.0.0

# 通过 SCP 上传
./all-upload-release.sh -m scp -H example.com -u user -d /path/to/release

# 通过 SCP 上传（指定私钥）
./all-upload-release.sh -m scp -H example.com -u user -d /path/to/release -k ~/.ssh/id_rsa

# 通过 FTP 上传
./all-upload-release.sh -m ftp -H ftp.example.com -u user -d /path/to/release

# 复制到本地目录
./all-upload-release.sh -m local -d /path/to/release

# 只打包不上传
./all-upload-release.sh --pack-only

# 不上传压缩包，直接上传二进制文件
./all-upload-release.sh -m scp --no-pack -H example.com -u user -d /path/to/release

# 显示帮助信息
./all-upload-release.sh -h
```

**支持的上传方式：**
- `gitea` - Gitea Releases（自动从 .git/config 读取仓库信息）
- `github` - GitHub Releases（需要 GitHub CLI `gh`）
- `scp` - 通过 SCP 上传到远程服务器
- `ftp` - 通过 FTP 上传
- `local` - 复制到本地目录

**功能特性：**
- ✅ **自动从 `version.json` 读取版本号和标签**（无需手动指定）
- ✅ **Token 已硬编码**（无需手动指定）
- ✅ **自动打包所有必要文件**：二进制文件、安装脚本、运行脚本、配置文件等
- ✅ 自动打包二进制文件（tar.gz 或 zip）
- ✅ 自动创建发布说明
- ✅ 支持指定平台上传
- ✅ 支持自定义版本号和标签（覆盖配置文件）
- ✅ 支持自定义发布说明
- ✅ 自动检测并处理已存在的 Release

**参数说明：**
- `-m, --method`: 上传方式（gitea|github|scp|ftp|local，默认: gitea）
- `-v, --version`: 版本号（默认: 从 version.json 读取）
- `-t, --tag`: Git 标签（默认: 从 version.json 读取）
- `-p, --platform`: 只上传指定平台
- `-T, --token`: 访问令牌（已硬编码，此选项已废弃）
- `-H, --host`: 主机地址（SCP/FTP）
- `-u, --user`: 用户名（SCP/FTP）
- `-d, --dest`: 目标路径（SCP/FTP/local）
- `-k, --key`: 私钥路径（SCP）
- `--pack-only`: 只打包不上传
- `--no-pack`: 不上传压缩包，直接上传二进制文件

**版本管理：**
版本号和标签统一从 `version.json` 文件读取：
```json
{
  "version": "1.1.3",
  "tag": "v1.1.3"
}
```

**典型工作流程：**
```bash
# 1. 编译所有平台（自动使用 version.json 中的版本号）
./all-build.sh

# 2. 上传到 Gitea Releases（自动使用 version.json 中的版本号和标签）
./all-upload-release.sh -m gitea
```

### 7. vendor.sh - Vendor 依赖打包脚本

将项目依赖下载到 vendor 目录，客户端克隆后可直接编译，无需网络连接。

**使用方法：**

```bash
# 运行脚本（会自动下载依赖并创建 vendor 目录）
./vendor.sh
```

**功能特性：**
- 检查 Go 环境
- 配置 Go 代理（使用官方源）
- 下载所有依赖包
- 创建 vendor 目录
- 更新 .gitignore（允许 vendor 目录被提交）
- 自动添加到 Git 暂存区

**使用场景：**
- 项目需要离线编译能力
- 需要确保依赖版本一致性
- 客户端环境网络受限

**注意事项：**
- 需要 Go 环境已安装
- vendor 目录会比较大，需要提交到 Git
- 编译时使用 `-mod=vendor` 标志

**编译命令（使用 vendor）：**
```bash
go build -mod=vendor -o agent ./cmd/agent
```

## API

### POST /api/test

统一测试接口

```json
{
  "type": "ceGet|cePost|cePing|ceDns|ceTrace|ceSocket|ceTCPing|ceFindPing",
  "url": "测试目标",
  "params": {}
}
```

### POST /api/continuous/start

启动持续测试

```json
{
  "type": "ping|tcping",
  "target": "测试目标",
  "interval": 10,
  "max_duration": 60
}
```

### POST /api/continuous/stop

停止持续测试

```json
{
  "task_id": "任务ID"
}
```

### GET /api/continuous/status?task_id=xxx

查询任务状态

### GET /api/health

健康检查

## 故障排查

### 心跳同步问题排查

如果节点无法同步心跳，可以使用排查脚本进行诊断：

```bash
# 运行心跳故障排查脚本
./check-heartbeat.sh
```

排查脚本会自动检查以下项目：

1. **进程状态** - 检查节点进程是否正在运行
2. **配置文件** - 检查配置文件是否存在和正确
3. **网络连接** - 检查能否连接到后端服务器
4. **日志分析** - 分析日志中的心跳相关错误
5. **手动测试** - 手动发送心跳测试连接
6. **系统资源** - 检查磁盘空间和内存使用情况

**常见问题及解决方案：**

1. **进程未运行**
   ```bash
   ./run.sh start
   ```

2. **网络连接失败**
   - 检查后端服务是否正常运行
   - 检查防火墙规则（确保可以访问后端端口）
   - 检查 BACKEND_URL 配置是否正确

3. **心跳发送失败**
   - 查看日志: `./run.sh logs`
   - 检查后端服务日志
   - 确认后端 `/api/node/heartbeat` 接口正常

4. **配置文件问题**
   - 检查 `config.yaml` 文件格式是否正确
   - 确认 `BACKEND_URL` 环境变量或配置文件中的 URL 正确

5. **查看详细日志**
   ```bash
   # 实时查看日志
   ./run.sh logs
   
   # 查看完整日志
   ./run.sh logs-all
   ```

### 日志功能

节点端支持将日志直接写入文件，便于排查问题和监控运行状态。

**日志配置方式：**

1. **环境变量**（推荐）
   ```bash
   LOG_FILE=/var/log/linkmaster-node.log ./run.sh start
   ```

2. **配置文件**
   在 `config.yaml` 中配置：
   ```yaml
   log:
     file: node.log        # 日志文件路径
     level: info           # 日志级别: debug, info, warn, error
   ```

3. **默认行为**
   - 默认日志文件：`node.log`（当前目录）
   - 默认日志级别：`info`
   - 如果未设置日志文件，日志输出到标准错误（stderr）

**日志特性：**
- ✅ 自动创建日志文件和目录
- ✅ 追加模式，不会覆盖已有日志
- ✅ JSON 格式，便于日志分析
- ✅ 包含调用信息（文件名和行号）
- ✅ Error 级别日志包含堆栈信息

**查看日志：**
```bash
# 实时查看日志
tail -f node.log

# 查看心跳相关日志
grep -i "心跳" node.log

# 查看错误日志
grep -i "error" node.log

# 查看最后100行
tail -n 100 node.log
```

## 心跳机制

节点会定期向后端发送心跳，上报节点状态和获取节点信息。

### 心跳请求字段

心跳请求包含以下字段：

- `type`: 固定值 `pingServer`
- `version`: 协议版本号，固定值 `2`
- `host_name`: 节点主机名（自动读取系统主机名）

### 心跳响应

心跳响应包含以下节点信息：

- `node_id`: 节点ID
- `node_ip`: 节点外网IP
- `country`: 国家
- `province`: 省份
- `city`: 城市
- `isp`: ISP

这些信息会自动保存到配置文件中，用于后续的数据推送。

## 持续测试功能

节点支持持续 Ping 和 TCPing 测试，测试结果会自动推送到后端服务器。

### 功能特性

- ✅ 实时推送测试结果到后端
- ✅ 批量推送优化（减少HTTP请求频率）
- ✅ 自动清理超时任务
- ✅ 资源自动清理（防止内存泄漏）
- ✅ 详细的调试日志（debug模式）

### 数据推送

- 测试结果会自动推送到后端 `/api/public/node/continuous/result` 接口
- 推送包含节点ID、IP、位置信息和测试结果
- 如果后端任务不存在，节点端会自动停止对应任务

## 更新日志

### v1.1.4 (最新)

**新增功能：**
- ✨ 心跳请求新增 `version` 字段（协议版本号，默认值：2）
- ✨ 心跳请求新增 `host_name` 字段（自动读取系统主机名）
- ✨ 支持环境变量 `BACKEND_URL` 覆盖配置文件中的后端地址
- ✨ 持续测试功能增强，支持批量推送和自动清理

**改进：**
- 🔧 修复持续测试数据推送的锁管理问题
- 🔧 修复任务停止时未清理推送缓冲的内存泄漏问题
- 🔧 优化配置加载逻辑，环境变量优先级最高
- 🔧 增强日志记录，添加详细的调试信息
- 📝 完善文档，添加配置优先级和心跳机制说明

### v1.1.3

**新增功能：**
- ✨ 添加日志文件输出功能，支持配置日志文件路径和级别
- ✨ 添加心跳故障排查工具 `check-heartbeat.sh`
- ✨ 支持通过环境变量 `LOG_FILE` 设置日志文件路径
- ✨ 日志自动创建目录，支持相对路径和绝对路径

**改进：**
- 🔧 优化日志初始化逻辑，支持直接写入文件
- 🔧 改进配置加载，支持日志配置项
- 📝 完善文档，添加故障排查章节

### v1.0.0

- 🎉 初始版本发布
- ✅ 支持 HTTP GET/POST 测试
- ✅ 支持 Ping、DNS、Traceroute 等网络测试
- ✅ 支持持续 Ping/TCPing 测试
- ✅ 支持心跳上报
