diff --git a/experimental/clashapi/connections.go b/experimental/clashapi/connections.go index 0079497e..5e1aa3a8 100644 --- a/experimental/clashapi/connections.go +++ b/experimental/clashapi/connections.go @@ -7,14 +7,14 @@ import ( "strconv" "time" - "github.com/sagernet/sing-box/experimental/clashapi/trafficontroll" + "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol" "github.com/go-chi/chi/v5" "github.com/go-chi/render" "github.com/gorilla/websocket" ) -func connectionRouter(trafficManager *trafficontroll.Manager) http.Handler { +func connectionRouter(trafficManager *trafficontrol.Manager) http.Handler { r := chi.NewRouter() r.Get("/", getConnections(trafficManager)) r.Delete("/", closeAllConnections(trafficManager)) @@ -22,7 +22,7 @@ func connectionRouter(trafficManager *trafficontroll.Manager) http.Handler { return r } -func getConnections(trafficManager *trafficontroll.Manager) func(w http.ResponseWriter, r *http.Request) { +func getConnections(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { if !websocket.IsWebSocketUpgrade(r) { snapshot := trafficManager.Snapshot() @@ -72,7 +72,7 @@ func getConnections(trafficManager *trafficontroll.Manager) func(w http.Response } } -func closeConnection(trafficManager *trafficontroll.Manager) func(w http.ResponseWriter, r *http.Request) { +func closeConnection(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") snapshot := trafficManager.Snapshot() @@ -86,7 +86,7 @@ func closeConnection(trafficManager *trafficontroll.Manager) func(w http.Respons } } -func closeAllConnections(trafficManager *trafficontroll.Manager) func(w http.ResponseWriter, r *http.Request) { +func closeAllConnections(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { snapshot := trafficManager.Snapshot() for _, c := range snapshot.Connections { diff --git a/experimental/clashapi/provider.go b/experimental/clashapi/provider.go index 32ecf1e2..352b2894 100644 --- a/experimental/clashapi/provider.go +++ b/experimental/clashapi/provider.go @@ -4,15 +4,13 @@ import ( "context" "net/http" - "github.com/sagernet/sing-box/adapter" - "github.com/go-chi/chi/v5" "github.com/go-chi/render" ) -func proxyProviderRouter(server *Server, router adapter.Router) http.Handler { +func proxyProviderRouter() http.Handler { r := chi.NewRouter() - r.Get("/", getProviders(server, router)) + r.Get("/", getProviders) r.Route("/{name}", func(r chi.Router) { r.Use(parseProviderName, findProviderByName) @@ -23,35 +21,10 @@ func proxyProviderRouter(server *Server, router adapter.Router) http.Handler { return r } -func getProviders(server *Server, router adapter.Router) func(w http.ResponseWriter, r *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - var proxies []any - proxies = append(proxies, render.M{ - "history": []*DelayHistory{}, - "name": "DIRECT", - "type": "Direct", - "udp": true, - }) - proxies = append(proxies, render.M{ - "history": []*DelayHistory{}, - "name": "REJECT", - "type": "Reject", - "udp": true, - }) - for _, detour := range router.Outbounds() { - proxies = append(proxies, proxyInfo(server, detour)) - } - render.JSON(w, r, render.M{ - "providers": render.M{ - "default": render.M{ - "name": "default", - "type": "Proxy", - "proxies": proxies, - "vehicleType": "Compatible", - }, - }, - }) - } +func getProviders(w http.ResponseWriter, r *http.Request) { + render.JSON(w, r, render.M{ + "providers": render.M{}, + }) } func getProvider(w http.ResponseWriter, r *http.Request) { diff --git a/experimental/clashapi/proxies.go b/experimental/clashapi/proxies.go index 53bd0faa..4af9b77b 100644 --- a/experimental/clashapi/proxies.go +++ b/experimental/clashapi/proxies.go @@ -19,6 +19,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/render" + "sort" ) func proxyRouter(server *Server, router adapter.Router) http.Handler { @@ -85,17 +86,20 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject { info.Put("name", detour.Tag()) info.Put("udp", common.Contains(detour.Network(), C.NetworkUDP)) - delayHistory, loaded := server.delayHistory[detour.Tag()] - if loaded { - info.Put("history", []*DelayHistory{delayHistory, delayHistory}) - } else { - info.Put("history", []*DelayHistory{{Time: time.Now()}, {Time: time.Now()}}) - } - + var delayHistory *DelayHistory + var loaded bool if isSelector { selector := detour.(*outbound.Selector) info.Put("now", selector.Now()) info.Put("all", selector.All()) + delayHistory, loaded = server.delayHistory[selector.Now()] + } else { + delayHistory, loaded = server.delayHistory[detour.Tag()] + } + if loaded { + info.Put("history", []*DelayHistory{delayHistory}) + } else { + info.Put("history", []*DelayHistory{}) } return &info } @@ -103,30 +107,39 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject { func getProxies(server *Server, router adapter.Router) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { var proxyMap badjson.JSONObject + outbounds := common.Filter(router.Outbounds(), func(detour adapter.Outbound) bool { + return detour.Tag() != "" + }) + + allProxies := make([]string, 0, len(outbounds)) + + for _, detour := range outbounds { + switch detour.Type() { + case C.TypeDirect, C.TypeBlock: + continue + } + allProxies = append(allProxies, detour.Tag()) + } + + defaultTag := router.DefaultOutbound(C.NetworkTCP).Tag() + if defaultTag == "" { + defaultTag = allProxies[0] + } + + sort.Slice(allProxies, func(i, j int) bool { + return allProxies[i] == defaultTag + }) // fix clash dashboard - proxyMap.Put("DIRECT", map[string]any{ - "type": "Direct", - "name": "DIRECT", - "udp": true, - "history": []*DelayHistory{}, - }) proxyMap.Put("GLOBAL", map[string]any{ - "type": "Selector", + "type": "Fallback", "name": "GLOBAL", "udp": true, "history": []*DelayHistory{}, - "all": []string{}, - "now": "", - }) - proxyMap.Put("REJECT", map[string]any{ - "type": "Reject", - "name": "REJECT", - "udp": true, - "history": []*DelayHistory{}, + "all": allProxies, + "now": defaultTag, }) - outbounds := router.Outbounds() for i, detour := range outbounds { var tag string if detour.Tag() == "" { diff --git a/experimental/clashapi/server.go b/experimental/clashapi/server.go index 31f4b984..280d0dfd 100644 --- a/experimental/clashapi/server.go +++ b/experimental/clashapi/server.go @@ -11,7 +11,7 @@ import ( "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/experimental/clashapi/trafficontroll" + "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" @@ -30,7 +30,7 @@ var _ adapter.ClashServer = (*Server)(nil) type Server struct { logger log.Logger httpServer *http.Server - trafficManager *trafficontroll.Manager + trafficManager *trafficontrol.Manager delayHistory map[string]*DelayHistory } @@ -40,7 +40,7 @@ type DelayHistory struct { } func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) *Server { - trafficManager := trafficontroll.NewManager() + trafficManager := trafficontrol.NewManager() chiRouter := chi.NewRouter() server := &Server{ logFactory.NewLogger("clash-api"), @@ -68,7 +68,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options r.Mount("/proxies", proxyRouter(server, router)) r.Mount("/rules", ruleRouter(router)) r.Mount("/connections", connectionRouter(trafficManager)) - r.Mount("/providers/proxies", proxyProviderRouter(server, router)) + r.Mount("/providers/proxies", proxyProviderRouter()) r.Mount("/providers/rules", ruleProviderRouter()) r.Mount("/script", scriptRouter()) r.Mount("/profile", profileRouter()) @@ -106,14 +106,14 @@ func (s *Server) Close() error { } func (s *Server) RoutedConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule) net.Conn { - return trafficontroll.NewTCPTracker(conn, s.trafficManager, castMetadata(metadata), matchedRule) + return trafficontrol.NewTCPTracker(conn, s.trafficManager, castMetadata(metadata), matchedRule) } func (s *Server) RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, matchedRule adapter.Rule) N.PacketConn { - return trafficontroll.NewUDPTracker(conn, s.trafficManager, castMetadata(metadata), matchedRule) + return trafficontrol.NewUDPTracker(conn, s.trafficManager, castMetadata(metadata), matchedRule) } -func castMetadata(metadata adapter.InboundContext) trafficontroll.Metadata { +func castMetadata(metadata adapter.InboundContext) trafficontrol.Metadata { var inbound string if metadata.Inbound != "" { inbound = metadata.InboundType + "/" + metadata.Inbound @@ -126,7 +126,7 @@ func castMetadata(metadata adapter.InboundContext) trafficontroll.Metadata { } else { domain = metadata.Destination.Fqdn } - return trafficontroll.Metadata{ + return trafficontrol.Metadata{ NetWork: metadata.Network, Type: inbound, SrcIP: metadata.Source.Addr, @@ -189,7 +189,7 @@ type Traffic struct { Down int64 `json:"down"` } -func traffic(trafficManager *trafficontroll.Manager) func(w http.ResponseWriter, r *http.Request) { +func traffic(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { var wsConn *websocket.Conn if websocket.IsWebSocketUpgrade(r) { diff --git a/experimental/clashapi/trafficontroll/manager.go b/experimental/clashapi/trafficontrol/manager.go similarity index 98% rename from experimental/clashapi/trafficontroll/manager.go rename to experimental/clashapi/trafficontrol/manager.go index 60f8edfe..ba8fbf87 100644 --- a/experimental/clashapi/trafficontroll/manager.go +++ b/experimental/clashapi/trafficontrol/manager.go @@ -1,4 +1,4 @@ -package trafficontroll +package trafficontrol import ( "time" diff --git a/experimental/clashapi/trafficontroll/tracker.go b/experimental/clashapi/trafficontrol/tracker.go similarity index 99% rename from experimental/clashapi/trafficontroll/tracker.go rename to experimental/clashapi/trafficontrol/tracker.go index 25778c59..f88401d0 100644 --- a/experimental/clashapi/trafficontroll/tracker.go +++ b/experimental/clashapi/trafficontrol/tracker.go @@ -1,4 +1,4 @@ -package trafficontroll +package trafficontrol import ( "net"