From eedc090a4f3d6da600ea6fa1e8a5b1d490b3f913 Mon Sep 17 00:00:00 2001 From: isboyjc Date: Sat, 4 Apr 2026 22:59:35 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20update=20CHANGELOG.md=20a?= =?UTF-8?q?nd=20Dockerfile=20for=20sing-box=20enhancements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated CHANGELOG.md to include version 0.4.1 with fixes related to sing-box, including an upgrade to version 1.13.5 and improvements in configuration validation and error logging. - Modified Dockerfile to reflect the new sing-box version. - Enhanced error handling in the sing-box startup process, including configuration checks and stderr logging for better diagnostics. --- CHANGELOG.md | 12 ++++++++++++ Dockerfile | 2 +- custom/singbox.go | 43 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adeb623..f9bff36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/), 版本号遵循 [语义化版本 2.0.0](https://semver.org/lang/zh-CN/)。 +## [v0.4.1] - 2026-04-04 + +### 修复 + +- 升级 sing-box 从 1.11.8 到 **1.13.5**,修复 anytls 等新协议不支持导致订阅节点启动失败的问题 +- sing-box 启动前新增 `sing-box check` 配置预检,配置无效时输出详细错误而非静默崩溃 +- 捕获 sing-box stderr 输出到 `[sing-box]` 日志,便于排查运行时错误 +- 检测 sing-box 进程启动后立即退出的情况,避免误报"端口未就绪" +- Docker healthcheck 从 `wget` 改为 `curl`(debian-slim 无 wget),Dockerfile 增加 curl 安装 +- 修复 `docker-compose.dokploy.yml` 服务未加入 `dokploy-network` 的问题 +- 修复中英文切换时订阅池统计模块动态文字未更新的问题 + ## [v0.4.0] - 2026-04-04 ### 新增 diff --git a/Dockerfile b/Dockerfile index 9c33d20..4db1ee0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ COPY . . RUN CGO_ENABLED=1 GOOS=linux go build -o proxy-pool . # 下载 sing-box 二进制 -ARG SINGBOX_VERSION=1.11.8 +ARG SINGBOX_VERSION=1.13.5 RUN ARCH=$(case "$(dpkg --print-architecture)" in amd64) echo "amd64";; arm64) echo "arm64";; *) echo "amd64";; esac) && \ curl -fsSL "https://github.com/SagerNet/sing-box/releases/download/v${SINGBOX_VERSION}/sing-box-${SINGBOX_VERSION}-linux-${ARCH}.tar.gz" \ -o /tmp/sing-box.tar.gz && \ diff --git a/custom/singbox.go b/custom/singbox.go index c79490b..58536c0 100644 --- a/custom/singbox.go +++ b/custom/singbox.go @@ -361,20 +361,45 @@ func convertPluginOpts(plugin string, opts map[string]interface{}) string { // startLocked 启动 sing-box(需持有锁) func (s *SingBoxProcess) startLocked() error { - if _, err := exec.LookPath(s.binPath); err != nil { + binPath, err := exec.LookPath(s.binPath) + if err != nil { return fmt.Errorf("sing-box 未找到: %s(请安装 sing-box 或设置 SINGBOX_PATH)", s.binPath) } - s.cmd = exec.Command(s.binPath, "run", "-c", s.configFile, "-D", s.configDir) + // 先检查配置是否有效 + checkCmd := exec.Command(binPath, "check", "-c", s.configFile, "-D", s.configDir) + if checkOutput, err := checkCmd.CombinedOutput(); err != nil { + log.Printf("[custom] ❌ sing-box 配置检查失败:\n%s", string(checkOutput)) + return fmt.Errorf("sing-box 配置无效: %s", string(checkOutput)) + } + + s.cmd = exec.Command(binPath, "run", "-c", s.configFile, "-D", s.configDir) + + // 捕获 stderr 用于错误诊断 + stderrPipe, _ := s.cmd.StderrPipe() s.cmd.Stdout = os.Stdout - s.cmd.Stderr = os.Stderr if err := s.cmd.Start(); err != nil { - return err + return fmt.Errorf("sing-box 启动失败: %w", err) } s.running = true + // 异步读取 stderr 并输出到日志 + go func() { + buf := make([]byte, 4096) + for { + n, err := stderrPipe.Read(buf) + if n > 0 { + log.Printf("[sing-box] %s", strings.TrimSpace(string(buf[:n]))) + } + if err != nil { + break + } + } + }() + // 监控进程退出 + exitCh := make(chan struct{}) go func() { if s.cmd != nil && s.cmd.Process != nil { s.cmd.Wait() @@ -382,12 +407,20 @@ func (s *SingBoxProcess) startLocked() error { s.running = false s.mu.Unlock() } + close(exitCh) }() // 等待端口就绪(最多 10 秒) - log.Printf("[custom] sing-box 启动中,等待端口就绪...") + log.Printf("[custom] sing-box 启动中,等待端口就绪(配置: %s)...", s.configFile) ready := false for i := 0; i < 20; i++ { + // 检查进程是否已退出 + select { + case <-exitCh: + return fmt.Errorf("sing-box 进程启动后立即退出,请检查日志") + default: + } + time.Sleep(500 * time.Millisecond) // 检查第一个端口是否可连 for _, port := range s.portMap {