Files
SingBox-Gopanel/internal/protocol/singbox.go
CN-JS-HuiBai e8e7664aff
All checks were successful
build / build (api, amd64, linux) (push) Successful in -15s
build / build (api, arm64, linux) (push) Successful in -29s
build / build (api.exe, amd64, windows) (push) Successful in -27s
软件功能基本开发完成,内测BUG等待修复
2026-04-17 22:31:09 +08:00

134 lines
3.1 KiB
Go

package protocol
import (
"encoding/json"
"xboard-go/internal/model"
"xboard-go/internal/service"
)
type SingBoxConfig struct {
Outbounds []map[string]interface{} `json:"outbounds"`
}
func GenerateSingBox(servers []model.Server, user model.User) (string, error) {
template := service.GetSubscribeTemplate("singbox")
if template == "" {
return generateSingBoxFallback(servers, user)
}
var config map[string]any
if err := json.Unmarshal([]byte(template), &config); err != nil {
return generateSingBoxFallback(servers, user)
}
outbounds := make([]any, 0, len(servers))
proxyTags := make([]string, 0, len(servers))
for _, s := range servers {
conf := service.BuildNodeConfig(&s)
outbound := buildSingBoxOutbound(conf, user)
if outbound == nil {
continue
}
outbounds = append(outbounds, outbound)
proxyTags = append(proxyTags, conf.Name)
}
existingOutbounds := anySlice(config["outbounds"])
for index, item := range existingOutbounds {
outbound, ok := item.(map[string]any)
if !ok {
continue
}
outboundType, _ := outbound["type"].(string)
if outboundType != "selector" && outboundType != "urltest" {
continue
}
outbound["outbounds"] = appendUniqueAny(anySlice(outbound["outbounds"]), stringsToAny(proxyTags)...)
existingOutbounds[index] = outbound
}
config["outbounds"] = append(existingOutbounds, outbounds...)
data, err := json.MarshalIndent(config, "", " ")
if err != nil {
return "", err
}
return string(data), nil
}
func buildSingBoxOutbound(conf service.NodeServerConfig, user model.User) map[string]any {
outbound := map[string]any{
"tag": conf.Name,
"server": conf.RawHost,
"server_port": conf.ServerPort,
}
switch conf.Protocol {
case "shadowsocks":
outbound["type"] = "shadowsocks"
outbound["method"] = conf.Cipher
outbound["password"] = user.UUID
case "vmess":
outbound["type"] = "vmess"
outbound["uuid"] = user.UUID
outbound["security"] = "auto"
case "trojan":
outbound["type"] = "trojan"
outbound["password"] = user.UUID
default:
return nil
}
return outbound
}
func generateSingBoxFallback(servers []model.Server, user model.User) (string, error) {
outbounds := []map[string]interface{}{}
proxyTags := []string{}
for _, s := range servers {
conf := service.BuildNodeConfig(&s)
outbound := map[string]interface{}{
"tag": conf.Name,
"server": conf.RawHost,
"server_port": conf.ServerPort,
}
switch conf.Protocol {
case "shadowsocks":
outbound["type"] = "shadowsocks"
outbound["method"] = conf.Cipher
outbound["password"] = user.UUID
case "vmess":
outbound["type"] = "vmess"
outbound["uuid"] = user.UUID
outbound["security"] = "auto"
case "trojan":
outbound["type"] = "trojan"
outbound["password"] = user.UUID
default:
continue
}
outbounds = append(outbounds, outbound)
proxyTags = append(proxyTags, conf.Name)
}
selector := map[string]interface{}{
"type": "selector",
"tag": "Proxy",
"outbounds": proxyTags,
}
outbounds = append(outbounds, selector)
config := SingBoxConfig{
Outbounds: outbounds,
}
data, err := json.MarshalIndent(config, "", " ")
return string(data), err
}