diff --git a/config/config.go b/config/config.go index 9cba5d09..45c4cfdb 100644 --- a/config/config.go +++ b/config/config.go @@ -55,6 +55,8 @@ type General struct { Interface string `json:"interface-name"` RoutingMark int `json:"-"` GeoXUrl GeoXUrl `json:"geox-url"` + GeoAutoUpdate bool `json:"geo-auto-update"` + GeoUpdateInterval int `json:"geo-update-interval"` GeodataMode bool `json:"geodata-mode"` GeodataLoader string `json:"geodata-loader"` TCPConcurrent bool `json:"tcp-concurrent"` @@ -298,6 +300,8 @@ type RawConfig struct { Interface string `yaml:"interface-name"` RoutingMark int `yaml:"routing-mark"` Tunnels []LC.Tunnel `yaml:"tunnels"` + GeoAutoUpdate bool `yaml:"geo-auto-update" json:"geo-auto-update"` + GeoUpdateInterval int `yaml:"geo-update-interval" json:"geo-update-interval"` GeodataMode bool `yaml:"geodata-mode" json:"geodata-mode"` GeodataLoader string `yaml:"geodata-loader" json:"geodata-loader"` TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"` @@ -377,22 +381,24 @@ func Parse(buf []byte) (*Config, error) { func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { // config with default value rawCfg := &RawConfig{ - AllowLan: false, - BindAddress: "*", - IPv6: true, - Mode: T.Rule, - GeodataMode: C.GeodataMode, - GeodataLoader: "memconservative", - UnifiedDelay: false, - Authentication: []string{}, - LogLevel: log.INFO, - Hosts: map[string]any{}, - Rule: []string{}, - Proxy: []map[string]any{}, - ProxyGroup: []map[string]any{}, - TCPConcurrent: false, - FindProcessMode: P.FindProcessStrict, - GlobalUA: "clash.meta", + AllowLan: false, + BindAddress: "*", + IPv6: true, + Mode: T.Rule, + GeoAutoUpdate: false, + GeoUpdateInterval: 24, + GeodataMode: C.GeodataMode, + GeodataLoader: "memconservative", + UnifiedDelay: false, + Authentication: []string{}, + LogLevel: log.INFO, + Hosts: map[string]any{}, + Rule: []string{}, + Proxy: []map[string]any{}, + ProxyGroup: []map[string]any{}, + TCPConcurrent: false, + FindProcessMode: P.FindProcessStrict, + GlobalUA: "clash.meta", Tun: RawTun{ Enable: false, Device: "", @@ -590,6 +596,8 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { func parseGeneral(cfg *RawConfig) (*General, error) { geodata.SetLoader(cfg.GeodataLoader) + C.GeoAutoUpdate = cfg.GeoAutoUpdate + C.GeoUpdateInterval = cfg.GeoUpdateInterval C.GeoIpUrl = cfg.GeoXUrl.GeoIp C.GeoSiteUrl = cfg.GeoXUrl.GeoSite C.MmdbUrl = cfg.GeoXUrl.Mmdb @@ -652,6 +660,8 @@ func parseGeneral(cfg *RawConfig) (*General, error) { Interface: cfg.Interface, RoutingMark: cfg.RoutingMark, GeoXUrl: cfg.GeoXUrl, + GeoAutoUpdate: cfg.GeoAutoUpdate, + GeoUpdateInterval: cfg.GeoUpdateInterval, GeodataMode: cfg.GeodataMode, GeodataLoader: cfg.GeodataLoader, TCPConcurrent: cfg.TCPConcurrent, diff --git a/config/update_geo.go b/config/update_geo.go index 718c2d07..2cde47a1 100644 --- a/config/update_geo.go +++ b/config/update_geo.go @@ -28,7 +28,7 @@ func UpdateGeoDatabases() error { return fmt.Errorf("invalid GeoIP database file: %s", err) } - if saveFile(data, C.Path.GeoIP()) != nil { + if err = saveFile(data, C.Path.GeoIP()); err != nil { return fmt.Errorf("can't save GeoIP database file: %w", err) } @@ -43,8 +43,7 @@ func UpdateGeoDatabases() error { return fmt.Errorf("invalid MMDB database file: %s", err) } _ = instance.Close() - - if saveFile(data, C.Path.MMDB()) != nil { + if err = saveFile(data, C.Path.MMDB()); err != nil { return fmt.Errorf("can't save MMDB database file: %w", err) } } @@ -58,7 +57,7 @@ func UpdateGeoDatabases() error { return fmt.Errorf("invalid GeoSite database file: %s", err) } - if saveFile(data, C.Path.GeoSite()) != nil { + if err = saveFile(data, C.Path.GeoSite()); err != nil { return fmt.Errorf("can't save GeoSite database file: %w", err) } diff --git a/config/update_ui.go b/config/update_ui.go index e5596597..cff1d6d7 100644 --- a/config/update_ui.go +++ b/config/update_ui.go @@ -40,7 +40,7 @@ func UpdateUI() error { } saved := path.Join(C.Path.HomeDir(), "download.zip") - if saveFile(data, saved) != nil { + if err = saveFile(data, saved); err != nil { return fmt.Errorf("can't save zip file: %w", err) } defer os.Remove(saved) diff --git a/constant/geodata.go b/constant/geodata.go index 72452270..e93d56b3 100644 --- a/constant/geodata.go +++ b/constant/geodata.go @@ -1,8 +1,10 @@ package constant var ( - GeodataMode bool - GeoIpUrl string - MmdbUrl string - GeoSiteUrl string + GeodataMode bool + GeoAutoUpdate bool + GeoUpdateInterval int + GeoIpUrl string + MmdbUrl string + GeoSiteUrl string ) diff --git a/docs/config.yaml b/docs/config.yaml index bdb822f7..b2524329 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -28,6 +28,9 @@ geox-url: geosite: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat" mmdb: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.metadb" +geo-auto-update: false # 是否自动更新 geodata +geo-update-interval: 24 # 更新间隔,单位:小时 + log-level: debug # 日志等级 silent/error/warning/info/debug ipv6: true # 开启 IPv6 总开关,关闭阻断所有 IPv6 链接和屏蔽 DNS 请求 AAAA 记录 diff --git a/main.go b/main.go index 01edee9f..425b4661 100644 --- a/main.go +++ b/main.go @@ -3,16 +3,18 @@ package main import ( "flag" "fmt" - "github.com/metacubex/mihomo/constant/features" "os" "os/signal" "path/filepath" "runtime" "strings" + "sync" "syscall" + "time" "github.com/metacubex/mihomo/config" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/features" "github.com/metacubex/mihomo/hub" "github.com/metacubex/mihomo/hub/executor" "github.com/metacubex/mihomo/log" @@ -29,6 +31,8 @@ var ( externalUI string externalController string secret string + updateGeoMux sync.Mutex + updatingGeo = false ) func init() { @@ -107,6 +111,17 @@ func main() { log.Fatalln("Parse config error: %s", err.Error()) } + if C.GeoAutoUpdate { + ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour) + + log.Infoln("[GEO] Start update GEO database every %d hours", C.GeoUpdateInterval) + go func() { + for range ticker.C { + updateGeoDatabases() + } + }() + } + defer executor.Shutdown() termSign := make(chan os.Signal, 1) @@ -126,3 +141,39 @@ func main() { } } } + +func updateGeoDatabases() { + log.Infoln("[GEO] Start updating GEO database") + updateGeoMux.Lock() + + if updatingGeo { + updateGeoMux.Unlock() + log.Infoln("[GEO] GEO database is updating, skip") + return + } + + updatingGeo = true + updateGeoMux.Unlock() + + go func() { + defer func() { + updatingGeo = false + }() + + log.Infoln("[GEO] Updating GEO database") + + if err := config.UpdateGeoDatabases(); err != nil { + log.Errorln("[GEO] update GEO database error: %s", err.Error()) + return + } + + cfg, err := executor.ParseWithPath(C.Path.Config()) + if err != nil { + log.Errorln("[GEO] update GEO database failed: %s", err.Error()) + return + } + + log.Infoln("[GEO] Update GEO database success, apply new config") + executor.ApplyConfig(cfg, false) + }() +}