init: 1.0.0

This commit is contained in:
Chang lue Tsen
2025-04-25 12:08:29 +09:00
commit 8addcc584b
1031 changed files with 76472 additions and 0 deletions
+111
View File
@@ -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
}
+24
View File
@@ -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))
}
+29
View File
@@ -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 }}
+12
View File
@@ -0,0 +1,12 @@
package surfboard
import "time"
type UserInfo struct {
UUID string
Upload int64
Download int64
TotalTraffic int64
ExpiredDate time.Time
SubscribeURL string
}
+24
View File
@@ -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"
}
+28
View File
@@ -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)
}
+41
View File
@@ -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"
}
+36
View File
@@ -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)
}
+45
View File
@@ -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"
}
+33
View File
@@ -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)
}