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://") }