github.com/yaling888/clash@v1.53.0/hub/route/configs.go (about) 1 package route 2 3 import ( 4 "encoding/json" 5 "errors" 6 "net/http" 7 "path/filepath" 8 9 "github.com/go-chi/chi/v5" 10 "github.com/go-chi/render" 11 "github.com/phuslu/log" 12 "github.com/samber/lo" 13 14 "github.com/yaling888/clash/component/resolver" 15 "github.com/yaling888/clash/config" 16 C "github.com/yaling888/clash/constant" 17 "github.com/yaling888/clash/hub/executor" 18 "github.com/yaling888/clash/listener" 19 L "github.com/yaling888/clash/log" 20 "github.com/yaling888/clash/tunnel" 21 ) 22 23 func configRouter() http.Handler { 24 r := chi.NewRouter() 25 r.Get("/", getConfigs) 26 r.Put("/", updateConfigs) 27 r.Patch("/", patchConfigs) 28 return r 29 } 30 31 func getConfigs(w http.ResponseWriter, r *http.Request) { 32 general := executor.GetGeneral() 33 render.JSON(w, r, general) 34 } 35 36 func patchConfigs(w http.ResponseWriter, r *http.Request) { 37 type tun struct { 38 Enable *bool `json:"enable,omitempty"` 39 Device *string `json:"device,omitempty"` 40 Stack *C.TUNStack `json:"stack,omitempty"` 41 DNSHijack *[]C.DNSUrl `json:"dns-hijack,omitempty"` 42 AutoRoute *bool `json:"auto-route,omitempty"` 43 AutoDetectInterface *bool `json:"auto-detect-interface,omitempty"` 44 } 45 general := struct { 46 Port *int `json:"port,omitempty"` 47 SocksPort *int `json:"socks-port,omitempty"` 48 RedirPort *int `json:"redir-port,omitempty"` 49 TProxyPort *int `json:"tproxy-port,omitempty"` 50 MixedPort *int `json:"mixed-port,omitempty"` 51 MitmPort *int `json:"mitm-port,omitempty"` 52 AllowLan *bool `json:"allow-lan,omitempty"` 53 BindAddress *string `json:"bind-address,omitempty"` 54 Mode *tunnel.TunnelMode `json:"mode,omitempty"` 55 LogLevel *L.LogLevel `json:"log-level,omitempty"` 56 IPv6 *bool `json:"ipv6,omitempty"` 57 Sniffing *bool `json:"sniffing,omitempty"` 58 Tun *tun `json:"tun,omitempty"` 59 }{} 60 61 if err := render.DecodeJSON(r.Body, &general); err != nil { 62 render.Status(r, http.StatusBadRequest) 63 render.JSON(w, r, ErrBadRequest) 64 return 65 } 66 67 if general.AllowLan != nil { 68 listener.SetAllowLan(*general.AllowLan) 69 } 70 71 if general.BindAddress != nil { 72 listener.SetBindAddress(*general.BindAddress) 73 } 74 75 if general.Mode != nil { 76 tunnel.SetMode(*general.Mode) 77 } 78 79 if general.LogLevel != nil { 80 L.SetLevel(*general.LogLevel) 81 } 82 83 if general.IPv6 != nil { 84 resolver.SetDisableIPv6(!*general.IPv6) 85 } 86 87 if general.Sniffing != nil { 88 tunnel.SetSniffing(*general.Sniffing) 89 } 90 91 tcpIn := tunnel.TCPIn() 92 udpIn := tunnel.UDPIn() 93 94 ports := listener.GetPorts() 95 ports.Port = lo.FromPtrOr(general.Port, ports.Port) 96 ports.SocksPort = lo.FromPtrOr(general.SocksPort, ports.SocksPort) 97 ports.RedirPort = lo.FromPtrOr(general.RedirPort, ports.RedirPort) 98 ports.TProxyPort = lo.FromPtrOr(general.TProxyPort, ports.TProxyPort) 99 ports.MixedPort = lo.FromPtrOr(general.MixedPort, ports.MixedPort) 100 ports.MitmPort = lo.FromPtrOr(general.MitmPort, ports.MitmPort) 101 102 listener.ReCreatePortsListeners(*ports, tcpIn, udpIn) 103 104 if general.Tun != nil { 105 tunSchema := general.Tun 106 tunConf := C.GetTunConf() 107 tunConf.StopRouteListener = true 108 109 tunConf.Enable = lo.FromPtrOr(tunSchema.Enable, tunConf.Enable) 110 tunConf.Device = lo.FromPtrOr(tunSchema.Device, tunConf.Device) 111 tunConf.Stack = lo.FromPtrOr(tunSchema.Stack, tunConf.Stack) 112 tunConf.DNSHijack = lo.FromPtrOr(tunSchema.DNSHijack, tunConf.DNSHijack) 113 tunConf.AutoRoute = lo.FromPtrOr(tunSchema.AutoRoute, tunConf.AutoRoute) 114 tunConf.AutoDetectInterface = lo.FromPtrOr(tunSchema.AutoDetectInterface, tunConf.AutoDetectInterface) 115 116 listener.ReCreateTun(&tunConf, tcpIn, udpIn) 117 listener.ReCreateRedirToTun(tunConf.RedirectToTun) 118 } 119 120 msg, _ := json.Marshal(general) 121 log.Warn().Str("data", string(msg)).Msg("[API] patch config") 122 123 render.NoContent(w, r) 124 } 125 126 func updateConfigs(w http.ResponseWriter, r *http.Request) { 127 req := struct { 128 Path string `json:"path"` 129 Payload string `json:"payload"` 130 }{} 131 if err := render.DecodeJSON(r.Body, &req); err != nil { 132 render.Status(r, http.StatusBadRequest) 133 render.JSON(w, r, ErrBadRequest) 134 return 135 } 136 137 var ( 138 cfg *config.Config 139 err error 140 hasPath bool 141 oldLevel = L.Level() 142 force = r.URL.Query().Get("force") == "true" 143 ) 144 145 defer func() { 146 if err == nil { 147 oldLevel = L.Level() 148 L.SetLevel(L.INFO) 149 if req.Payload != "" { 150 log.Info().Msg("[API] payload config updated") 151 } else { 152 if hasPath { 153 C.SetConfig(req.Path) 154 } 155 log.Info().Str("path", req.Path).Msg("[API] configuration file reloaded") 156 } 157 } 158 L.SetLevel(oldLevel) 159 }() 160 161 L.SetLevel(L.INFO) 162 163 if req.Payload != "" { 164 log.Info().Msg("[API] payload config updating...") 165 166 cfg, err = executor.ParseWithBytes([]byte(req.Payload)) 167 if err != nil { 168 log.Error().Err(err).Msg("[API] update config failed") 169 170 render.Status(r, http.StatusBadRequest) 171 render.JSON(w, r, newError(err.Error())) 172 return 173 } 174 } else { 175 if hasPath = req.Path != ""; !hasPath { 176 req.Path = C.Path.Config() 177 } 178 179 log.Info().Str("path", req.Path).Msg("[API] configuration file reloading...") 180 181 if !filepath.IsAbs(req.Path) { 182 err = errors.New("path is not a absolute path") 183 log.Error(). 184 Err(err). 185 Str("path", req.Path). 186 Msg("[API] reload config failed") 187 188 render.Status(r, http.StatusBadRequest) 189 render.JSON(w, r, newError(err.Error())) 190 return 191 } 192 193 cfg, err = executor.ParseWithPath(req.Path) 194 if err != nil { 195 log.Error(). 196 Err(err). 197 Str("path", req.Path). 198 Msg("[API] reload config failed") 199 200 render.Status(r, http.StatusBadRequest) 201 render.JSON(w, r, newError(err.Error())) 202 return 203 } 204 } 205 206 executor.ApplyConfig(cfg, force) 207 render.NoContent(w, r) 208 }