init: 1.0.0
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
package surfboard
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/adapter/proxy"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/logger"
|
||||
"github.com/perfect-panel/ppanel-server/pkg/tool"
|
||||
"github.com/perfect-panel/ppanel-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
|
||||
for _, node := range servers.Proxies {
|
||||
if uri := buildProxy(node, user.UUID); uri != "" {
|
||||
proxies += uri
|
||||
}
|
||||
}
|
||||
|
||||
for _, group := range servers.Group {
|
||||
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 += "# 最终规则" + "\r\n" + "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
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package surfboard
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/adapter/proxy"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/uuidx"
|
||||
)
|
||||
|
||||
func TestBuildSurfboard(t *testing.T) {
|
||||
siteName := "test"
|
||||
user := UserInfo{
|
||||
UUID: uuidx.NewUUID().String(),
|
||||
Upload: 0,
|
||||
Download: 0,
|
||||
TotalTraffic: 0,
|
||||
ExpiredDate: time.Now().AddDate(0, 1, 1),
|
||||
SubscribeURL: "https://test.com",
|
||||
}
|
||||
conf := BuildSurfboard(proxy.Adapter{}, siteName, user)
|
||||
t.Log(string(conf))
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#!MANAGED-CONFIG {{ .SubscribeURL }} interval=43200 strict=true
|
||||
|
||||
[General]
|
||||
loglevel = notify
|
||||
ipv6 = false
|
||||
skip-proxy = localhost, *.local, injections.adguard.org, local.adguard.org, 0.0.0.0/8, 10.0.0.0/8, 17.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.168.0.0/16, 192.88.99.0/24, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/4, 240.0.0.0/4, 255.255.255.255/32
|
||||
tls-provider = default
|
||||
show-error-page-for-reject = true
|
||||
dns-server = 223.6.6.6, 119.29.29.29, 119.28.28.28
|
||||
test-timeout = 5
|
||||
internet-test-url = http://bing.com
|
||||
proxy-test-url = http://bing.com
|
||||
|
||||
[Panel]
|
||||
SubscribeInfo = {{ .SubscribeInfo }}, style=info
|
||||
|
||||
# Surfboard 配置文档:https://manual.getsurfboard.com/
|
||||
|
||||
[Proxy]
|
||||
# 代理列表
|
||||
{{ .Proxies }}
|
||||
|
||||
[Proxy Group]
|
||||
# 代理组列表
|
||||
{{ .ProxyGroup }}
|
||||
|
||||
[Rule]
|
||||
# 规则列表
|
||||
{{ .Rules }}
|
||||
@@ -0,0 +1,12 @@
|
||||
package surfboard
|
||||
|
||||
import "time"
|
||||
|
||||
type UserInfo struct {
|
||||
UUID string
|
||||
Upload int64
|
||||
Download int64
|
||||
TotalTraffic int64
|
||||
ExpiredDate time.Time
|
||||
SubscribeURL string
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package surfboard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/adapter/proxy"
|
||||
)
|
||||
|
||||
func buildShadowsocks(data proxy.Proxy, uuid string) string {
|
||||
ss, ok := data.Option.(proxy.Shadowsocks)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
addr := fmt.Sprintf("%s=ss, %s, %d", data.Name, data.Server, data.Port)
|
||||
config := []string{
|
||||
addr,
|
||||
fmt.Sprintf("encrypt-method=%s", ss.Method),
|
||||
fmt.Sprintf("password=%s", uuid),
|
||||
"tfo=true",
|
||||
"udp-relay=true",
|
||||
}
|
||||
return strings.Join(config, ",") + "\r\n"
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package surfboard
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/adapter/proxy"
|
||||
)
|
||||
|
||||
func createSS() proxy.Proxy {
|
||||
return proxy.Proxy{
|
||||
Name: "Shadowsocks",
|
||||
Server: "test.xxxx.com",
|
||||
Port: 10301,
|
||||
Protocol: "shadowsocks",
|
||||
Option: proxy.Shadowsocks{
|
||||
Port: 10301,
|
||||
Method: "aes-256-gcm",
|
||||
ServerKey: "123456",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestShadowsocks(t *testing.T) {
|
||||
node := createSS()
|
||||
uuid := "123456"
|
||||
shadowsocks := buildShadowsocks(node, uuid)
|
||||
t.Log(shadowsocks)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package surfboard
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/adapter/proxy"
|
||||
)
|
||||
|
||||
func buildTrojan(data proxy.Proxy, uuid string) string {
|
||||
// $config = [
|
||||
// "{$server['name']}=trojan",
|
||||
// "{$server['host']}",
|
||||
// "{$server['port']}",
|
||||
// "password={$password}",
|
||||
// $protocol_settings['server_name'] ? "sni={$protocol_settings['server_name']}" : "",
|
||||
// 'tfo=true',
|
||||
// 'udp-relay=true'
|
||||
//];
|
||||
trojan, ok := data.Option.(proxy.Trojan)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
config := []string{
|
||||
data.Name + "=trojan",
|
||||
data.Server,
|
||||
strconv.Itoa(data.Port),
|
||||
"password=" + uuid,
|
||||
"tfo=true",
|
||||
"udp-relay=true",
|
||||
}
|
||||
if trojan.SecurityConfig.SNI != "" {
|
||||
config = append(config, "sni="+trojan.SecurityConfig.SNI)
|
||||
}
|
||||
if trojan.SecurityConfig.AllowInsecure {
|
||||
config = append(config, "skip-cert-verify=true")
|
||||
} else {
|
||||
config = append(config, "skip-cert-verify=false")
|
||||
}
|
||||
return strings.Join(config, ",") + "\r\n"
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package surfboard
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/adapter/proxy"
|
||||
)
|
||||
|
||||
func createTrojan() proxy.Proxy {
|
||||
|
||||
return proxy.Proxy{
|
||||
Name: "Trojan",
|
||||
Server: "test.xxxx.com",
|
||||
Port: 13002,
|
||||
Protocol: "trojan",
|
||||
Option: proxy.Trojan{
|
||||
Port: 13002,
|
||||
Transport: "websocket",
|
||||
TransportConfig: proxy.TransportConfig{
|
||||
Path: "/ws",
|
||||
Host: "baidu.com",
|
||||
},
|
||||
SecurityConfig: proxy.SecurityConfig{
|
||||
SNI: "baidu.com",
|
||||
AllowInsecure: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrojan(t *testing.T) {
|
||||
node := createTrojan()
|
||||
uuid := "123456"
|
||||
trojan := buildTrojan(node, uuid)
|
||||
t.Log(trojan)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package surfboard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/adapter/proxy"
|
||||
)
|
||||
|
||||
func buildVMess(data proxy.Proxy, uuid string) string {
|
||||
vmess, ok := data.Option.(proxy.Vmess)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
addr := fmt.Sprintf("%s=vmess, %s, %d", data.Name, data.Server, data.Port)
|
||||
uriConfig := []string{
|
||||
addr,
|
||||
fmt.Sprintf("username=%s", uuid),
|
||||
"vmess-aead=true",
|
||||
"tfo=true",
|
||||
"udp-relay=true",
|
||||
}
|
||||
if vmess.Security == "tls" {
|
||||
uriConfig = append(uriConfig, "tls=true")
|
||||
if vmess.SecurityConfig.AllowInsecure {
|
||||
uriConfig = append(uriConfig, "skip-cert-verify=true")
|
||||
} else {
|
||||
uriConfig = append(uriConfig, "skip-cert-verify=false")
|
||||
}
|
||||
if vmess.SecurityConfig.SNI != "" {
|
||||
uriConfig = append(uriConfig, fmt.Sprintf("sni=%s", vmess.SecurityConfig.SNI))
|
||||
}
|
||||
}
|
||||
if vmess.Transport == "websocket" {
|
||||
uriConfig = append(uriConfig, "ws=true")
|
||||
if vmess.TransportConfig.Path != "" {
|
||||
uriConfig = append(uriConfig, fmt.Sprintf("ws-path=%s", vmess.TransportConfig.Path))
|
||||
}
|
||||
if vmess.TransportConfig.Host != "" {
|
||||
uriConfig = append(uriConfig, fmt.Sprintf("ws-headers=Host:%s", vmess.TransportConfig.Host))
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(uriConfig, ",") + "\r\n"
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package surfboard
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/adapter/proxy"
|
||||
)
|
||||
|
||||
func createVMess() proxy.Proxy {
|
||||
|
||||
return proxy.Proxy{
|
||||
Name: "Vmess",
|
||||
Server: "test.xxxx.com",
|
||||
Port: 13002,
|
||||
Protocol: "vmess",
|
||||
Option: proxy.Vmess{
|
||||
Port: 13002,
|
||||
Transport: "websocket",
|
||||
TransportConfig: proxy.TransportConfig{
|
||||
Path: "/ws",
|
||||
Host: "test.xx.com",
|
||||
},
|
||||
Security: "none",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMess(t *testing.T) {
|
||||
node := createVMess()
|
||||
uuid := "123456"
|
||||
p := buildVMess(node, uuid)
|
||||
t.Log(p)
|
||||
}
|
||||
Reference in New Issue
Block a user