Nodes with port=0 or empty server cause client-side errors (e.g. sing-box decode config failures). Filter them at the render dispatch layer so all targets (sing-box, mihomo, surge, etc.) are protected.
108 lines
3.4 KiB
Go
108 lines
3.4 KiB
Go
package render
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/peterqiu0516/sub-store/internal/model"
|
|
"github.com/peterqiu0516/sub-store/internal/util"
|
|
)
|
|
|
|
// filterInvalidProxies removes nodes missing required server/port fields.
|
|
// These would cause client-side errors (e.g. sing-box "missing server_port").
|
|
func filterInvalidProxies(proxies []model.ProxyNode) []model.ProxyNode {
|
|
result := make([]model.ProxyNode, 0, len(proxies))
|
|
for _, p := range proxies {
|
|
if p == nil {
|
|
continue
|
|
}
|
|
server := getString(p, "server")
|
|
port := getInt(p, "port")
|
|
if server == "" || port <= 0 {
|
|
continue
|
|
}
|
|
result = append(result, p)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// RenderTarget dispatches to the appropriate renderer based on target.
|
|
// This is the simplified entry point matching renderTarget() in the reference.
|
|
func RenderTarget(proxies []model.ProxyNode, target string, template map[string]any) (string, error) {
|
|
proxies = filterInvalidProxies(proxies)
|
|
if len(proxies) == 0 {
|
|
return "", fmt.Errorf("No supported nodes for %s output", target)
|
|
}
|
|
switch target {
|
|
case model.TargetMihomo, model.TargetStash:
|
|
return RenderMihomoYaml(proxies, "https://sub-store.local/convert", template), nil
|
|
case model.TargetSurge:
|
|
return RenderSurgeProxies(proxies)
|
|
case model.TargetSurgeMac:
|
|
return RenderSurgeMacProxies(proxies)
|
|
case model.TargetSurfboard:
|
|
return RenderSurfboardProxies(proxies)
|
|
case model.TargetLoon:
|
|
return RenderLoonProxies(proxies)
|
|
case model.TargetEgern:
|
|
return RenderEgernYaml(proxies)
|
|
case model.TargetQX:
|
|
return RenderQxProxies(proxies)
|
|
case model.TargetSingBox:
|
|
return RenderSingBoxWithTemplate(proxies, template), nil
|
|
case model.TargetV2ray:
|
|
return util.Base64Utf8(RenderProxyUris(proxies)), nil
|
|
case model.TargetURI, model.TargetShadowrocket:
|
|
return RenderProxyUris(proxies), nil
|
|
case model.TargetJSON:
|
|
return RenderJson(proxies), nil
|
|
default:
|
|
return RenderMihomoYaml(proxies, "https://sub-store.local/convert", template), nil
|
|
}
|
|
}
|
|
|
|
// RenderBuildTarget dispatches with a request URL for mihomo source comments.
|
|
func RenderBuildTarget(proxies []model.ProxyNode, target string, requestUrl string, template map[string]any) (string, error) {
|
|
proxies = filterInvalidProxies(proxies)
|
|
if len(proxies) == 0 {
|
|
return "", fmt.Errorf("No supported nodes for %s output", target)
|
|
}
|
|
switch target {
|
|
case model.TargetMihomo, model.TargetStash:
|
|
return RenderMihomoYaml(proxies, requestUrl, template), nil
|
|
case model.TargetSurge:
|
|
return RenderSurgeProxies(proxies)
|
|
case model.TargetSurgeMac:
|
|
return RenderSurgeMacProxies(proxies)
|
|
case model.TargetSurfboard:
|
|
return RenderSurfboardProxies(proxies)
|
|
case model.TargetLoon:
|
|
return RenderLoonProxies(proxies)
|
|
case model.TargetEgern:
|
|
return RenderEgernYaml(proxies)
|
|
case model.TargetShadowrocket:
|
|
return RenderProxyUris(proxies), nil
|
|
case model.TargetQX:
|
|
return RenderQxProxies(proxies)
|
|
case model.TargetSingBox:
|
|
return RenderSingBoxWithTemplate(proxies, template), nil
|
|
case model.TargetV2ray:
|
|
return util.Base64Utf8(RenderProxyUris(proxies)), nil
|
|
case model.TargetURI:
|
|
return RenderProxyUris(proxies), nil
|
|
case model.TargetJSON:
|
|
return RenderJson(proxies), nil
|
|
default:
|
|
return RenderMihomoYaml(proxies, requestUrl, template), nil
|
|
}
|
|
}
|
|
|
|
// sourcePath extracts the path portion of requestUrl for the YAML header comment.
|
|
func sourcePath(requestUrl string) string {
|
|
if u, err := url.Parse(requestUrl); err == nil && u.Path != "" {
|
|
return u.Path
|
|
}
|
|
return strings.TrimPrefix(requestUrl, "https://")
|
|
}
|