package main import ( "context" "core/state" "encoding/json" "fmt" "github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter/outboundgroup" "github.com/metacubex/mihomo/common/observable" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/mmdb" "github.com/metacubex/mihomo/component/updater" "github.com/metacubex/mihomo/config" "github.com/metacubex/mihomo/constant" cp "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/hub/executor" "github.com/metacubex/mihomo/listener" "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/tunnel" "github.com/metacubex/mihomo/tunnel/statistic" "net" "runtime" "sort" "strconv" "time" ) var ( isInit = false configParams = ConfigExtendedParams{} externalProviders = map[string]cp.Provider{} logSubscriber observable.Subscription[log.Event] currentConfig *config.Config ) func handleInitClash(paramsString string) bool { var params = InitParams{} err := json.Unmarshal([]byte(paramsString), ¶ms) if err != nil { return false } version = params.Version if !isInit { constant.SetHomeDir(params.HomeDir) isInit = true } return isInit } func handleStartListener() bool { runLock.Lock() defer runLock.Unlock() isRunning = true updateListeners(true) return true } func handleStopListener() bool { runLock.Lock() defer runLock.Unlock() isRunning = false listener.StopListener() return true } func handleGetIsInit() bool { return isInit } func handleForceGc() { go func() { log.Infoln("[APP] request force GC") runtime.GC() }() } func handleShutdown() bool { stopListeners() executor.Shutdown() runtime.GC() isInit = false return true } func handleValidateConfig(bytes []byte) string { _, err := config.UnmarshalRawConfig(bytes) if err != nil { return err.Error() } return "" } func handleUpdateConfig(bytes []byte) string { var params = &GenerateConfigParams{} err := json.Unmarshal(bytes, params) if err != nil { return err.Error() } configParams = params.Params prof := decorationConfig(params.ProfileId, params.Config) err = applyConfig(prof) if err != nil { return err.Error() } return "" } func handleGetProxies() string { runLock.Lock() defer runLock.Unlock() data, err := json.Marshal(tunnel.ProxiesWithProviders()) if err != nil { return "" } return string(data) } func handleChangeProxy(data string, fn func(string string)) { runLock.Lock() go func() { defer runLock.Unlock() var params = &ChangeProxyParams{} err := json.Unmarshal([]byte(data), params) if err != nil { fn(err.Error()) return } groupName := *params.GroupName proxyName := *params.ProxyName proxies := tunnel.ProxiesWithProviders() group, ok := proxies[groupName] if !ok { fn("Not found group") return } adapterProxy := group.(*adapter.Proxy) selector, ok := adapterProxy.ProxyAdapter.(outboundgroup.SelectAble) if !ok { fn("Group is not selectable") return } if proxyName == "" { selector.ForceSet(proxyName) } else { err = selector.Set(proxyName) } if err != nil { fn(err.Error()) return } fn("") return }() } func handleGetTraffic() string { up, down := statistic.DefaultManager.Current(state.CurrentState.OnlyStatisticsProxy) traffic := map[string]int64{ "up": up, "down": down, } data, err := json.Marshal(traffic) if err != nil { fmt.Println("Error:", err) return "" } return string(data) } func handleGetTotalTraffic() string { up, down := statistic.DefaultManager.Total(state.CurrentState.OnlyStatisticsProxy) traffic := map[string]int64{ "up": up, "down": down, } data, err := json.Marshal(traffic) if err != nil { fmt.Println("Error:", err) return "" } return string(data) } func handleGetProfile(profileId string) string { prof := getRawConfigWithId(profileId) data, err := json.Marshal(prof) if err != nil { return "" } return string(data) } func handleResetTraffic() { statistic.DefaultManager.ResetStatistic() } func handleAsyncTestDelay(paramsString string, fn func(string)) { b.Go(paramsString, func() (bool, error) { var params = &TestDelayParams{} err := json.Unmarshal([]byte(paramsString), params) if err != nil { fn("") return false, nil } expectedStatus, err := utils.NewUnsignedRanges[uint16]("") if err != nil { fn("") return false, nil } ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(params.Timeout)) defer cancel() proxies := tunnel.ProxiesWithProviders() proxy := proxies[params.ProxyName] delayData := &Delay{ Name: params.ProxyName, } if proxy == nil { delayData.Value = -1 data, _ := json.Marshal(delayData) fn(string(data)) return false, nil } testUrl := constant.DefaultTestURL if params.TestUrl != "" { testUrl = params.TestUrl } delayData.Url = testUrl delay, err := proxy.URLTest(ctx, testUrl, expectedStatus) if err != nil || delay == 0 { delayData.Value = -1 data, _ := json.Marshal(delayData) fn(string(data)) return false, nil } delayData.Value = int32(delay) data, _ := json.Marshal(delayData) fn(string(data)) return false, nil }) } func handleGetConnections() string { runLock.Lock() defer runLock.Unlock() snapshot := statistic.DefaultManager.Snapshot() data, err := json.Marshal(snapshot) if err != nil { fmt.Println("Error:", err) return "" } return string(data) } func handleCloseConnections() bool { runLock.Lock() defer runLock.Unlock() statistic.DefaultManager.Range(func(c statistic.Tracker) bool { err := c.Close() if err != nil { return false } return true }) return true } func handleCloseConnection(connectionId string) bool { runLock.Lock() defer runLock.Unlock() c := statistic.DefaultManager.Get(connectionId) if c == nil { return false } _ = c.Close() return true } func handleGetExternalProviders() string { runLock.Lock() defer runLock.Unlock() externalProviders = getExternalProvidersRaw() eps := make([]ExternalProvider, 0) for _, p := range externalProviders { externalProvider, err := toExternalProvider(p) if err != nil { continue } eps = append(eps, *externalProvider) } sort.Sort(ExternalProviders(eps)) data, err := json.Marshal(eps) if err != nil { return "" } return string(data) } func handleGetExternalProvider(externalProviderName string) string { runLock.Lock() defer runLock.Unlock() externalProvider, exist := externalProviders[externalProviderName] if !exist { return "" } e, err := toExternalProvider(externalProvider) if err != nil { return "" } data, err := json.Marshal(e) if err != nil { return "" } return string(data) } func handleUpdateGeoData(geoType string, geoName string, fn func(value string)) { go func() { path := constant.Path.Resolve(geoName) switch geoType { case "MMDB": err := updater.UpdateMMDBWithPath(path) if err != nil { fn(err.Error()) return } case "ASN": err := updater.UpdateASNWithPath(path) if err != nil { fn(err.Error()) return } case "GeoIp": err := updater.UpdateGeoIpWithPath(path) if err != nil { fn(err.Error()) return } case "GeoSite": err := updater.UpdateGeoSiteWithPath(path) if err != nil { fn(err.Error()) return } } fn("") }() } func handleUpdateExternalProvider(providerName string, fn func(value string)) { go func() { externalProvider, exist := externalProviders[providerName] if !exist { fn("external provider is not exist") return } err := externalProvider.Update() if err != nil { fn(err.Error()) return } fn("") }() } func handleSideLoadExternalProvider(providerName string, data []byte, fn func(value string)) { go func() { runLock.Lock() defer runLock.Unlock() externalProvider, exist := externalProviders[providerName] if !exist { fn("external provider is not exist") return } err := sideUpdateExternalProvider(externalProvider, data) if err != nil { fn(err.Error()) return } fn("") }() } func handleStartLog() { if logSubscriber != nil { log.UnSubscribe(logSubscriber) logSubscriber = nil } logSubscriber = log.Subscribe() go func() { for logData := range logSubscriber { if logData.LogLevel < log.Level() { continue } message := &Message{ Type: LogMessage, Data: logData, } sendMessage(*message) } }() } func handleStopLog() { if logSubscriber != nil { log.UnSubscribe(logSubscriber) logSubscriber = nil } } func handleGetCountryCode(ip string, fn func(value string)) { go func() { runLock.Lock() defer runLock.Unlock() codes := mmdb.IPInstance().LookupCode(net.ParseIP(ip)) if len(codes) == 0 { fn("") return } fn(codes[0]) }() } func handleGetMemory(fn func(value string)) { go func() { fn(strconv.FormatUint(statistic.DefaultManager.Memory(), 10)) }() } func handleSetState(params string) { _ = json.Unmarshal([]byte(params), state.CurrentState) } func init() { adapter.UrlTestHook = func(url string, name string, delay uint16) { delayData := &Delay{ Url: url, Name: name, } if delay == 0 { delayData.Value = -1 } else { delayData.Value = int32(delay) } sendMessage(Message{ Type: DelayMessage, Data: delayData, }) } statistic.DefaultRequestNotify = func(c statistic.Tracker) { sendMessage(Message{ Type: RequestMessage, Data: c, }) } executor.DefaultProviderLoadedHook = func(providerName string) { sendMessage(Message{ Type: LoadedMessage, Data: providerName, }) } }