Files
SingBox-Gopanel/internal/protocol/singbox.go
CN-JS-HuiBai 1a03d3d1fc
Some checks failed
build / build (api, amd64, linux) (push) Failing after -51s
build / build (api, arm64, linux) (push) Failing after -51s
build / build (api.exe, amd64, windows) (push) Failing after -51s
修复SS2022订阅下发错误的问题
2026-04-17 23:36:46 +08:00

136 lines
3.2 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)
password := service.GenerateServerPassword(&s, &user)
outbound := buildSingBoxOutbound(conf, password)
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, password string) 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"] = password
case "vmess":
outbound["type"] = "vmess"
outbound["uuid"] = password
outbound["security"] = "auto"
case "trojan":
outbound["type"] = "trojan"
outbound["password"] = password
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)
password := service.GenerateServerPassword(&s, &user)
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"] = password
case "vmess":
outbound["type"] = "vmess"
outbound["uuid"] = password
outbound["security"] = "auto"
case "trojan":
outbound["type"] = "trojan"
outbound["password"] = password
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
}