134 lines
3.1 KiB
Go
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
|
|
}
|