进一步查看前半部分对不对
This commit is contained in:
@@ -29,7 +29,7 @@ func GenerateClashWithTemplate(templateName string, servers []model.Server, user
|
||||
for _, s := range servers {
|
||||
conf := service.BuildNodeConfig(&s)
|
||||
password := service.GenerateServerPassword(&s, &user)
|
||||
proxy := buildClashProxy(conf, password)
|
||||
proxy := buildClashProxy(templateName, conf, password)
|
||||
if proxy == nil {
|
||||
continue
|
||||
}
|
||||
@@ -53,35 +53,88 @@ func GenerateClashWithTemplate(templateName string, servers []model.Server, user
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func buildClashProxy(conf service.NodeServerConfig, password string) map[string]any {
|
||||
func buildClashProxy(templateName string, conf service.NodeServerConfig, password string) map[string]any {
|
||||
switch conf.Protocol {
|
||||
case "shadowsocks":
|
||||
cipher, _ := conf.Cipher.(string)
|
||||
if templateName == "clash" {
|
||||
switch cipher {
|
||||
case "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305":
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return map[string]any{
|
||||
"name": conf.Name,
|
||||
"type": "ss",
|
||||
"server": conf.RawHost,
|
||||
"port": conf.ServerPort,
|
||||
"port": conf.Port,
|
||||
"cipher": cipher,
|
||||
"password": password,
|
||||
"udp": true,
|
||||
}
|
||||
case "vmess":
|
||||
return map[string]any{
|
||||
"name": conf.Name,
|
||||
"type": "vmess",
|
||||
"server": conf.RawHost,
|
||||
"port": conf.ServerPort,
|
||||
"port": conf.Port,
|
||||
"uuid": password,
|
||||
"alterId": 0,
|
||||
"cipher": "auto",
|
||||
"udp": true,
|
||||
}
|
||||
case "vless":
|
||||
if templateName != "clashmeta" {
|
||||
return nil
|
||||
}
|
||||
proxy := map[string]any{
|
||||
"name": conf.Name,
|
||||
"type": "vless",
|
||||
"server": conf.RawHost,
|
||||
"port": conf.Port,
|
||||
"uuid": password,
|
||||
"alterId": 0,
|
||||
"cipher": "auto",
|
||||
"udp": true,
|
||||
"flow": toClashString(conf.Flow),
|
||||
"encryption": "none",
|
||||
"tls": false,
|
||||
}
|
||||
switch toClashInt(conf.Tls) {
|
||||
case 1:
|
||||
proxy["tls"] = true
|
||||
if tlsSettings, ok := conf.TlsSettings.(map[string]any); ok {
|
||||
proxy["skip-cert-verify"] = toClashBool(tlsSettings["allow_insecure"])
|
||||
if serverName := toClashString(tlsSettings["server_name"]); serverName != "" {
|
||||
proxy["servername"] = serverName
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
proxy["tls"] = true
|
||||
if tlsSettings, ok := conf.TlsSettings.(map[string]any); ok {
|
||||
proxy["skip-cert-verify"] = toClashBool(tlsSettings["allow_insecure"])
|
||||
if serverName := toClashString(tlsSettings["server_name"]); serverName != "" {
|
||||
proxy["servername"] = serverName
|
||||
}
|
||||
proxy["reality-opts"] = map[string]any{
|
||||
"public-key": toClashString(tlsSettings["public_key"]),
|
||||
"short-id": toClashString(tlsSettings["short_id"]),
|
||||
}
|
||||
}
|
||||
}
|
||||
network := toClashString(conf.Network)
|
||||
if network == "" {
|
||||
network = "tcp"
|
||||
}
|
||||
proxy["network"] = network
|
||||
return proxy
|
||||
case "trojan":
|
||||
return map[string]any{
|
||||
"name": conf.Name,
|
||||
"type": "trojan",
|
||||
"server": conf.RawHost,
|
||||
"port": conf.ServerPort,
|
||||
"port": conf.Port,
|
||||
"password": password,
|
||||
"udp": true,
|
||||
}
|
||||
@@ -155,6 +208,54 @@ func appendUniqueAny(base []any, values ...any) []any {
|
||||
return base
|
||||
}
|
||||
|
||||
func toClashString(value any) string {
|
||||
switch typed := value.(type) {
|
||||
case string:
|
||||
return typed
|
||||
case nil:
|
||||
return ""
|
||||
default:
|
||||
return fmt.Sprint(typed)
|
||||
}
|
||||
}
|
||||
|
||||
func toClashInt(value any) int {
|
||||
switch typed := value.(type) {
|
||||
case int:
|
||||
return typed
|
||||
case int64:
|
||||
return int(typed)
|
||||
case float64:
|
||||
return int(typed)
|
||||
case string:
|
||||
if typed == "" {
|
||||
return 0
|
||||
}
|
||||
var result int
|
||||
fmt.Sscanf(typed, "%d", &result)
|
||||
return result
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func toClashBool(value any) bool {
|
||||
switch typed := value.(type) {
|
||||
case bool:
|
||||
return typed
|
||||
case int:
|
||||
return typed != 0
|
||||
case int64:
|
||||
return typed != 0
|
||||
case float64:
|
||||
return typed != 0
|
||||
case string:
|
||||
return typed == "1" || strings.EqualFold(typed, "true")
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func generateClashFallback(servers []model.Server, user model.User) string {
|
||||
var builder strings.Builder
|
||||
|
||||
@@ -163,7 +264,7 @@ func generateClashFallback(servers []model.Server, user model.User) string {
|
||||
for _, s := range servers {
|
||||
conf := service.BuildNodeConfig(&s)
|
||||
password := service.GenerateServerPassword(&s, &user)
|
||||
proxy := buildClashProxy(conf, password)
|
||||
proxy := buildClashProxy("clash", conf, password)
|
||||
if proxy == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ func buildShadowsocks(c service.NodeServerConfig, password string) string {
|
||||
userInfo := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", cipher, password)))
|
||||
userInfo = strings.TrimRight(strings.ReplaceAll(strings.ReplaceAll(userInfo, "+", "-"), "/", "_"), "=")
|
||||
|
||||
link := fmt.Sprintf("ss://%s@%s:%d", userInfo, wrapIPv6(c.RawHost), c.ServerPort)
|
||||
link := fmt.Sprintf("ss://%s@%s:%d", userInfo, wrapIPv6(c.RawHost), c.Port)
|
||||
params := url.Values{}
|
||||
if plugin := toString(c.Plugin); plugin != "" {
|
||||
opts := toString(c.PluginOpts)
|
||||
@@ -69,7 +69,7 @@ func buildVmess(c service.NodeServerConfig, password string) string {
|
||||
"v": "2",
|
||||
"ps": c.Name,
|
||||
"add": c.RawHost,
|
||||
"port": fmt.Sprintf("%d", c.ServerPort),
|
||||
"port": fmt.Sprintf("%d", c.Port),
|
||||
"id": password,
|
||||
"aid": "0",
|
||||
"net": toString(c.Network),
|
||||
@@ -145,7 +145,7 @@ func buildVless(c service.NodeServerConfig, password string) string {
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("vless://%s@%s:%d?%s#%s", password, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
return fmt.Sprintf("vless://%s@%s:%d?%s#%s", password, wrapIPv6(c.RawHost), c.Port, params.Encode(), url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
func buildTrojan(c service.NodeServerConfig, password string) string {
|
||||
@@ -176,7 +176,7 @@ func buildTrojan(c service.NodeServerConfig, password string) string {
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("trojan://%s@%s:%d?%s#%s", password, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
return fmt.Sprintf("trojan://%s@%s:%d?%s#%s", password, wrapIPv6(c.RawHost), c.Port, params.Encode(), url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
func buildHysteria(c service.NodeServerConfig, password string) string {
|
||||
@@ -188,7 +188,10 @@ func buildHysteria(c service.NodeServerConfig, password string) string {
|
||||
params.Set("obfs", "salamander")
|
||||
params.Set("obfs-password", toString(c.ObfsPassword))
|
||||
}
|
||||
return fmt.Sprintf("hysteria2://%s@%s:%d?%s#%s", password, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
if c.Ports != "" {
|
||||
params.Set("mport", c.Ports)
|
||||
}
|
||||
return fmt.Sprintf("hysteria2://%s@%s:%d?%s#%s", password, wrapIPv6(c.RawHost), c.Port, params.Encode(), url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
params.Set("protocol", "udp")
|
||||
@@ -200,7 +203,7 @@ func buildHysteria(c service.NodeServerConfig, password string) string {
|
||||
params.Set("downmbps", fmt.Sprintf("%v", c.DownMbps))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("hysteria://%s:%d?%s#%s", wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
return fmt.Sprintf("hysteria://%s:%d?%s#%s", wrapIPv6(c.RawHost), c.Port, params.Encode(), url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
func buildTuic(c service.NodeServerConfig, password string) string {
|
||||
@@ -209,23 +212,23 @@ func buildTuic(c service.NodeServerConfig, password string) string {
|
||||
params.Set("congestion_control", toString(c.CongestionControl))
|
||||
params.Set("udp-relay-mode", "native")
|
||||
|
||||
return fmt.Sprintf("tuic://%s:%s@%s:%d?%s#%s", password, password, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
return fmt.Sprintf("tuic://%s:%s@%s:%d?%s#%s", password, password, wrapIPv6(c.RawHost), c.Port, params.Encode(), url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
func buildAnyTLS(c service.NodeServerConfig, password string) string {
|
||||
params := url.Values{}
|
||||
params.Set("sni", toString(c.ServerName))
|
||||
return fmt.Sprintf("anytls://%s@%s:%d?%s#%s", password, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
return fmt.Sprintf("anytls://%s@%s:%d?%s#%s", password, wrapIPv6(c.RawHost), c.Port, params.Encode(), url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
func buildSocks(c service.NodeServerConfig, password string) string {
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(password + ":" + password))
|
||||
return fmt.Sprintf("socks://%s@%s:%d#%s", auth, wrapIPv6(c.RawHost), c.ServerPort, url.PathEscape(c.Name))
|
||||
return fmt.Sprintf("socks://%s@%s:%d#%s", auth, wrapIPv6(c.RawHost), c.Port, url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
func buildHttp(c service.NodeServerConfig, password string) string {
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(password + ":" + password))
|
||||
link := fmt.Sprintf("http://%s@%s:%d", auth, wrapIPv6(c.RawHost), c.ServerPort)
|
||||
link := fmt.Sprintf("http://%s@%s:%d", auth, wrapIPv6(c.RawHost), c.Port)
|
||||
if toInt(c.Tls) > 0 {
|
||||
params := url.Values{}
|
||||
params.Set("security", "tls")
|
||||
|
||||
@@ -64,7 +64,7 @@ func buildSingBoxOutbound(conf service.NodeServerConfig, password string) map[st
|
||||
outbound := map[string]any{
|
||||
"tag": conf.Name,
|
||||
"server": conf.RawHost,
|
||||
"server_port": conf.ServerPort,
|
||||
"server_port": conf.Port,
|
||||
}
|
||||
|
||||
switch conf.Protocol {
|
||||
@@ -76,6 +76,19 @@ func buildSingBoxOutbound(conf service.NodeServerConfig, password string) map[st
|
||||
outbound["type"] = "vmess"
|
||||
outbound["uuid"] = password
|
||||
outbound["security"] = "auto"
|
||||
case "vless":
|
||||
outbound["type"] = "vless"
|
||||
outbound["uuid"] = password
|
||||
outbound["packet_encoding"] = "xudp"
|
||||
if flow := singBoxString(conf.Flow); flow != "" {
|
||||
outbound["flow"] = flow
|
||||
}
|
||||
if tls := buildSingBoxTLS(conf); tls != nil {
|
||||
outbound["tls"] = tls
|
||||
}
|
||||
if transport := buildSingBoxTransport(conf); transport != nil {
|
||||
outbound["transport"] = transport
|
||||
}
|
||||
case "trojan":
|
||||
outbound["type"] = "trojan"
|
||||
outbound["password"] = password
|
||||
@@ -96,7 +109,7 @@ func generateSingBoxFallback(servers []model.Server, user model.User) (string, e
|
||||
outbound := map[string]interface{}{
|
||||
"tag": conf.Name,
|
||||
"server": conf.RawHost,
|
||||
"server_port": conf.ServerPort,
|
||||
"server_port": conf.Port,
|
||||
}
|
||||
|
||||
switch conf.Protocol {
|
||||
@@ -108,6 +121,19 @@ func generateSingBoxFallback(servers []model.Server, user model.User) (string, e
|
||||
outbound["type"] = "vmess"
|
||||
outbound["uuid"] = password
|
||||
outbound["security"] = "auto"
|
||||
case "vless":
|
||||
outbound["type"] = "vless"
|
||||
outbound["uuid"] = password
|
||||
outbound["packet_encoding"] = "xudp"
|
||||
if flow := singBoxString(conf.Flow); flow != "" {
|
||||
outbound["flow"] = flow
|
||||
}
|
||||
if tls := buildSingBoxTLS(conf); tls != nil {
|
||||
outbound["tls"] = tls
|
||||
}
|
||||
if transport := buildSingBoxTransport(conf); transport != nil {
|
||||
outbound["transport"] = transport
|
||||
}
|
||||
case "trojan":
|
||||
outbound["type"] = "trojan"
|
||||
outbound["password"] = password
|
||||
@@ -133,3 +159,128 @@ func generateSingBoxFallback(servers []model.Server, user model.User) (string, e
|
||||
data, err := json.MarshalIndent(config, "", " ")
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
func buildSingBoxTLS(conf service.NodeServerConfig) map[string]any {
|
||||
switch singBoxInt(conf.Tls) {
|
||||
case 1:
|
||||
tls := map[string]any{
|
||||
"enabled": true,
|
||||
}
|
||||
if tlsSettings, ok := conf.TlsSettings.(map[string]any); ok {
|
||||
tls["insecure"] = singBoxBool(tlsSettings["allow_insecure"])
|
||||
if serverName := singBoxString(tlsSettings["server_name"]); serverName != "" {
|
||||
tls["server_name"] = serverName
|
||||
}
|
||||
}
|
||||
return tls
|
||||
case 2:
|
||||
tls := map[string]any{
|
||||
"enabled": true,
|
||||
}
|
||||
if tlsSettings, ok := conf.TlsSettings.(map[string]any); ok {
|
||||
tls["insecure"] = singBoxBool(tlsSettings["allow_insecure"])
|
||||
if serverName := singBoxString(tlsSettings["server_name"]); serverName != "" {
|
||||
tls["server_name"] = serverName
|
||||
}
|
||||
tls["reality"] = map[string]any{
|
||||
"enabled": true,
|
||||
"public_key": singBoxString(tlsSettings["public_key"]),
|
||||
"short_id": singBoxString(tlsSettings["short_id"]),
|
||||
}
|
||||
}
|
||||
return tls
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func buildSingBoxTransport(conf service.NodeServerConfig) map[string]any {
|
||||
network := singBoxString(conf.Network)
|
||||
settings, _ := conf.NetworkSettings.(map[string]any)
|
||||
switch network {
|
||||
case "ws":
|
||||
transport := map[string]any{
|
||||
"type": "ws",
|
||||
}
|
||||
if settings != nil {
|
||||
if path := singBoxString(settings["path"]); path != "" {
|
||||
transport["path"] = path
|
||||
}
|
||||
if headers, ok := settings["headers"].(map[string]any); ok {
|
||||
if host := singBoxString(headers["Host"]); host != "" {
|
||||
transport["headers"] = map[string]any{"Host": host}
|
||||
}
|
||||
}
|
||||
}
|
||||
return transport
|
||||
case "grpc":
|
||||
transport := map[string]any{
|
||||
"type": "grpc",
|
||||
}
|
||||
if settings != nil {
|
||||
if serviceName := singBoxString(settings["serviceName"]); serviceName != "" {
|
||||
transport["service_name"] = serviceName
|
||||
}
|
||||
}
|
||||
return transport
|
||||
case "httpupgrade":
|
||||
transport := map[string]any{
|
||||
"type": "httpupgrade",
|
||||
}
|
||||
if settings != nil {
|
||||
if path := singBoxString(settings["path"]); path != "" {
|
||||
transport["path"] = path
|
||||
}
|
||||
if host := singBoxString(settings["host"]); host != "" {
|
||||
transport["host"] = host
|
||||
}
|
||||
if headers, ok := settings["headers"].(map[string]any); ok && len(headers) > 0 {
|
||||
transport["headers"] = headers
|
||||
}
|
||||
}
|
||||
return transport
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func singBoxString(value any) string {
|
||||
switch typed := value.(type) {
|
||||
case string:
|
||||
return typed
|
||||
case nil:
|
||||
return ""
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func singBoxInt(value any) int {
|
||||
switch typed := value.(type) {
|
||||
case int:
|
||||
return typed
|
||||
case int64:
|
||||
return int(typed)
|
||||
case float64:
|
||||
return int(typed)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func singBoxBool(value any) bool {
|
||||
switch typed := value.(type) {
|
||||
case bool:
|
||||
return typed
|
||||
case int:
|
||||
return typed != 0
|
||||
case int64:
|
||||
return typed != 0
|
||||
case float64:
|
||||
return typed != 0
|
||||
case string:
|
||||
return typed == "1" || typed == "true"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,8 @@ type NodeServerConfig struct {
|
||||
Name string `json:"-"`
|
||||
Protocol string `json:"protocol"`
|
||||
RawHost string `json:"-"`
|
||||
Port int `json:"-"`
|
||||
Ports string `json:"-"`
|
||||
ListenIP string `json:"listen_ip"`
|
||||
ServerPort int `json:"server_port"`
|
||||
Network any `json:"network"`
|
||||
@@ -230,10 +232,13 @@ func CurrentRate(server *model.Server) float64 {
|
||||
|
||||
func BuildNodeConfig(node *model.Server) NodeServerConfig {
|
||||
settings := parseObject(node.ProtocolSettings)
|
||||
clientPort, portRange := resolveClientPort(node.Port, node.ServerPort)
|
||||
response := NodeServerConfig{
|
||||
Name: node.Name,
|
||||
Protocol: node.Type,
|
||||
RawHost: node.Host,
|
||||
Port: clientPort,
|
||||
Ports: portRange,
|
||||
ListenIP: "0.0.0.0",
|
||||
ServerPort: node.ServerPort,
|
||||
Network: getMapAny(settings, "network"),
|
||||
@@ -408,6 +413,31 @@ func uuidPrefixBase64(uuid string, size int) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(uuid[:size]))
|
||||
}
|
||||
|
||||
func resolveClientPort(rawPort string, fallback int) (int, string) {
|
||||
rawPort = strings.TrimSpace(rawPort)
|
||||
if rawPort == "" {
|
||||
return fallback, ""
|
||||
}
|
||||
|
||||
if strings.Contains(rawPort, "-") {
|
||||
parts := strings.SplitN(rawPort, "-", 2)
|
||||
start, errStart := strconv.Atoi(strings.TrimSpace(parts[0]))
|
||||
end, errEnd := strconv.Atoi(strings.TrimSpace(parts[1]))
|
||||
if errStart == nil && errEnd == nil && start > 0 && end >= start {
|
||||
if start == end {
|
||||
return start, rawPort
|
||||
}
|
||||
return start + int(time.Now().UnixNano()%int64(end-start+1)), rawPort
|
||||
}
|
||||
}
|
||||
|
||||
if port, err := strconv.Atoi(rawPort); err == nil && port > 0 {
|
||||
return port, ""
|
||||
}
|
||||
|
||||
return fallback, ""
|
||||
}
|
||||
|
||||
func parseIntSlice(raw *string) []int {
|
||||
if raw == nil || strings.TrimSpace(*raw) == "" {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user