96 lines
2.6 KiB
Go
96 lines
2.6 KiB
Go
package clash
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"text/template"
|
|
|
|
"github.com/Masterminds/sprig/v3"
|
|
"github.com/perfect-panel/server/pkg/adapter/proxy"
|
|
"github.com/perfect-panel/server/pkg/logger"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type Clash struct {
|
|
proxy.Adapter
|
|
}
|
|
|
|
func NewClash(adapter proxy.Adapter) *Clash {
|
|
return &Clash{
|
|
Adapter: adapter,
|
|
}
|
|
}
|
|
|
|
func (c *Clash) Build(uuid string) ([]byte, error) {
|
|
var proxies []Proxy
|
|
for _, proxied := range c.Adapter.Proxies {
|
|
p, err := c.parseProxy(proxied, uuid)
|
|
if err != nil {
|
|
logger.Errorw("Failed to parse proxy", logger.Field("error", err), logger.Field("proxy", p.Name))
|
|
continue
|
|
}
|
|
proxies = append(proxies, *p)
|
|
}
|
|
var groups []ProxyGroup
|
|
for _, group := range c.Adapter.Group {
|
|
groups = append(groups, ProxyGroup{
|
|
Name: group.Name,
|
|
Type: string(group.Type),
|
|
Proxies: group.Proxies,
|
|
Url: group.URL,
|
|
Interval: group.Interval,
|
|
})
|
|
}
|
|
var rules = append(c.Rules, fmt.Sprintf("MATCH,%s", c.Default))
|
|
|
|
tmplBytes, err := c.TemplateFS.ReadFile("template/clash.tpl")
|
|
if err != nil {
|
|
logger.Errorw("Failed to read template file", logger.Field("error", err))
|
|
return nil, fmt.Errorf("failed to read template file: %w", err)
|
|
}
|
|
tpl, err := template.New("clash.yaml").Funcs(sprig.FuncMap()).Funcs(template.FuncMap{
|
|
"toYaml": func(v interface{}) string {
|
|
out, err := yaml.Marshal(v)
|
|
if err != nil {
|
|
return fmt.Sprintf("# YAML encode error: %v", err.Error())
|
|
}
|
|
return string(out)
|
|
},
|
|
}).Parse(string(tmplBytes))
|
|
if err != nil {
|
|
logger.Errorw("[Clash] Failed to parse template", logger.Field("error", err))
|
|
return nil, fmt.Errorf("failed to parse template: %w", err)
|
|
}
|
|
var buf bytes.Buffer
|
|
err = tpl.Execute(&buf, map[string]interface{}{
|
|
"Proxies": proxies,
|
|
"ProxyGroups": groups,
|
|
"Rules": rules,
|
|
})
|
|
if err != nil {
|
|
logger.Errorw("[Clash] Failed to execute template", logger.Field("error", err))
|
|
return nil, fmt.Errorf("failed to execute template: %w", err)
|
|
}
|
|
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
func (c *Clash) parseProxy(p proxy.Proxy, uuid string) (*Proxy, error) {
|
|
parseFuncs := map[string]func(proxy.Proxy, string) (*Proxy, error){
|
|
"shadowsocks": parseShadowsocks,
|
|
"trojan": parseTrojan,
|
|
"vless": parseVless,
|
|
"vmess": parseVmess,
|
|
"hysteria2": parseHysteria2,
|
|
"tuic": parseTuic,
|
|
"anytls": parseAnyTLS,
|
|
}
|
|
|
|
if parseFunc, exists := parseFuncs[p.Protocol]; exists {
|
|
return parseFunc(p, uuid)
|
|
}
|
|
|
|
logger.Errorw("Unknown protocol", logger.Field("protocol", p.Protocol), logger.Field("server", p.Name))
|
|
return nil, fmt.Errorf("unknown protocol: %s", p.Protocol)
|
|
}
|