Fix integration with clash-dashboard

This commit is contained in:
世界 2022-07-22 09:29:13 +08:00
parent 8004ff51f0
commit 3838d3070d
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
6 changed files with 58 additions and 72 deletions

View file

@ -7,14 +7,14 @@ import (
"strconv" "strconv"
"time" "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/chi/v5"
"github.com/go-chi/render" "github.com/go-chi/render"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
func connectionRouter(trafficManager *trafficontroll.Manager) http.Handler { func connectionRouter(trafficManager *trafficontrol.Manager) http.Handler {
r := chi.NewRouter() r := chi.NewRouter()
r.Get("/", getConnections(trafficManager)) r.Get("/", getConnections(trafficManager))
r.Delete("/", closeAllConnections(trafficManager)) r.Delete("/", closeAllConnections(trafficManager))
@ -22,7 +22,7 @@ func connectionRouter(trafficManager *trafficontroll.Manager) http.Handler {
return r 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) { return func(w http.ResponseWriter, r *http.Request) {
if !websocket.IsWebSocketUpgrade(r) { if !websocket.IsWebSocketUpgrade(r) {
snapshot := trafficManager.Snapshot() 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) { return func(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id") id := chi.URLParam(r, "id")
snapshot := trafficManager.Snapshot() 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) { return func(w http.ResponseWriter, r *http.Request) {
snapshot := trafficManager.Snapshot() snapshot := trafficManager.Snapshot()
for _, c := range snapshot.Connections { for _, c := range snapshot.Connections {

View file

@ -4,15 +4,13 @@ import (
"context" "context"
"net/http" "net/http"
"github.com/sagernet/sing-box/adapter"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/render" "github.com/go-chi/render"
) )
func proxyProviderRouter(server *Server, router adapter.Router) http.Handler { func proxyProviderRouter() http.Handler {
r := chi.NewRouter() r := chi.NewRouter()
r.Get("/", getProviders(server, router)) r.Get("/", getProviders)
r.Route("/{name}", func(r chi.Router) { r.Route("/{name}", func(r chi.Router) {
r.Use(parseProviderName, findProviderByName) r.Use(parseProviderName, findProviderByName)
@ -23,35 +21,10 @@ func proxyProviderRouter(server *Server, router adapter.Router) http.Handler {
return r return r
} }
func getProviders(server *Server, router adapter.Router) func(w http.ResponseWriter, r *http.Request) { func getProviders(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{ render.JSON(w, r, render.M{
"providers": render.M{ "providers": render.M{},
"default": render.M{
"name": "default",
"type": "Proxy",
"proxies": proxies,
"vehicleType": "Compatible",
},
},
}) })
}
} }
func getProvider(w http.ResponseWriter, r *http.Request) { func getProvider(w http.ResponseWriter, r *http.Request) {

View file

@ -19,6 +19,7 @@ import (
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/render" "github.com/go-chi/render"
"sort"
) )
func proxyRouter(server *Server, router adapter.Router) http.Handler { 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("name", detour.Tag())
info.Put("udp", common.Contains(detour.Network(), C.NetworkUDP)) info.Put("udp", common.Contains(detour.Network(), C.NetworkUDP))
delayHistory, loaded := server.delayHistory[detour.Tag()] var delayHistory *DelayHistory
if loaded { var loaded bool
info.Put("history", []*DelayHistory{delayHistory, delayHistory})
} else {
info.Put("history", []*DelayHistory{{Time: time.Now()}, {Time: time.Now()}})
}
if isSelector { if isSelector {
selector := detour.(*outbound.Selector) selector := detour.(*outbound.Selector)
info.Put("now", selector.Now()) info.Put("now", selector.Now())
info.Put("all", selector.All()) 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 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) { func getProxies(server *Server, router adapter.Router) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
var proxyMap badjson.JSONObject 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 // fix clash dashboard
proxyMap.Put("DIRECT", map[string]any{
"type": "Direct",
"name": "DIRECT",
"udp": true,
"history": []*DelayHistory{},
})
proxyMap.Put("GLOBAL", map[string]any{ proxyMap.Put("GLOBAL", map[string]any{
"type": "Selector", "type": "Fallback",
"name": "GLOBAL", "name": "GLOBAL",
"udp": true, "udp": true,
"history": []*DelayHistory{}, "history": []*DelayHistory{},
"all": []string{}, "all": allProxies,
"now": "", "now": defaultTag,
})
proxyMap.Put("REJECT", map[string]any{
"type": "Reject",
"name": "REJECT",
"udp": true,
"history": []*DelayHistory{},
}) })
outbounds := router.Outbounds()
for i, detour := range outbounds { for i, detour := range outbounds {
var tag string var tag string
if detour.Tag() == "" { if detour.Tag() == "" {

View file

@ -11,7 +11,7 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant" 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/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
@ -30,7 +30,7 @@ var _ adapter.ClashServer = (*Server)(nil)
type Server struct { type Server struct {
logger log.Logger logger log.Logger
httpServer *http.Server httpServer *http.Server
trafficManager *trafficontroll.Manager trafficManager *trafficontrol.Manager
delayHistory map[string]*DelayHistory delayHistory map[string]*DelayHistory
} }
@ -40,7 +40,7 @@ type DelayHistory struct {
} }
func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) *Server { func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) *Server {
trafficManager := trafficontroll.NewManager() trafficManager := trafficontrol.NewManager()
chiRouter := chi.NewRouter() chiRouter := chi.NewRouter()
server := &Server{ server := &Server{
logFactory.NewLogger("clash-api"), 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("/proxies", proxyRouter(server, router))
r.Mount("/rules", ruleRouter(router)) r.Mount("/rules", ruleRouter(router))
r.Mount("/connections", connectionRouter(trafficManager)) r.Mount("/connections", connectionRouter(trafficManager))
r.Mount("/providers/proxies", proxyProviderRouter(server, router)) r.Mount("/providers/proxies", proxyProviderRouter())
r.Mount("/providers/rules", ruleProviderRouter()) r.Mount("/providers/rules", ruleProviderRouter())
r.Mount("/script", scriptRouter()) r.Mount("/script", scriptRouter())
r.Mount("/profile", profileRouter()) 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 { 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 { 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 var inbound string
if metadata.Inbound != "" { if metadata.Inbound != "" {
inbound = metadata.InboundType + "/" + metadata.Inbound inbound = metadata.InboundType + "/" + metadata.Inbound
@ -126,7 +126,7 @@ func castMetadata(metadata adapter.InboundContext) trafficontroll.Metadata {
} else { } else {
domain = metadata.Destination.Fqdn domain = metadata.Destination.Fqdn
} }
return trafficontroll.Metadata{ return trafficontrol.Metadata{
NetWork: metadata.Network, NetWork: metadata.Network,
Type: inbound, Type: inbound,
SrcIP: metadata.Source.Addr, SrcIP: metadata.Source.Addr,
@ -189,7 +189,7 @@ type Traffic struct {
Down int64 `json:"down"` 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) { return func(w http.ResponseWriter, r *http.Request) {
var wsConn *websocket.Conn var wsConn *websocket.Conn
if websocket.IsWebSocketUpgrade(r) { if websocket.IsWebSocketUpgrade(r) {

View file

@ -1,4 +1,4 @@
package trafficontroll package trafficontrol
import ( import (
"time" "time"

View file

@ -1,4 +1,4 @@
package trafficontroll package trafficontrol
import ( import (
"net" "net"