120 lines
3.5 KiB
Go
120 lines
3.5 KiB
Go
package surfboard
|
||
|
||
import (
|
||
"bytes"
|
||
"embed"
|
||
"fmt"
|
||
"net/url"
|
||
"strings"
|
||
"text/template"
|
||
"time"
|
||
|
||
"github.com/perfect-panel/server/pkg/adapter/proxy"
|
||
|
||
"github.com/perfect-panel/server/pkg/logger"
|
||
"github.com/perfect-panel/server/pkg/tool"
|
||
"github.com/perfect-panel/server/pkg/traffic"
|
||
)
|
||
|
||
//go:embed *.tpl
|
||
var configFiles embed.FS
|
||
var shadowsocksSupportMethod = []string{"aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305"}
|
||
|
||
func BuildSurfboard(servers proxy.Adapter, siteName string, user UserInfo) []byte {
|
||
var proxies, proxyGroup string
|
||
var removed []string
|
||
for _, node := range servers.Proxies {
|
||
if uri := buildProxy(node, user.UUID); uri != "" {
|
||
proxies += uri
|
||
} else {
|
||
removed = append(removed, node.Name)
|
||
}
|
||
}
|
||
|
||
for _, group := range servers.Group {
|
||
|
||
if len(removed) > 0 {
|
||
group.Proxies = tool.RemoveStringElement(group.Proxies, removed...)
|
||
}
|
||
|
||
if group.Type == proxy.GroupTypeSelect {
|
||
proxyGroup += fmt.Sprintf("%s = select, %s", group.Name, strings.Join(group.Proxies, ", ")) + "\r\n"
|
||
} else if group.Type == proxy.GroupTypeURLTest {
|
||
proxyGroup += fmt.Sprintf("%s = url-test, %s, url=%s, interval=%d", group.Name, strings.Join(group.Proxies, ", "), group.URL, group.Interval) + "\r\n"
|
||
} else if group.Type == proxy.GroupTypeFallback {
|
||
proxyGroup += fmt.Sprintf("%s = fallback, %s, url=%s, interval=%d", group.Name, strings.Join(group.Proxies, ", "), group.URL, group.Interval) + "\r\n"
|
||
} else {
|
||
logger.Errorf("[BuildSurfboard] unknown group type: %s", group.Type)
|
||
}
|
||
}
|
||
|
||
var rules string
|
||
for _, rule := range servers.Rules {
|
||
if rule == "" {
|
||
continue
|
||
}
|
||
rules += rule + "\r\n"
|
||
}
|
||
|
||
//final rule
|
||
rules += "FINAL, 手动选择"
|
||
|
||
file, err := configFiles.ReadFile("default.tpl")
|
||
if err != nil {
|
||
logger.Errorf("read default surfboard config error: %v", err.Error())
|
||
return nil
|
||
}
|
||
// replace template
|
||
tpl, err := template.New("default").Parse(string(file))
|
||
if err != nil {
|
||
logger.Errorf("read default surfboard config error: %v", err.Error())
|
||
return nil
|
||
}
|
||
var buf bytes.Buffer
|
||
|
||
var expiredAt string
|
||
if user.ExpiredDate.Before(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) {
|
||
expiredAt = "长期有效"
|
||
} else {
|
||
expiredAt = user.ExpiredDate.Format("2006-01-02 15:04:05")
|
||
}
|
||
// convert traffic
|
||
upload := traffic.AutoConvert(user.Upload, false)
|
||
download := traffic.AutoConvert(user.Download, false)
|
||
total := traffic.AutoConvert(user.TotalTraffic, false)
|
||
unusedTraffic := traffic.AutoConvert(user.TotalTraffic-user.Upload-user.Download, false)
|
||
// query Host
|
||
urlParse, err := url.Parse(user.SubscribeURL)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
if err := tpl.Execute(&buf, map[string]interface{}{
|
||
"Proxies": proxies,
|
||
"ProxyGroup": proxyGroup,
|
||
"SubscribeURL": user.SubscribeURL,
|
||
"SubscribeInfo": fmt.Sprintf("title=%s订阅信息, content=上传流量:%s\\n下载流量:%s\\n剩余流量: %s\\n套餐流量:%s\\n到期时间:%s", siteName, upload, download, unusedTraffic, total, expiredAt),
|
||
"SubscribeDomain": urlParse.Host,
|
||
"Rules": rules,
|
||
}); err != nil {
|
||
logger.Errorf("build surfboard config error: %v", err.Error())
|
||
return nil
|
||
}
|
||
return buf.Bytes()
|
||
}
|
||
|
||
func buildProxy(data proxy.Proxy, uuid string) string {
|
||
var p string
|
||
switch data.Protocol {
|
||
case "vmess":
|
||
p = buildVMess(data, uuid)
|
||
case "shadowsocks":
|
||
if !tool.Contains(shadowsocksSupportMethod, data.Option.(proxy.Shadowsocks).Method) {
|
||
return ""
|
||
}
|
||
p = buildShadowsocks(data, uuid)
|
||
case "trojan":
|
||
p = buildTrojan(data, uuid)
|
||
}
|
||
return p
|
||
}
|