diff --git a/Dockerfile b/Dockerfile index d462b09..902b2f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,11 +18,11 @@ RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /sub-store . # ─── Stage 3: Runtime ─── FROM alpine:3.21 RUN apk add --no-cache ca-certificates tzdata && \ - wget -qO /tmp/sing-box.tar.gz "https://github.com/SagerNet/sing-box/releases/download/v1.13.14/sing-box-1.13.14-linux-amd64-musl.tar.gz" && \ - tar xzf /tmp/sing-box.tar.gz -C /tmp && \ - mv /tmp/sing-box-*/sing-box /usr/local/bin/sing-box && \ - chmod +x /usr/local/bin/sing-box && \ - rm -rf /tmp/sing-box* + wget -qO /tmp/mihomo.gz "https://github.com/MetaCubeX/mihomo/releases/download/v1.19.29/mihomo-linux-amd64-v1.19.29.gz" && \ + gunzip /tmp/mihomo.gz && \ + mv /tmp/mihomo /usr/local/bin/mihomo && \ + chmod +x /usr/local/bin/mihomo && \ + rm -rf /tmp/mihomo* WORKDIR /app COPY --from=backend-builder /sub-store /app/sub-store COPY --from=frontend-builder /frontend/dist /app/frontend/dist diff --git a/internal/handler/egress_info.go b/internal/handler/egress_info.go index 42aa46c..e3c5b29 100644 --- a/internal/handler/egress_info.go +++ b/internal/handler/egress_info.go @@ -17,9 +17,9 @@ import ( "time" "github.com/gofiber/fiber/v3" + "gopkg.in/yaml.v3" "github.com/peterqiu0516/sub-store/internal/model" - "github.com/peterqiu0516/sub-store/internal/render" "github.com/peterqiu0516/sub-store/internal/util" ) @@ -134,37 +134,35 @@ func buildEgressProbeConfig(node model.ProxyNode, port int) ([]byte, error) { } probeNode["name"] = "PROXY" - outbound := render.ToSingBoxOutbound(probeNode) - if outbound == nil { - return nil, fmt.Errorf("Unsupported proxy node for egress probe") + // Build a minimal mihomo (Clash Meta) config with the probe node as the + // only proxy. mihomo natively supports xhttp and other Clash transports. + proxyYaml, err := yaml.Marshal(probeNode) + if err != nil { + return nil, fmt.Errorf("Failed to marshal proxy node: %w", err) } - doc := map[string]any{ - "log": map[string]any{"level": "warn"}, - "inbounds": []any{ - map[string]any{ - "type": "mixed", - "tag": "mixed-in", - "listen": "127.0.0.1", - "listen_port": port, - }, - }, - "outbounds": []any{ - outbound, - map[string]any{"type": "direct", "tag": "DIRECT"}, - }, - "route": map[string]any{ - "auto_detect_interface": true, - "final": "PROXY", - "rules": []any{map[string]any{"action": "sniff"}}, - }, - } - return json.Marshal(doc) + + config := fmt.Sprintf(`mixed-port: %d +allow-lan: false +mode: direct +log-level: warning +proxies: +- %s +proxy-groups: +- name: PROXY + type: select + proxies: + - PROXY +rules: +- MATCH,PROXY +`, port, strings.ReplaceAll(strings.TrimSuffix(string(proxyYaml), "\n"), "\n", "\n ")) + + return []byte(config), nil } func runEgressProbe(configData []byte, port int) (fiber.Map, error) { - singBox, err := exec.LookPath("sing-box") + mihomo, err := exec.LookPath("mihomo") if err != nil { - return nil, fmt.Errorf("sing-box executable not found") + return nil, fmt.Errorf("mihomo executable not found") } dir, err := os.MkdirTemp("", "sub-store-egress-*") if err != nil { @@ -172,14 +170,14 @@ func runEgressProbe(configData []byte, port int) (fiber.Map, error) { } defer os.RemoveAll(dir) - configPath := filepath.Join(dir, "config.json") + configPath := filepath.Join(dir, "config.yaml") if err := os.WriteFile(configPath, configData, 0o600); err != nil { return nil, err } ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second) defer cancel() - cmd := exec.CommandContext(ctx, singBox, "run", "-c", configPath) + cmd := exec.CommandContext(ctx, mihomo, "-f", configPath) var stderr bytes.Buffer cmd.Stderr = &stderr if err := cmd.Start(); err != nil {