255 lines
6.5 KiB
Go
255 lines
6.5 KiB
Go
package handler
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/dushixiang/pika"
|
|
"github.com/dushixiang/pika/pkg/version"
|
|
"github.com/go-orz/orz"
|
|
"github.com/labstack/echo/v4"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// GetAgentVersion 获取 Agent 版本信息
|
|
func (h *AgentHandler) GetAgentVersion(c echo.Context) error {
|
|
return orz.Ok(c, orz.Map{
|
|
"version": version.GetAgentVersion(),
|
|
})
|
|
}
|
|
|
|
// DownloadAgent 下载 Agent 二进制文件
|
|
func (h *AgentHandler) DownloadAgent(c echo.Context) error {
|
|
filename := c.Param("filename")
|
|
|
|
// 校验 API Key
|
|
apiKey := c.QueryParam("key")
|
|
if _, err := h.apiKeyService.ValidateApiKey(c.Request().Context(), apiKey); err != nil {
|
|
// API Key 校验失败,尝试 IP 白名单兜底(兼容旧版 Agent 自动更新)
|
|
clientIP := c.RealIP()
|
|
isOnline, checkErr := h.agentService.IsAgentByIP(c.Request().Context(), clientIP)
|
|
if checkErr != nil {
|
|
h.logger.Error("check agent online by ip failed", zap.String("ip", clientIP), zap.Error(checkErr))
|
|
return orz.NewError(500, "系统内部错误")
|
|
}
|
|
|
|
if !isOnline {
|
|
h.logger.Warn("download agent failed: invalid api key and ip not whitelisted",
|
|
zap.String("key", apiKey),
|
|
zap.String("ip", clientIP))
|
|
return orz.NewError(401, "无效的 API 密钥,且来源 IP 未在白名单中")
|
|
}
|
|
h.logger.Info("download agent allowed by ip whitelist", zap.String("ip", clientIP), zap.String("filename", filename))
|
|
}
|
|
|
|
// 从嵌入的文件系统读取
|
|
agentFile, err := pika.AgentFS().Open(fmt.Sprintf("pika-%s", filename))
|
|
if err != nil {
|
|
h.logger.Error("agent binary not found", zap.String("filename", filename), zap.Error(err))
|
|
return orz.NewError(404, "未找到对应平台的 Agent 二进制文件")
|
|
}
|
|
|
|
// 设置响应头
|
|
c.Response().Header().Set("Content-Type", "application/octet-stream")
|
|
c.Response().Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
|
|
|
|
return c.Stream(http.StatusOK, "application/octet-stream", agentFile)
|
|
}
|
|
|
|
// getServerURL 获取服务器地址(支持反向代理)
|
|
func getServerURL(c echo.Context) string {
|
|
// 优先读取 X-Forwarded-Proto 和 X-Forwarded-Host
|
|
scheme := c.Request().Header.Get("X-Forwarded-Proto")
|
|
host := c.Request().Header.Get("X-Forwarded-Host")
|
|
|
|
// 如果没有反向代理头部,使用默认值
|
|
if scheme == "" {
|
|
scheme = c.Scheme()
|
|
}
|
|
if host == "" {
|
|
host = c.Request().Host
|
|
}
|
|
|
|
return scheme + "://" + host
|
|
}
|
|
|
|
func bashSingleQuote(value string) string {
|
|
if value == "" {
|
|
return "''"
|
|
}
|
|
return "'" + strings.ReplaceAll(value, "'", "'\"'\"'") + "'"
|
|
}
|
|
|
|
func (h *AgentHandler) GetServerUrl(c echo.Context) error {
|
|
serverUrl := getServerURL(c)
|
|
return orz.Ok(c, orz.Map{
|
|
"serverUrl": serverUrl,
|
|
})
|
|
}
|
|
|
|
// GetInstallScript 生成自动安装脚本
|
|
func (h *AgentHandler) GetInstallScript(c echo.Context) error {
|
|
token := c.QueryParam("token")
|
|
if token == "" {
|
|
return orz.NewError(400, "token不能为空")
|
|
}
|
|
|
|
// 使用统一的 getServerURL 函数获取服务器地址(支持反向代理)
|
|
serverUrl := getServerURL(c)
|
|
customName := strings.TrimSpace(c.QueryParam("name"))
|
|
customNameLiteral := bashSingleQuote(customName)
|
|
|
|
script := `#!/bin/bash
|
|
set -e
|
|
|
|
# 颜色输出
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
echo_info() {
|
|
echo -e "${GREEN}[INFO]${NC} $1"
|
|
}
|
|
|
|
echo_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
echo_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
# 解析参数
|
|
AGENT_NAME_CUSTOM=` + customNameLiteral + `
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--name)
|
|
AGENT_NAME_CUSTOM="$2"
|
|
shift # past argument
|
|
shift # past value
|
|
;;
|
|
*)
|
|
shift # past argument
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -n "$AGENT_NAME_CUSTOM" ]; then
|
|
echo_info "使用自定义探针名称: $AGENT_NAME_CUSTOM"
|
|
fi
|
|
|
|
# 检测操作系统和架构
|
|
detect_platform() {
|
|
OS=$(uname -s | awk '{print tolower($0)}')
|
|
ARCH=$(uname -m)
|
|
|
|
case "$ARCH" in
|
|
x86_64)
|
|
ARCH="amd64"
|
|
;;
|
|
aarch64|arm64|armv8*)
|
|
ARCH="arm64"
|
|
;;
|
|
armv7*|armv7l)
|
|
ARCH="armv7"
|
|
;;
|
|
loongarch64)
|
|
ARCH="loong64"
|
|
;;
|
|
*)
|
|
echo_error "不支持的架构: $ARCH"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
case "$OS" in
|
|
linux)
|
|
PLATFORM="linux-$ARCH"
|
|
AGENT_NAME="pika-agent"
|
|
;;
|
|
darwin)
|
|
PLATFORM="darwin-$ARCH"
|
|
AGENT_NAME="pika-agent"
|
|
;;
|
|
*)
|
|
echo_error "不支持的操作系统: $OS"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
echo_info "检测到平台: $PLATFORM"
|
|
}
|
|
|
|
# 下载探针
|
|
download_agent() {
|
|
local download_url="` + serverUrl + `/api/agent/downloads/agent-$PLATFORM?key=` + token + `"
|
|
local temp_file="/tmp/pika-agent-download"
|
|
|
|
echo_info "正在下载探针..."
|
|
|
|
if command -v curl &> /dev/null; then
|
|
curl -# -L "$download_url" -o "$temp_file"
|
|
elif command -v wget &> /dev/null; then
|
|
wget -q "$download_url" -O "$temp_file"
|
|
else
|
|
echo_error "未找到 wget 或 curl 命令,请先安装其中之一"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -f "$temp_file" ]; then
|
|
echo_error "下载失败"
|
|
exit 1
|
|
fi
|
|
|
|
# 移动到目标位置
|
|
mkdir -p /usr/local/bin
|
|
mv "$temp_file" "/usr/local/bin/$AGENT_NAME"
|
|
chmod +x "/usr/local/bin/$AGENT_NAME"
|
|
|
|
echo_info "探针下载完成: /usr/local/bin/$AGENT_NAME"
|
|
}
|
|
|
|
# 注册并启动服务
|
|
register_agent() {
|
|
local endpoint="` + serverUrl + `"
|
|
local token="` + token + `"
|
|
|
|
echo_info "正在注册探针..."
|
|
if [ -n "$AGENT_NAME_CUSTOM" ]; then
|
|
/usr/local/bin/$AGENT_NAME register --endpoint "$endpoint" --token "$token" --name "$AGENT_NAME_CUSTOM" --yes
|
|
else
|
|
/usr/local/bin/$AGENT_NAME register --endpoint "$endpoint" --token "$token" --yes
|
|
fi
|
|
}
|
|
|
|
# 主流程
|
|
main() {
|
|
echo_info "开始安装 Pika Agent..."
|
|
echo ""
|
|
|
|
detect_platform
|
|
download_agent
|
|
register_agent
|
|
|
|
echo ""
|
|
echo_info "=========================================="
|
|
echo_info "安装完成!"
|
|
echo_info "=========================================="
|
|
echo ""
|
|
echo_info "常用命令:"
|
|
echo " 查看状态: pika-agent status"
|
|
echo " 启动服务: pika-agent start"
|
|
echo " 停止服务: pika-agent stop"
|
|
echo " 重启服务: pika-agent restart"
|
|
echo " 卸载服务: pika-agent uninstall"
|
|
echo ""
|
|
}
|
|
|
|
main`
|
|
|
|
c.Response().Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
return c.String(http.StatusOK, script)
|
|
}
|