2023-09-15 15:25:56 +00:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/zip"
|
2023-09-23 09:59:59 +00:00
|
|
|
"errors"
|
2023-09-15 15:25:56 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
2023-09-20 06:23:58 +00:00
|
|
|
|
2023-11-03 13:01:45 +00:00
|
|
|
C "github.com/metacubex/mihomo/constant"
|
2023-09-15 15:25:56 +00:00
|
|
|
)
|
|
|
|
|
2023-09-18 11:21:30 +00:00
|
|
|
var (
|
|
|
|
ExternalUIURL string
|
|
|
|
ExternalUIPath string
|
|
|
|
ExternalUIFolder string
|
|
|
|
ExternalUIName string
|
|
|
|
)
|
2023-09-23 09:59:59 +00:00
|
|
|
var (
|
2023-09-24 11:00:51 +00:00
|
|
|
ErrIncompleteConf = errors.New("ExternalUI configure incomplete")
|
2023-09-23 09:59:59 +00:00
|
|
|
)
|
2023-09-15 15:25:56 +00:00
|
|
|
var xdMutex sync.Mutex
|
|
|
|
|
2023-09-18 11:21:30 +00:00
|
|
|
func UpdateUI() error {
|
2023-09-15 15:25:56 +00:00
|
|
|
xdMutex.Lock()
|
|
|
|
defer xdMutex.Unlock()
|
|
|
|
|
2023-09-23 09:59:59 +00:00
|
|
|
err := prepare()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2023-09-18 11:21:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
data, err := downloadForBytes(ExternalUIURL)
|
2023-09-15 15:25:56 +00:00
|
|
|
if err != nil {
|
2023-09-18 11:21:30 +00:00
|
|
|
return fmt.Errorf("can't download file: %w", err)
|
2023-09-15 15:25:56 +00:00
|
|
|
}
|
|
|
|
|
2023-09-20 06:23:58 +00:00
|
|
|
saved := path.Join(C.Path.HomeDir(), "download.zip")
|
2023-12-03 15:10:09 +00:00
|
|
|
if err = saveFile(data, saved); err != nil {
|
2023-09-18 11:21:30 +00:00
|
|
|
return fmt.Errorf("can't save zip file: %w", err)
|
2023-09-15 15:25:56 +00:00
|
|
|
}
|
|
|
|
defer os.Remove(saved)
|
|
|
|
|
2023-09-20 06:23:58 +00:00
|
|
|
err = cleanup(ExternalUIFolder)
|
|
|
|
if err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return fmt.Errorf("cleanup exist file error: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unzipFolder, err := unzip(saved, C.Path.HomeDir())
|
2023-09-15 15:25:56 +00:00
|
|
|
if err != nil {
|
2023-09-18 11:21:30 +00:00
|
|
|
return fmt.Errorf("can't extract zip file: %w", err)
|
2023-09-15 15:25:56 +00:00
|
|
|
}
|
|
|
|
|
2023-09-18 11:21:30 +00:00
|
|
|
err = os.Rename(unzipFolder, ExternalUIFolder)
|
2023-09-15 15:25:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("can't rename folder: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-09-23 09:59:59 +00:00
|
|
|
func prepare() error {
|
|
|
|
if ExternalUIPath == "" || ExternalUIURL == "" {
|
2023-09-24 11:00:51 +00:00
|
|
|
return ErrIncompleteConf
|
2023-09-23 09:59:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ExternalUIName != "" {
|
|
|
|
ExternalUIFolder = filepath.Clean(path.Join(ExternalUIPath, ExternalUIName))
|
|
|
|
if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) {
|
|
|
|
if err := os.MkdirAll(ExternalUIPath, os.ModePerm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ExternalUIFolder = ExternalUIPath
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-09-18 11:21:30 +00:00
|
|
|
func unzip(src, dest string) (string, error) {
|
2023-09-15 15:25:56 +00:00
|
|
|
r, err := zip.OpenReader(src)
|
|
|
|
if err != nil {
|
2023-09-18 11:21:30 +00:00
|
|
|
return "", err
|
2023-09-15 15:25:56 +00:00
|
|
|
}
|
|
|
|
defer r.Close()
|
2023-09-18 11:21:30 +00:00
|
|
|
var extractedFolder string
|
2023-09-15 15:25:56 +00:00
|
|
|
for _, f := range r.File {
|
|
|
|
fpath := filepath.Join(dest, f.Name)
|
|
|
|
if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
|
2023-09-18 11:21:30 +00:00
|
|
|
return "", fmt.Errorf("invalid file path: %s", fpath)
|
2023-09-15 15:25:56 +00:00
|
|
|
}
|
|
|
|
if f.FileInfo().IsDir() {
|
|
|
|
os.MkdirAll(fpath, os.ModePerm)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
|
2023-09-18 11:21:30 +00:00
|
|
|
return "", err
|
2023-09-15 15:25:56 +00:00
|
|
|
}
|
|
|
|
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
|
|
|
if err != nil {
|
2023-09-18 11:21:30 +00:00
|
|
|
return "", err
|
2023-09-15 15:25:56 +00:00
|
|
|
}
|
|
|
|
rc, err := f.Open()
|
|
|
|
if err != nil {
|
2023-09-18 11:21:30 +00:00
|
|
|
return "", err
|
2023-09-15 15:25:56 +00:00
|
|
|
}
|
|
|
|
_, err = io.Copy(outFile, rc)
|
|
|
|
outFile.Close()
|
|
|
|
rc.Close()
|
|
|
|
if err != nil {
|
2023-09-18 11:21:30 +00:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if extractedFolder == "" {
|
|
|
|
extractedFolder = filepath.Dir(fpath)
|
2023-09-15 15:25:56 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-18 11:21:30 +00:00
|
|
|
return extractedFolder, nil
|
2023-09-15 15:25:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func cleanup(root string) error {
|
2023-09-18 11:21:30 +00:00
|
|
|
if _, err := os.Stat(root); os.IsNotExist(err) {
|
|
|
|
return nil
|
|
|
|
}
|
2023-09-15 15:25:56 +00:00
|
|
|
return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if info.IsDir() {
|
|
|
|
if err := os.RemoveAll(path); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := os.Remove(path); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|