mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
channel: master version: 1.0.0 source-ref: master published-at-utc: 2026-03-13T15:19:28Z
128 lines
3.8 KiB
Go
128 lines
3.8 KiB
Go
package proxy
|
|
|
|
import (
|
|
"ant-chrome/backend/internal/config"
|
|
"ant-chrome/backend/internal/logger"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
)
|
|
|
|
// ClashManager Clash 进程管理器
|
|
type ClashManager struct {
|
|
Config *config.Config
|
|
AppRoot string // 应用根目录,所有相对路径基于此解析
|
|
Processes map[string]*exec.Cmd
|
|
}
|
|
|
|
// NewClashManager 创建 Clash 管理器
|
|
func NewClashManager(cfg *config.Config, appRoot string) *ClashManager {
|
|
return &ClashManager{
|
|
Config: cfg,
|
|
AppRoot: appRoot,
|
|
Processes: make(map[string]*exec.Cmd),
|
|
}
|
|
}
|
|
|
|
// ClashProfile Clash 配置接口
|
|
type ClashProfile interface {
|
|
GetProfileId() string
|
|
GetClashEnabled() bool
|
|
GetClashRunning() bool
|
|
GetClashConfigPath() string
|
|
GetClashProxyPort() int
|
|
SetClashRunning(bool)
|
|
SetClashPid(int)
|
|
SetClashProxyPort(int)
|
|
SetClashLastError(string)
|
|
}
|
|
|
|
// StartForProfile 为配置启动 Clash 进程
|
|
func (m *ClashManager) StartForProfile(profile ClashProfile, userDataDir string) error {
|
|
log := logger.New("Clash")
|
|
if !profile.GetClashEnabled() {
|
|
return nil
|
|
}
|
|
if profile.GetClashRunning() {
|
|
return nil
|
|
}
|
|
clashBinaryPath := strings.TrimSpace(m.Config.Browser.ClashBinaryPath)
|
|
if clashBinaryPath == "" {
|
|
err := fmt.Errorf("clash binary path not configured")
|
|
profile.SetClashLastError(err.Error())
|
|
log.Error("Clash 启动失败", logger.F("profile_id", profile.GetProfileId()), logger.F("error", err))
|
|
return err
|
|
}
|
|
if _, err := os.Stat(clashBinaryPath); err != nil {
|
|
profile.SetClashLastError(err.Error())
|
|
log.Error("Clash 启动失败", logger.F("profile_id", profile.GetProfileId()), logger.F("error", err))
|
|
return err
|
|
}
|
|
templatePath := strings.TrimSpace(profile.GetClashConfigPath())
|
|
if templatePath == "" {
|
|
err := fmt.Errorf("clash config path not configured")
|
|
profile.SetClashLastError(err.Error())
|
|
log.Error("Clash 启动失败", logger.F("profile_id", profile.GetProfileId()), logger.F("error", err))
|
|
return err
|
|
}
|
|
if _, err := os.Stat(templatePath); err != nil {
|
|
profile.SetClashLastError(err.Error())
|
|
log.Error("Clash 启动失败", logger.F("profile_id", profile.GetProfileId()), logger.F("error", err))
|
|
return err
|
|
}
|
|
port := profile.GetClashProxyPort()
|
|
if port == 0 {
|
|
p, err := nextAvailablePort()
|
|
if err != nil {
|
|
profile.SetClashLastError(err.Error())
|
|
log.Error("Clash 端口分配失败", logger.F("profile_id", profile.GetProfileId()), logger.F("error", err))
|
|
return err
|
|
}
|
|
port = p
|
|
profile.SetClashProxyPort(port)
|
|
}
|
|
args := []string{
|
|
"-f", templatePath,
|
|
"-d", userDataDir,
|
|
}
|
|
cmd := exec.Command(clashBinaryPath, args...)
|
|
hideWindow(cmd)
|
|
if err := cmd.Start(); err != nil {
|
|
profile.SetClashLastError(err.Error())
|
|
log.Error("Clash 启动失败", logger.F("profile_id", profile.GetProfileId()), logger.F("error", err))
|
|
return err
|
|
}
|
|
m.Processes[profile.GetProfileId()] = cmd
|
|
profile.SetClashRunning(true)
|
|
profile.SetClashPid(cmd.Process.Pid)
|
|
profile.SetClashLastError("")
|
|
log.Info("Clash 启动成功", logger.F("profile_id", profile.GetProfileId()), logger.F("pid", cmd.Process.Pid), logger.F("port", port))
|
|
return nil
|
|
}
|
|
|
|
// StopForProfile 停止配置的 Clash 进程
|
|
func (m *ClashManager) StopForProfile(profile ClashProfile) {
|
|
log := logger.New("Clash")
|
|
cmd := m.Processes[profile.GetProfileId()]
|
|
if cmd != nil && cmd.Process != nil {
|
|
if err := cmd.Process.Kill(); err != nil {
|
|
log.Error("Clash 停止失败", logger.F("profile_id", profile.GetProfileId()), logger.F("error", err))
|
|
}
|
|
}
|
|
delete(m.Processes, profile.GetProfileId())
|
|
profile.SetClashRunning(false)
|
|
profile.SetClashPid(0)
|
|
log.Info("Clash 已停止", logger.F("profile_id", profile.GetProfileId()))
|
|
}
|
|
|
|
// StopAll 停止所有 Clash 进程
|
|
func (m *ClashManager) StopAll() {
|
|
for profileID, cmd := range m.Processes {
|
|
if cmd != nil && cmd.Process != nil {
|
|
_ = cmd.Process.Kill()
|
|
}
|
|
delete(m.Processes, profileID)
|
|
}
|
|
}
|