github.com/yaling888/clash@v1.53.0/hub/route/provider.go (about) 1 package route 2 3 import ( 4 "context" 5 "net/http" 6 7 "github.com/go-chi/chi/v5" 8 "github.com/go-chi/render" 9 "github.com/phuslu/log" 10 "github.com/samber/lo" 11 12 C "github.com/yaling888/clash/constant" 13 "github.com/yaling888/clash/constant/provider" 14 "github.com/yaling888/clash/tunnel" 15 ) 16 17 func proxyProviderRouter() http.Handler { 18 r := chi.NewRouter() 19 r.Get("/", getProviders) 20 21 r.Route("/{providerName}", func(r chi.Router) { 22 r.Use(parseProviderName, findProviderByName) 23 r.Get("/", getProvider) 24 r.Put("/", updateProvider) 25 r.Get("/healthcheck", healthCheckProvider) 26 r.Mount("/", proxyProviderProxyRouter()) 27 }) 28 return r 29 } 30 31 func proxyProviderProxyRouter() http.Handler { 32 r := chi.NewRouter() 33 r.Route("/{name}", func(r chi.Router) { 34 r.Use(parseProxyName, findProviderProxyByName) 35 r.Get("/", getProxy) 36 r.Get("/healthcheck", getProxyDelay) 37 }) 38 return r 39 } 40 41 func getProviders(w http.ResponseWriter, r *http.Request) { 42 providers := tunnel.Providers() 43 render.JSON(w, r, render.M{ 44 "providers": providers, 45 }) 46 } 47 48 func getProvider(w http.ResponseWriter, r *http.Request) { 49 provider := r.Context().Value(CtxKeyProvider).(provider.ProxyProvider) 50 render.JSON(w, r, provider) 51 } 52 53 func updateProvider(w http.ResponseWriter, r *http.Request) { 54 provider := r.Context().Value(CtxKeyProvider).(provider.ProxyProvider) 55 log.Info().Str("name", provider.Name()).Msg("[API] proxy provider updating...") 56 if err := provider.Update(); err != nil { 57 render.Status(r, http.StatusServiceUnavailable) 58 render.JSON(w, r, newError(err.Error())) 59 log.Error().Err(err).Str("name", provider.Name()).Msg("[API] update proxy provider failed") 60 return 61 } 62 log.Info().Str("name", provider.Name()).Msg("[API] proxy provider updated") 63 render.NoContent(w, r) 64 } 65 66 func healthCheckProvider(w http.ResponseWriter, r *http.Request) { 67 provider := r.Context().Value(CtxKeyProvider).(provider.ProxyProvider) 68 provider.HealthCheck() 69 render.NoContent(w, r) 70 } 71 72 func parseProviderName(next http.Handler) http.Handler { 73 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 74 name := getEscapeParam(r, "providerName") 75 ctx := context.WithValue(r.Context(), CtxKeyProviderName, name) 76 next.ServeHTTP(w, r.WithContext(ctx)) 77 }) 78 } 79 80 func findProviderByName(next http.Handler) http.Handler { 81 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 82 name := r.Context().Value(CtxKeyProviderName).(string) 83 providers := tunnel.Providers() 84 provider, exist := providers[name] 85 if !exist { 86 render.Status(r, http.StatusNotFound) 87 render.JSON(w, r, ErrNotFound) 88 return 89 } 90 91 ctx := context.WithValue(r.Context(), CtxKeyProvider, provider) 92 next.ServeHTTP(w, r.WithContext(ctx)) 93 }) 94 } 95 96 func findProviderProxyByName(next http.Handler) http.Handler { 97 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 98 var ( 99 name = r.Context().Value(CtxKeyProxyName).(string) 100 pd = r.Context().Value(CtxKeyProvider).(provider.ProxyProvider) 101 ) 102 proxy, exist := lo.Find(pd.Proxies(), func(proxy C.Proxy) bool { 103 return proxy.Name() == name 104 }) 105 106 if !exist { 107 render.Status(r, http.StatusNotFound) 108 render.JSON(w, r, ErrNotFound) 109 return 110 } 111 112 ctx := context.WithValue(r.Context(), CtxKeyProxy, proxy) 113 next.ServeHTTP(w, r.WithContext(ctx)) 114 }) 115 }