1package route
2
3import (
4	"context"
5	"net/http"
6
7	"github.com/Dreamacro/clash/adapters/provider"
8	"github.com/Dreamacro/clash/tunnel"
9
10	"github.com/go-chi/chi/v5"
11	"github.com/go-chi/render"
12)
13
14func proxyProviderRouter() http.Handler {
15	r := chi.NewRouter()
16	r.Get("/", getProviders)
17
18	r.Route("/{name}", func(r chi.Router) {
19		r.Use(parseProviderName, findProviderByName)
20		r.Get("/", getProvider)
21		r.Put("/", updateProvider)
22		r.Get("/healthcheck", healthCheckProvider)
23	})
24	return r
25}
26
27func getProviders(w http.ResponseWriter, r *http.Request) {
28	providers := tunnel.Providers()
29	render.JSON(w, r, render.M{
30		"providers": providers,
31	})
32}
33
34func getProvider(w http.ResponseWriter, r *http.Request) {
35	provider := r.Context().Value(CtxKeyProvider).(provider.ProxyProvider)
36	render.JSON(w, r, provider)
37}
38
39func updateProvider(w http.ResponseWriter, r *http.Request) {
40	provider := r.Context().Value(CtxKeyProvider).(provider.ProxyProvider)
41	if err := provider.Update(); err != nil {
42		render.Status(r, http.StatusServiceUnavailable)
43		render.JSON(w, r, newError(err.Error()))
44		return
45	}
46	render.NoContent(w, r)
47}
48
49func healthCheckProvider(w http.ResponseWriter, r *http.Request) {
50	provider := r.Context().Value(CtxKeyProvider).(provider.ProxyProvider)
51	provider.HealthCheck()
52	render.NoContent(w, r)
53}
54
55func parseProviderName(next http.Handler) http.Handler {
56	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
57		name := getEscapeParam(r, "name")
58		ctx := context.WithValue(r.Context(), CtxKeyProviderName, name)
59		next.ServeHTTP(w, r.WithContext(ctx))
60	})
61}
62
63func findProviderByName(next http.Handler) http.Handler {
64	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
65		name := r.Context().Value(CtxKeyProviderName).(string)
66		providers := tunnel.Providers()
67		provider, exist := providers[name]
68		if !exist {
69			render.Status(r, http.StatusNotFound)
70			render.JSON(w, r, ErrNotFound)
71			return
72		}
73
74		ctx := context.WithValue(r.Context(), CtxKeyProvider, provider)
75		next.ServeHTTP(w, r.WithContext(ctx))
76	})
77}
78