package render import ( "github.com/peterqiu0516/sub-store/internal/model" ) // RenderSurgeProxies renders proxies as Surge text lines. func RenderSurgeProxies(proxies []model.ProxyNode) (string, error) { return RenderTextProxyList(proxies, model.TargetSurge, func(p model.ProxyNode) string { return ToSurgeProxyLine(p) }) } // RenderSurgeMacProxies renders proxies as Surge-Mac text lines (adds ssh/h2-connect/snell). func RenderSurgeMacProxies(proxies []model.ProxyNode) (string, error) { return RenderTextProxyList(proxies, model.TargetSurgeMac, func(p model.ProxyNode) string { return ToSurgeMacProxyLine(p) }) } // ToSurgeProxyLine converts a single proxy node to a Surge proxy line. // Returns "" for unsupported proxy types. func ToSurgeProxyLine(proxy model.ProxyNode) string { name := SanitizeTextProxyName(getString(proxy, "name")) ptype := getString(proxy, "type") server := getString(proxy, "server") port := getInt(proxy, "port") base := name + "=" + surgeType(proxy) + "," + server + "," + itoa(port) entries := CommonTextOptions(proxy) switch ptype { case "ss": entries = unshift(entries, [2]any{"encrypt-method", strOr(proxy, "cipher", "none")}, [2]any{"password", proxy["password"]}, ) entries = AppendPluginOptions(entries, proxy, "surge") return JoinTextProxy(base, entries) case "vmess": entries = unshift(entries, [2]any{"username", proxy["uuid"]}, [2]any{"encrypt-method", strOr(proxy, "cipher", "auto")}, [2]any{"tls", proxy["tls"]}, [2]any{"sni", proxy["servername"]}, ) entries = AppendWsOptions(entries, proxy, "surge") return JoinTextProxy(base, entries) case "trojan": entries = unshift(entries, [2]any{"password", proxy["password"]}, [2]any{"sni", sniOr(proxy, "sni", "servername")}, ) return JoinTextProxy(base, entries) case "http", "socks5": entries = unshift(entries, [2]any{"username", proxy["username"]}, [2]any{"password", proxy["password"]}, [2]any{"tls", proxy["tls"]}, [2]any{"sni", sniOr(proxy, "sni", "servername")}, ) return JoinTextProxy(base, entries) case "hysteria2": entries = unshift(entries, [2]any{"password", proxy["password"]}, [2]any{"sni", proxy["sni"]}, ) if obfs := proxy["obfs"]; obfs != nil { entries = append(entries, [2]any{"obfs", obfs}, [2]any{"obfs-password", proxy["obfs-password"]}) } return JoinTextProxy(base, entries) case "tuic": entries = unshift(entries, [2]any{"uuid", proxy["uuid"]}, [2]any{"password", proxy["password"]}, [2]any{"sni", proxy["sni"]}, ) return JoinTextProxy(name+"=tuic-v5,"+server+","+itoa(port), entries) case "anytls": entries = unshift(entries, [2]any{"password", proxy["password"]}, [2]any{"sni", sniOr(proxy, "sni", "servername")}, ) return JoinTextProxy(base, entries) case "snell": entries = unshift(entries, [2]any{"psk", pskOrPassword(proxy)}, [2]any{"version", versionOr3(proxy)}, ) return JoinTextProxy(name+"=snell,"+server+","+itoa(port), entries) } return "" } // ToSurgeMacProxyLine adds surge-mac specific types (ssh, h2-connect) on top // of the base surge line. func ToSurgeMacProxyLine(proxy model.ProxyNode) string { name := SanitizeTextProxyName(getString(proxy, "name")) ptype := getString(proxy, "type") server := getString(proxy, "server") port := getInt(proxy, "port") switch ptype { case "ssh": entries := CommonTextOptions(proxy) entries = unshift(entries, [2]any{"username", proxy["username"]}, [2]any{"password", proxy["password"]}, [2]any{"private-key", proxy["private-key"]}, ) return JoinTextProxy(name+"=ssh,"+server+","+itoa(port), entries) case "h2-connect": entries := CommonTextOptions(proxy) entries = unshift(entries, [2]any{"username", proxy["username"]}, [2]any{"password", proxy["password"]}, [2]any{"tls", proxy["tls"]}, [2]any{"sni", sniOr(proxy, "sni", "servername")}, ) return JoinTextProxy(name+"=h2-connect,"+server+","+itoa(port), entries) } return ToSurgeProxyLine(proxy) } func surgeType(proxy model.ProxyNode) string { ptype := getString(proxy, "type") if ptype == "socks5" { if getBool(proxy, "tls") { return "socks5-tls" } return "socks5" } if ptype == "http" { if getBool(proxy, "tls") { return "https" } return "http" } return ptype } // --- small helpers --- func unshift(entries [][2]any, front ...[2]any) [][2]any { return append(front, entries...) } func strOr(proxy model.ProxyNode, key, def string) string { s := getString(proxy, key) if s == "" { return def } return s } func sniOr(proxy model.ProxyNode, keys ...string) any { for _, k := range keys { if v := proxy[k]; v != nil { return v } } return nil } func pskOrPassword(proxy model.ProxyNode) any { if v := proxy["psk"]; v != nil { return v } return proxy["password"] } func versionOr3(proxy model.ProxyNode) any { if v := proxy["version"]; v != nil { return v } return 3 } func itoa(n int) string { if n == 0 { return "0" } neg := false if n < 0 { neg = true n = -n } var buf [20]byte i := len(buf) for n > 0 { i-- buf[i] = byte('0' + n%10) n /= 10 } if neg { i-- buf[i] = '-' } return string(buf[i:]) }