177 lines
4.4 KiB
Go
177 lines
4.4 KiB
Go
package render
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/peterqiu0516/sub-store/internal/model"
|
|
"github.com/peterqiu0516/sub-store/internal/util"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// RenderEgernYaml renders proxies as an Egern YAML document.
|
|
func RenderEgernYaml(proxies []model.ProxyNode) (string, error) {
|
|
var list []map[string]any
|
|
for _, p := range proxies {
|
|
if e := ToEgernProxy(p); e != nil {
|
|
list = append(list, e)
|
|
}
|
|
}
|
|
if len(list) == 0 {
|
|
return "", errNoNodes("egern")
|
|
}
|
|
data, err := yaml.Marshal(map[string]any{"proxies": list})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(data), nil
|
|
}
|
|
|
|
// ToEgernProxy converts a proxy node to an Egern proxy map.
|
|
// Returns nil for unsupported proxy types.
|
|
func ToEgernProxy(proxy model.ProxyNode) map[string]any {
|
|
common := util.StripUndefined(map[string]any{
|
|
"name": proxy["name"],
|
|
"server": proxy["server"],
|
|
"port": proxy["port"],
|
|
"tfo": proxy["tfo"],
|
|
"udp_relay": proxy["udp"],
|
|
})
|
|
|
|
ptype := getString(proxy, "type")
|
|
|
|
switch ptype {
|
|
case "ss":
|
|
return util.StripUndefined(mergeMap(common, map[string]any{
|
|
"type": "shadowsocks",
|
|
"method": proxy["cipher"],
|
|
"password": proxy["password"],
|
|
}))
|
|
|
|
case "vmess", "vless":
|
|
security := ""
|
|
if ptype == "vmess" {
|
|
security = strOr(proxy, "cipher", "auto")
|
|
}
|
|
return util.StripUndefined(mergeMap(common, map[string]any{
|
|
"type": ptype,
|
|
"uuid": proxy["uuid"],
|
|
"alter_id": proxy["alterId"],
|
|
"security": security,
|
|
"flow": proxy["flow"],
|
|
"tls": proxy["tls"],
|
|
"sni": proxy["servername"],
|
|
"network": proxy["network"],
|
|
"ws_opts": egernWsOptions(proxy),
|
|
"reality": egernRealityOptions(proxy),
|
|
}))
|
|
|
|
case "trojan", "anytls", "hysteria2":
|
|
return util.StripUndefined(mergeMap(common, map[string]any{
|
|
"type": ptype,
|
|
"password": proxy["password"],
|
|
"sni": sniOr(proxy, "sni", "servername"),
|
|
"skip_tls_verify": proxy["skip-cert-verify"],
|
|
"obfs": proxy["obfs"],
|
|
"obfs_password": proxy["obfs-password"],
|
|
"reality": egernRealityOptions(proxy),
|
|
}))
|
|
|
|
case "http", "socks5":
|
|
egernType := ptype
|
|
if ptype == "http" && getBool(proxy, "tls") {
|
|
egernType = "https"
|
|
} else if ptype == "socks5" && getBool(proxy, "tls") {
|
|
egernType = "socks5_tls"
|
|
}
|
|
return util.StripUndefined(mergeMap(common, map[string]any{
|
|
"type": egernType,
|
|
"username": proxy["username"],
|
|
"password": proxy["password"],
|
|
"skip_tls_verify": proxy["skip-cert-verify"],
|
|
}))
|
|
|
|
case "tuic":
|
|
return util.StripUndefined(mergeMap(common, map[string]any{
|
|
"type": "tuic",
|
|
"uuid": proxy["uuid"],
|
|
"password": proxy["password"],
|
|
"sni": proxy["sni"],
|
|
"skip_tls_verify": proxy["skip-cert-verify"],
|
|
}))
|
|
|
|
case "wireguard":
|
|
return util.StripUndefined(mergeMap(common, map[string]any{
|
|
"type": "wireguard",
|
|
"private_key": proxy["private-key"],
|
|
"public_key": proxy["public-key"],
|
|
"pre_shared_key": proxy["pre-shared-key"],
|
|
"address": proxy["ip"],
|
|
"ipv6_address": proxy["ipv6"],
|
|
}))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func egernWsOptions(proxy model.ProxyNode) any {
|
|
if getString(proxy, "network") != "ws" {
|
|
return nil
|
|
}
|
|
wsOpts, _ := proxy["ws-opts"].(map[string]any)
|
|
path := "/"
|
|
if wsOpts != nil {
|
|
if p, ok := wsOpts["path"].(string); ok && p != "" {
|
|
path = p
|
|
}
|
|
}
|
|
result := map[string]any{"path": path}
|
|
if wsOpts != nil {
|
|
if h, ok := wsOpts["headers"]; ok && h != nil {
|
|
result["headers"] = h
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func egernRealityOptions(proxy model.ProxyNode) any {
|
|
realityOpts, _ := proxy["reality-opts"].(map[string]any)
|
|
if realityOpts == nil {
|
|
return nil
|
|
}
|
|
pubKey := realityOpts["public-key"]
|
|
if pubKey == nil {
|
|
return nil
|
|
}
|
|
return util.StripUndefined(map[string]any{
|
|
"public_key": pubKey,
|
|
"short_id": realityOpts["short-id"],
|
|
})
|
|
}
|
|
|
|
func mergeMap(base, next map[string]any) map[string]any {
|
|
result := make(map[string]any, len(base)+len(next))
|
|
for k, v := range base {
|
|
result[k] = v
|
|
}
|
|
for k, v := range next {
|
|
result[k] = v
|
|
}
|
|
return result
|
|
}
|
|
|
|
func errNoNodes(target string) error {
|
|
return &noNodesError{target: target}
|
|
}
|
|
|
|
type noNodesError struct {
|
|
target string
|
|
}
|
|
|
|
func (e *noNodesError) Error() string {
|
|
var sb strings.Builder
|
|
sb.WriteString("No supported nodes for ")
|
|
sb.WriteString(e.target)
|
|
sb.WriteString(" output")
|
|
return sb.String()
|
|
}
|