1package route 2 3import ( 4 "net/http" 5 "path/filepath" 6 7 "github.com/Dreamacro/clash/component/resolver" 8 "github.com/Dreamacro/clash/config" 9 "github.com/Dreamacro/clash/hub/executor" 10 "github.com/Dreamacro/clash/log" 11 P "github.com/Dreamacro/clash/proxy" 12 "github.com/Dreamacro/clash/tunnel" 13 14 "github.com/go-chi/chi/v5" 15 "github.com/go-chi/render" 16) 17 18func configRouter() http.Handler { 19 r := chi.NewRouter() 20 r.Get("/", getConfigs) 21 r.Put("/", updateConfigs) 22 r.Patch("/", patchConfigs) 23 return r 24} 25 26type configSchema struct { 27 Port *int `json:"port"` 28 SocksPort *int `json:"socks-port"` 29 RedirPort *int `json:"redir-port"` 30 TProxyPort *int `json:"tproxy-port"` 31 MixedPort *int `json:"mixed-port"` 32 AllowLan *bool `json:"allow-lan"` 33 BindAddress *string `json:"bind-address"` 34 Mode *tunnel.TunnelMode `json:"mode"` 35 LogLevel *log.LogLevel `json:"log-level"` 36 IPv6 *bool `json:"ipv6"` 37} 38 39func getConfigs(w http.ResponseWriter, r *http.Request) { 40 general := executor.GetGeneral() 41 render.JSON(w, r, general) 42} 43 44func pointerOrDefault(p *int, def int) int { 45 if p != nil { 46 return *p 47 } 48 49 return def 50} 51 52func patchConfigs(w http.ResponseWriter, r *http.Request) { 53 general := &configSchema{} 54 if err := render.DecodeJSON(r.Body, general); err != nil { 55 render.Status(r, http.StatusBadRequest) 56 render.JSON(w, r, ErrBadRequest) 57 return 58 } 59 60 if general.AllowLan != nil { 61 P.SetAllowLan(*general.AllowLan) 62 } 63 64 if general.BindAddress != nil { 65 P.SetBindAddress(*general.BindAddress) 66 } 67 68 ports := P.GetPorts() 69 P.ReCreateHTTP(pointerOrDefault(general.Port, ports.Port)) 70 P.ReCreateSocks(pointerOrDefault(general.SocksPort, ports.SocksPort)) 71 P.ReCreateRedir(pointerOrDefault(general.RedirPort, ports.RedirPort)) 72 P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort)) 73 P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort)) 74 75 if general.Mode != nil { 76 tunnel.SetMode(*general.Mode) 77 } 78 79 if general.LogLevel != nil { 80 log.SetLevel(*general.LogLevel) 81 } 82 83 if general.IPv6 != nil { 84 resolver.DisableIPv6 = !*general.IPv6 85 } 86 87 render.NoContent(w, r) 88} 89 90type updateConfigRequest struct { 91 Path string `json:"path"` 92 Payload string `json:"payload"` 93} 94 95func updateConfigs(w http.ResponseWriter, r *http.Request) { 96 req := updateConfigRequest{} 97 if err := render.DecodeJSON(r.Body, &req); err != nil { 98 render.Status(r, http.StatusBadRequest) 99 render.JSON(w, r, ErrBadRequest) 100 return 101 } 102 103 force := r.URL.Query().Get("force") == "true" 104 var cfg *config.Config 105 var err error 106 107 if req.Payload != "" { 108 cfg, err = executor.ParseWithBytes([]byte(req.Payload)) 109 if err != nil { 110 render.Status(r, http.StatusBadRequest) 111 render.JSON(w, r, newError(err.Error())) 112 return 113 } 114 } else { 115 if !filepath.IsAbs(req.Path) { 116 render.Status(r, http.StatusBadRequest) 117 render.JSON(w, r, newError("path is not a absolute path")) 118 return 119 } 120 121 cfg, err = executor.ParseWithPath(req.Path) 122 if err != nil { 123 render.Status(r, http.StatusBadRequest) 124 render.JSON(w, r, newError(err.Error())) 125 return 126 } 127 } 128 129 executor.ApplyConfig(cfg, force) 130 render.NoContent(w, r) 131} 132