platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
132
selective-vpn-api/app/transport_bootstrap_bypass_candidates.go
Normal file
132
selective-vpn-api/app/transport_bootstrap_bypass_candidates.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func transportCollectBootstrapCandidates(client TransportClient) []string {
|
||||
candidates := make([]string, 0, 16)
|
||||
keys := []string{
|
||||
"bootstrap_endpoint",
|
||||
"bootstrap_endpoints",
|
||||
"bootstrap_host",
|
||||
"bootstrap_hosts",
|
||||
"endpoint",
|
||||
"endpoint_host",
|
||||
"remote_host",
|
||||
"server",
|
||||
"host",
|
||||
"url",
|
||||
"uri",
|
||||
"link",
|
||||
"ssh_host",
|
||||
}
|
||||
for _, key := range keys {
|
||||
candidates = append(candidates, transportConfigValues(client.Config, key)...)
|
||||
}
|
||||
if client.Kind == TransportClientSingBox {
|
||||
candidates = append(candidates, transportCollectSingBoxConfigCandidates(client)...)
|
||||
}
|
||||
return dedupeStrings(candidates)
|
||||
}
|
||||
|
||||
func transportCollectSingBoxConfigCandidates(client TransportClient) []string {
|
||||
path := transportSingBoxConfigPath(client)
|
||||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil || len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
var root map[string]any
|
||||
if err := json.Unmarshal(data, &root); err != nil {
|
||||
return nil
|
||||
}
|
||||
rawOutbounds, ok := root["outbounds"].([]any)
|
||||
if !ok || len(rawOutbounds) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, 8)
|
||||
for _, raw := range rawOutbounds {
|
||||
transportCollectServerCandidatesRecursive(raw, &out)
|
||||
}
|
||||
return dedupeStrings(out)
|
||||
}
|
||||
|
||||
func transportCollectServerCandidatesRecursive(node any, out *[]string) {
|
||||
switch v := node.(type) {
|
||||
case map[string]any:
|
||||
for key, val := range v {
|
||||
k := strings.ToLower(strings.TrimSpace(key))
|
||||
if k == "server" || k == "address" || k == "host" {
|
||||
*out = append(*out, transportValueToStrings(val)...)
|
||||
}
|
||||
transportCollectServerCandidatesRecursive(val, out)
|
||||
}
|
||||
case []any:
|
||||
for _, item := range v {
|
||||
transportCollectServerCandidatesRecursive(item, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func transportConfigValues(cfg map[string]any, key string) []string {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
raw, ok := cfg[key]
|
||||
if !ok || raw == nil {
|
||||
return nil
|
||||
}
|
||||
return transportValueToStrings(raw)
|
||||
}
|
||||
|
||||
func transportValueToStrings(raw any) []string {
|
||||
switch v := raw.(type) {
|
||||
case string:
|
||||
return transportSplitValue(v)
|
||||
case []string:
|
||||
out := make([]string, 0, len(v))
|
||||
for _, it := range v {
|
||||
out = append(out, transportSplitValue(it)...)
|
||||
}
|
||||
return out
|
||||
case []any:
|
||||
out := make([]string, 0, len(v))
|
||||
for _, it := range v {
|
||||
switch vv := it.(type) {
|
||||
case string:
|
||||
out = append(out, transportSplitValue(vv)...)
|
||||
default:
|
||||
out = append(out, transportSplitValue(fmt.Sprint(vv))...)
|
||||
}
|
||||
}
|
||||
return out
|
||||
default:
|
||||
return transportSplitValue(fmt.Sprint(v))
|
||||
}
|
||||
}
|
||||
|
||||
func transportSplitValue(raw string) []string {
|
||||
s := strings.TrimSpace(raw)
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
if strings.ContainsAny(s, ",;\n\t") {
|
||||
repl := strings.NewReplacer(",", " ", ";", " ", "\n", " ", "\t", " ")
|
||||
fields := strings.Fields(repl.Replace(s))
|
||||
out := make([]string, 0, len(fields))
|
||||
for _, f := range fields {
|
||||
ff := strings.TrimSpace(f)
|
||||
if ff != "" {
|
||||
out = append(out, ff)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
return []string{s}
|
||||
}
|
||||
Reference in New Issue
Block a user