feat: 工具配置增强、站点拨测与日志中心多项改进

- 工具配置:新增卸载 API/UI、Go 工具链、流水线构建自动安装依赖
- 修复 ListTools VerifyTool 递归栈溢出导致获取工具配置失败
- Node.js Alpine 安装改用 unofficial-builds musl 预编译包与 index.json 解析 LTS
- 站点拨测:详情页路由、告警自动恢复、并发拨测与默认可观测频率
- 日志中心:RBAC、查询构建器、ES/Loki 增强与审计 request/response 展示
- 发布配置:构建节点、流水线远程执行、Deploy RBAC 导航拆分
This commit is contained in:
Logan committed 2026-07-02 07:47:33 +08:00
1 parent cde7bbe540
commit 7e1c8f583c
84 files changed
+7729 -1507

No files matched your search

+107
View File
@@ -6,6 +6,7 @@ package remoteexec
import (
"bytes"
"fmt"
"io"
"log"
"net"
"os"
@@ -191,6 +192,112 @@ func UploadFile(cfg SSHConfig, localPath, remotePath string) (*UploadResult, err
return result, nil
}
// DownloadFile 从远程主机下载文件到本地(二进制安全,供流水线回拉产物) // Logan.Li
func DownloadFile(cfg SSHConfig, remotePath, localPath string) error {
client, err := connect(cfg)
if err != nil {
return err
}
defer client.Close()
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("创建SSH会话失败: %v", err)
}
defer session.Close()
stdout, err := session.StdoutPipe()
if err != nil {
return fmt.Errorf("获取stdout失败: %v", err)
}
remoteDir := filepath.Dir(remotePath)
mkdirCmd := fmt.Sprintf("mkdir -p %s", remoteDir)
_, _ = execWithClient(client, cfg, mkdirCmd, 30*time.Second)
if err := session.Start(fmt.Sprintf("cat %s", shellQuote(remotePath))); err != nil {
return fmt.Errorf("启动远程读取失败: %v", err)
}
if err := os.MkdirAll(filepath.Dir(localPath), 0755); err != nil {
return err
}
f, err := os.Create(localPath)
if err != nil {
return err
}
defer f.Close()
if _, err := io.Copy(f, stdout); err != nil {
return fmt.Errorf("写入本地文件失败: %v", err)
}
if err := session.Wait(); err != nil {
return fmt.Errorf("远程读取失败: %v", err)
}
return nil
}
// StreamRemoteTar 将远程目录打包为 tar.gz 流式写入本地文件 // Logan.Li
func StreamRemoteTar(cfg SSHConfig, remoteDir, localTarPath string, timeout time.Duration) error {
if timeout <= 0 {
timeout = 30 * time.Minute
}
client, err := connect(cfg)
if err != nil {
return err
}
defer client.Close()
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("创建SSH会话失败: %v", err)
}
defer session.Close()
stdout, err := session.StdoutPipe()
if err != nil {
return fmt.Errorf("获取stdout失败: %v", err)
}
cmd := fmt.Sprintf("tar czf - -C %s .", shellQuote(remoteDir))
if err := session.Start(cmd); err != nil {
return fmt.Errorf("启动远程打包失败: %v", err)
}
if err := os.MkdirAll(filepath.Dir(localTarPath), 0755); err != nil {
return err
}
f, err := os.Create(localTarPath)
if err != nil {
return err
}
defer f.Close()
done := make(chan error, 1)
go func() {
_, copyErr := io.Copy(f, stdout)
done <- copyErr
}()
select {
case copyErr := <-done:
if copyErr != nil {
return fmt.Errorf("写入 tar 失败: %v", copyErr)
}
case <-time.After(timeout):
return fmt.Errorf("下载工作区超时")
}
if err := session.Wait(); err != nil {
return fmt.Errorf("远程打包失败: %v", err)
}
return nil
}
// shellQuote 简单 shell 路径转义 // Logan.Li
func shellQuote(s string) string {
return "'" + strings.ReplaceAll(s, "'", "'\\''") + "'"
}
// BatchUpload 批量上传文件到多台服务器(并发)。
func BatchUpload(servers []SSHConfig, localPath, remotePath string, maxConcurrency int) []UploadResult {
if maxConcurrency <= 0 {