package main import ( "bytes" "fmt" "os" "os/exec" "strings" "text/template" ) // frpc.toml 模板 - 完全复制你的配置文件结构 const frpcTemplate = `serverAddr = "{{.ServerAddr}}" serverPort = {{.ServerPort}} [auth] token = "{{.Token}}" [log] to = "./frpc.log" level = "{{.LogLevel}}" maxDays = {{.LogMaxDays}} [transport] tcpMux = {{.TcpMux}} tcpMuxKeepaliveInterval = {{.TcpMuxKeepalive}} heartbeatInterval = {{.HeartbeatInterval}} heartbeatTimeout = {{.HeartbeatTimeout}} poolCount = {{.PoolCount}} {{range .Proxies}} [[proxies]] name = "{{.Name}}" type = "{{.Type}}" localIP = "{{.LocalIP}}" localPort = {{.LocalPort}} remotePort = {{.RemotePort}} {{end}}` // 生成 frpc.toml 文件 func GenerateFrpcConfig() error { // 1. 读取全局配置 cfg, err := GetGlobalConfig() if err != nil { return fmt.Errorf("读取全局配置失败: %w", err) } // 2. 读取所有启用的隧道 proxies, err := GetProxies() if err != nil { return fmt.Errorf("读取隧道列表失败: %w", err) } var activeProxies []Proxy for _, p := range proxies { if p.Enabled { activeProxies = append(activeProxies, p) } } // 3. 组合数据 data := struct { *GlobalConfig Proxies []Proxy }{ GlobalConfig: cfg, Proxies: activeProxies, } // 4. 渲染模板 tmpl, err := template.New("frpc").Parse(frpcTemplate) if err != nil { return fmt.Errorf("解析模板失败: %w", err) } var buf bytes.Buffer err = tmpl.Execute(&buf, data) if err != nil { return fmt.Errorf("渲染模板失败: %w", err) } // 5. 写入文件 err = os.WriteFile("./frpc.toml", buf.Bytes(), 0644) if err != nil { return fmt.Errorf("写入配置文件失败: %w", err) } return nil } // 热加载 frpc func ReloadFrpc() error { // 先检查 frpc 进程是否在运行 cmd := exec.Command("pgrep", "-f", "frpc") output, err := cmd.Output() if err != nil || len(output) == 0 { // frpc 没在运行,尝试启动 return StartFrpc() } // 执行 reload reloadCmd := exec.Command("frpc", "reload", "-c", "./frpc.toml") output, err = reloadCmd.CombinedOutput() if err != nil { return fmt.Errorf("reload 失败: %s, %w", string(output), err) } return nil } // 启动 frpc func StartFrpc() error { // 先确保配置文件存在 if _, err := os.Stat("./frpc.toml"); os.IsNotExist(err) { if err := GenerateFrpcConfig(); err != nil { return fmt.Errorf("生成配置文件失败: %w", err) } } cmd := exec.Command("frpc", "-c", "./frpc.toml") // 后台运行,不阻塞 err := cmd.Start() if err != nil { return fmt.Errorf("启动 frpc 失败: %w", err) } return nil } // StopFrpc 停止 frpc func StopFrpc() error { cmd := exec.Command("pkill", "-f", "frpc") err := cmd.Run() if err != nil && !strings.Contains(err.Error(), "no process") { return fmt.Errorf("停止 frpc 失败: %w", err) } return nil } // 检查 frpc 状态 func GetFrpcStatus() (bool, error) { cmd := exec.Command("pgrep", "-f", "frpc") output, err := cmd.Output() if err != nil { return false, nil } return len(output) > 0, nil }