package main import ( "fmt" "strconv" "strings" ) // FrpcToml 对应 frpc.toml 的完整结构 type FrpcToml struct { ServerAddr string `json:"serverAddr"` ServerPort int `json:"serverPort"` Auth struct { Token string `json:"token"` } `json:"auth"` Log struct { To string `json:"to"` Level string `json:"level"` MaxDays int `json:"maxDays"` } `json:"log"` Transport struct { TcpMux bool `json:"tcpMux"` TcpMuxKeepalive int `json:"tcpMuxKeepalive"` HeartbeatInterval int `json:"heartbeatInterval"` HeartbeatTimeout int `json:"heartbeatTimeout"` PoolCount int `json:"poolCount"` } `json:"transport"` Proxies []TomlProxy `json:"proxies"` } // TomlProxy 对应 [[proxies]] 条目 type TomlProxy struct { Name string `json:"name"` Type string `json:"type"` LocalIP string `json:"localIP"` LocalPort int `json:"localPort"` RemotePort int `json:"remotePort"` Enabled bool `json:"enabled"` // 导入时默认 true } // ParseToml 解析 frpc.toml 内容 func ParseToml(content string) (*FrpcToml, error) { lines := strings.Split(content, "\n") result := &FrpcToml{ Proxies: []TomlProxy{}, Transport: struct { TcpMux bool `json:"tcpMux"` TcpMuxKeepalive int `json:"tcpMuxKeepalive"` HeartbeatInterval int `json:"heartbeatInterval"` HeartbeatTimeout int `json:"heartbeatTimeout"` PoolCount int `json:"poolCount"` }{ TcpMux: true, // 默认值 TcpMuxKeepalive: 30, HeartbeatInterval: 15, HeartbeatTimeout: 70, PoolCount: 8, }, } // 简单状态机解析 var currentProxy *TomlProxy inProxies := false for _, rawLine := range lines { line := strings.TrimSpace(rawLine) if line == "" || strings.HasPrefix(line, "#") { continue } // 检测 [[proxies]] 段开始 if strings.HasPrefix(line, "[[proxies]]") { inProxies = true currentProxy = &TomlProxy{ Type: "tcp", Enabled: true, } result.Proxies = append(result.Proxies, *currentProxy) // 注意:由于值传递,需要取最后一个元素的指针 currentProxy = &result.Proxies[len(result.Proxies)-1] continue } // 检测其他段头(忽略,我们的解析器只关心具体键值对) if strings.HasPrefix(line, "[") { inProxies = false currentProxy = nil continue } // 解析键值对 if strings.Contains(line, "=") { parts := strings.SplitN(line, "=", 2) key := strings.TrimSpace(parts[0]) value := strings.TrimSpace(parts[1]) // 去掉引号 value = strings.Trim(value, `"`) if inProxies && currentProxy != nil { // 解析隧道字段 switch key { case "name": currentProxy.Name = value case "type": currentProxy.Type = value case "localIP": currentProxy.LocalIP = value case "localPort": currentProxy.LocalPort, _ = strconv.Atoi(value) case "remotePort": currentProxy.RemotePort, _ = strconv.Atoi(value) } } else { // 解析全局字段 switch key { case "serverAddr": result.ServerAddr = value case "serverPort": result.ServerPort, _ = strconv.Atoi(value) case "token": result.Auth.Token = value case "level": result.Log.Level = value case "maxDays": result.Log.MaxDays, _ = strconv.Atoi(value) case "tcpMux": result.Transport.TcpMux = value == "true" case "tcpMuxKeepaliveInterval": result.Transport.TcpMuxKeepalive, _ = strconv.Atoi(value) case "heartbeatInterval": result.Transport.HeartbeatInterval, _ = strconv.Atoi(value) case "heartbeatTimeout": result.Transport.HeartbeatTimeout, _ = strconv.Atoi(value) case "poolCount": result.Transport.PoolCount, _ = strconv.Atoi(value) } } } } if result.ServerAddr == "" { return nil, fmt.Errorf("未找到 serverAddr 字段") } if len(result.Proxies) == 0 { return nil, fmt.Errorf("未找到任何隧道条目") } return result, nil } // ToGlobalConfig 将解析结果转换为 GlobalConfig func (f *FrpcToml) ToGlobalConfig() *GlobalConfig { return &GlobalConfig{ ServerAddr: f.ServerAddr, ServerPort: f.ServerPort, Token: f.Auth.Token, LogLevel: f.Log.Level, LogMaxDays: f.Log.MaxDays, TcpMux: f.Transport.TcpMux, TcpMuxKeepalive: f.Transport.TcpMuxKeepalive, HeartbeatInterval: f.Transport.HeartbeatInterval, HeartbeatTimeout: f.Transport.HeartbeatTimeout, PoolCount: f.Transport.PoolCount, } } // ToProxies 将解析结果转换为 Proxy 列表 func (f *FrpcToml) ToProxies() []Proxy { var proxies []Proxy for _, p := range f.Proxies { proxies = append(proxies, Proxy{ Name: p.Name, Type: p.Type, LocalIP: p.LocalIP, LocalPort: p.LocalPort, RemotePort: p.RemotePort, Enabled: true, // 导入默认启用 }) } return proxies }