fix: geo auto update #1261

This commit is contained in:
Larvan2 2024-05-17 11:49:09 +08:00
parent fe88f0e437
commit 5c3a9b1dfc
9 changed files with 144 additions and 114 deletions

View file

@ -67,7 +67,7 @@ func (e *updateError) Error() string {
// Update performs the auto-updater. It returns an error if the updater failed.
// If firstRun is true, it assumes the configuration file doesn't exist.
func Update(execPath string) (err error) {
func UpdateCore(execPath string) (err error) {
mu.Lock()
defer mu.Unlock()

View file

@ -1,18 +1,29 @@
package config
package updater
import (
"errors"
"fmt"
"os"
"runtime"
"sync"
"time"
"github.com/metacubex/mihomo/common/atomic"
"github.com/metacubex/mihomo/component/geodata"
_ "github.com/metacubex/mihomo/component/geodata/standard"
"github.com/metacubex/mihomo/component/mmdb"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
"github.com/oschwald/maxminddb-golang"
)
func UpdateGeoDatabases() error {
var (
updateGeoMux sync.Mutex
UpdatingGeo atomic.Bool
)
func updateGeoDatabases() error {
defer runtime.GC()
geoLoader, err := geodata.GetGeoDataLoader("standard")
if err != nil {
@ -88,3 +99,82 @@ func UpdateGeoDatabases() error {
return nil
}
func UpdateGeoDatabases() error {
log.Infoln("[GEO] Start updating GEO database")
updateGeoMux.Lock()
if UpdatingGeo.Load() {
updateGeoMux.Unlock()
return errors.New("GEO database is updating, skip")
}
UpdatingGeo.Store(true)
updateGeoMux.Unlock()
defer func() {
UpdatingGeo.Store(false)
}()
log.Infoln("[GEO] Updating GEO database")
if err := updateGeoDatabases(); err != nil {
log.Errorln("[GEO] update GEO database error: %s", err.Error())
return err
}
return nil
}
func getUpdateTime() (err error, time time.Time) {
var fileInfo os.FileInfo
if C.GeodataMode {
fileInfo, err = os.Stat(C.Path.GeoIP())
if err != nil {
return err, time
}
} else {
fileInfo, err = os.Stat(C.Path.MMDB())
if err != nil {
return err, time
}
}
return nil, fileInfo.ModTime()
}
func RegisterGeoUpdater() {
if C.GeoUpdateInterval <= 0 {
log.Errorln("[GEO] Invalid update interval: %d", C.GeoUpdateInterval)
return
}
ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour)
defer ticker.Stop()
log.Infoln("[GEO] update GEO database every %d hours", C.GeoUpdateInterval)
go func() {
err, lastUpdate := getUpdateTime()
if err != nil {
log.Errorln("[GEO] Get GEO database update time error: %s", err.Error())
return
}
log.Infoln("[GEO] last update time %s", lastUpdate)
if lastUpdate.Add(time.Duration(C.GeoUpdateInterval) * time.Hour).Before(time.Now()) {
log.Infoln("[GEO] Database has not been updated for %v, update now", time.Duration(C.GeoUpdateInterval)*time.Hour)
if err := UpdateGeoDatabases(); err != nil {
log.Errorln("[GEO] Failed to update GEO database: %s", err.Error())
return
}
}
for range ticker.C {
if err := UpdateGeoDatabases(); err != nil {
log.Errorln("[GEO] Failed to update GEO database: %s", err.Error())
return
}
}
}()
}

View file

@ -1,4 +1,4 @@
package config
package updater
import (
"archive/zip"
@ -29,7 +29,7 @@ func UpdateUI() error {
xdMutex.Lock()
defer xdMutex.Unlock()
err := prepare()
err := prepare_ui()
if err != nil {
return err
}
@ -64,7 +64,7 @@ func UpdateUI() error {
return nil
}
func prepare() error {
func prepare_ui() error {
if ExternalUIPath == "" || ExternalUIURL == "" {
return ErrIncompleteConf
}

View file

@ -1,12 +1,35 @@
package updater
import (
"context"
"fmt"
"io"
"net/http"
"os"
"time"
mihomoHttp "github.com/metacubex/mihomo/component/http"
C "github.com/metacubex/mihomo/constant"
"golang.org/x/exp/constraints"
)
func downloadForBytes(url string) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
defer cancel()
resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
func saveFile(bytes []byte, path string) error {
return os.WriteFile(path, bytes, 0o644)
}
// LimitReachedError records the limit and the operation that caused it.
type LimitReachedError struct {
Limit int64

View file

@ -28,6 +28,7 @@ import (
SNIFF "github.com/metacubex/mihomo/component/sniffer"
tlsC "github.com/metacubex/mihomo/component/tls"
"github.com/metacubex/mihomo/component/trie"
"github.com/metacubex/mihomo/component/updater"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/constant/features"
providerTypes "github.com/metacubex/mihomo/constant/provider"
@ -640,28 +641,28 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second
}
ExternalUIPath = cfg.ExternalUI
updater.ExternalUIPath = cfg.ExternalUI
// checkout externalUI exist
if ExternalUIPath != "" {
ExternalUIPath = C.Path.Resolve(ExternalUIPath)
if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) {
if updater.ExternalUIPath != "" {
updater.ExternalUIPath = C.Path.Resolve(updater.ExternalUIPath)
if _, err := os.Stat(updater.ExternalUIPath); os.IsNotExist(err) {
defaultUIpath := path.Join(C.Path.HomeDir(), "ui")
log.Warnln("external-ui: %s does not exist, creating folder in %s", ExternalUIPath, defaultUIpath)
log.Warnln("external-ui: %s does not exist, creating folder in %s", updater.ExternalUIPath, defaultUIpath)
if err := os.MkdirAll(defaultUIpath, os.ModePerm); err != nil {
return nil, err
}
ExternalUIPath = defaultUIpath
updater.ExternalUIPath = defaultUIpath
cfg.ExternalUI = defaultUIpath
}
}
// checkout UIpath/name exist
if cfg.ExternalUIName != "" {
ExternalUIName = cfg.ExternalUIName
updater.ExternalUIName = cfg.ExternalUIName
} else {
ExternalUIFolder = ExternalUIPath
updater.ExternalUIFolder = updater.ExternalUIPath
}
if cfg.ExternalUIURL != "" {
ExternalUIURL = cfg.ExternalUIURL
updater.ExternalUIURL = cfg.ExternalUIURL
}
cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun

View file

@ -1,38 +1,15 @@
package config
import (
"context"
"fmt"
"io"
"net"
"net/http"
"net/netip"
"os"
"strings"
"time"
"github.com/metacubex/mihomo/adapter/outboundgroup"
"github.com/metacubex/mihomo/common/structure"
mihomoHttp "github.com/metacubex/mihomo/component/http"
C "github.com/metacubex/mihomo/constant"
)
func downloadForBytes(url string) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
defer cancel()
resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
func saveFile(bytes []byte, path string) error {
return os.WriteFile(path, bytes, 0o644)
}
func trimArr(arr []string) (r []string) {
for _, e := range arr {
r = append(r, strings.Trim(e, " "))

View file

@ -4,11 +4,11 @@ import (
"net/http"
"net/netip"
"path/filepath"
"sync"
"github.com/metacubex/mihomo/adapter/inbound"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/resolver"
"github.com/metacubex/mihomo/component/updater"
"github.com/metacubex/mihomo/config"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/hub/executor"
@ -21,11 +21,6 @@ import (
"github.com/go-chi/render"
)
var (
updateGeoMux sync.Mutex
updatingGeo = false
)
func configRouter() http.Handler {
r := chi.NewRouter()
r.Get("/", getConfigs)
@ -369,30 +364,20 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) {
}
func updateGeoDatabases(w http.ResponseWriter, r *http.Request) {
updateGeoMux.Lock()
if updatingGeo {
updateGeoMux.Unlock()
if updater.UpdatingGeo.Load() {
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, newError("updating..."))
return
}
updatingGeo = true
updateGeoMux.Unlock()
err := updater.UpdateGeoDatabases()
if err != nil {
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, newError(err.Error()))
return
}
go func() {
defer func() {
updatingGeo = false
}()
log.Warnln("[REST-API] updating GEO databases...")
if err := config.UpdateGeoDatabases(); err != nil {
log.Errorln("[REST-API] update GEO databases failed: %v", err)
return
}
cfg, err := executor.ParseWithPath(C.Path.Config())
if err != nil {
log.Errorln("[REST-API] update GEO databases failed: %v", err)

View file

@ -6,8 +6,7 @@ import (
"net/http"
"os"
"github.com/metacubex/mihomo/config"
"github.com/metacubex/mihomo/hub/updater"
"github.com/metacubex/mihomo/component/updater"
"github.com/metacubex/mihomo/log"
"github.com/go-chi/chi/v5"
@ -18,6 +17,7 @@ func upgradeRouter() http.Handler {
r := chi.NewRouter()
r.Post("/", upgradeCore)
r.Post("/ui", updateUI)
r.Post("/geo", updateGeoDatabases)
return r
}
@ -31,7 +31,7 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) {
return
}
err = updater.Update(execPath)
err = updater.UpdateCore(execPath)
if err != nil {
log.Warnln("%s", err)
render.Status(r, http.StatusInternalServerError)
@ -48,9 +48,9 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) {
}
func updateUI(w http.ResponseWriter, r *http.Request) {
err := config.UpdateUI()
err := updater.UpdateUI()
if err != nil {
if errors.Is(err, config.ErrIncompleteConf) {
if errors.Is(err, updater.ErrIncompleteConf) {
log.Warnln("%s", err)
render.Status(r, http.StatusNotImplemented)
render.JSON(w, r, newError(fmt.Sprintf("%s", err)))

50
main.go
View file

@ -8,10 +8,9 @@ import (
"path/filepath"
"runtime"
"strings"
"sync"
"syscall"
"time"
"github.com/metacubex/mihomo/component/updater"
"github.com/metacubex/mihomo/config"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/constant/features"
@ -32,8 +31,6 @@ var (
externalController string
externalControllerUnix string
secret string
updateGeoMux sync.Mutex
updatingGeo = false
)
func init() {
@ -116,14 +113,7 @@ func main() {
}
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()
}
}()
updater.RegisterGeoUpdater()
}
defer executor.Shutdown()
@ -145,39 +135,3 @@ 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)
}()
}