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  }