github.com/kelleygo/clashcore@v1.0.2/config/update_ui.go (about)

     1  package config
     2  
     3  import (
     4  	"archive/zip"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path"
    10  	"path/filepath"
    11  	"strings"
    12  	"sync"
    13  
    14  	C "github.com/kelleygo/clashcore/constant"
    15  )
    16  
    17  var (
    18  	ExternalUIURL    string
    19  	ExternalUIPath   string
    20  	ExternalUIFolder string
    21  	ExternalUIName   string
    22  )
    23  var (
    24  	ErrIncompleteConf = errors.New("ExternalUI configure incomplete")
    25  )
    26  var xdMutex sync.Mutex
    27  
    28  func UpdateUI() error {
    29  	xdMutex.Lock()
    30  	defer xdMutex.Unlock()
    31  
    32  	err := prepare()
    33  	if err != nil {
    34  		return err
    35  	}
    36  
    37  	data, err := downloadForBytes(ExternalUIURL)
    38  	if err != nil {
    39  		return fmt.Errorf("can't download  file: %w", err)
    40  	}
    41  
    42  	saved := path.Join(C.Path.HomeDir(), "download.zip")
    43  	if err = saveFile(data, saved); err != nil {
    44  		return fmt.Errorf("can't save zip file: %w", err)
    45  	}
    46  	defer os.Remove(saved)
    47  
    48  	err = cleanup(ExternalUIFolder)
    49  	if err != nil {
    50  		if !os.IsNotExist(err) {
    51  			return fmt.Errorf("cleanup exist file error: %w", err)
    52  		}
    53  	}
    54  
    55  	unzipFolder, err := unzip(saved, C.Path.HomeDir())
    56  	if err != nil {
    57  		return fmt.Errorf("can't extract zip file: %w", err)
    58  	}
    59  
    60  	err = os.Rename(unzipFolder, ExternalUIFolder)
    61  	if err != nil {
    62  		return fmt.Errorf("can't rename folder: %w", err)
    63  	}
    64  	return nil
    65  }
    66  
    67  func prepare() error {
    68  	if ExternalUIPath == "" || ExternalUIURL == "" {
    69  		return ErrIncompleteConf
    70  	}
    71  
    72  	if ExternalUIName != "" {
    73  		ExternalUIFolder = filepath.Clean(path.Join(ExternalUIPath, ExternalUIName))
    74  		if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) {
    75  			if err := os.MkdirAll(ExternalUIPath, os.ModePerm); err != nil {
    76  				return err
    77  			}
    78  		}
    79  	} else {
    80  		ExternalUIFolder = ExternalUIPath
    81  	}
    82  
    83  	return nil
    84  }
    85  
    86  func unzip(src, dest string) (string, error) {
    87  	r, err := zip.OpenReader(src)
    88  	if err != nil {
    89  		return "", err
    90  	}
    91  	defer r.Close()
    92  	var extractedFolder string
    93  	for _, f := range r.File {
    94  		fpath := filepath.Join(dest, f.Name)
    95  		if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
    96  			return "", fmt.Errorf("invalid file path: %s", fpath)
    97  		}
    98  		if f.FileInfo().IsDir() {
    99  			os.MkdirAll(fpath, os.ModePerm)
   100  			continue
   101  		}
   102  		if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
   103  			return "", err
   104  		}
   105  		outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
   106  		if err != nil {
   107  			return "", err
   108  		}
   109  		rc, err := f.Open()
   110  		if err != nil {
   111  			return "", err
   112  		}
   113  		_, err = io.Copy(outFile, rc)
   114  		outFile.Close()
   115  		rc.Close()
   116  		if err != nil {
   117  			return "", err
   118  		}
   119  		if extractedFolder == "" {
   120  			extractedFolder = filepath.Dir(fpath)
   121  		}
   122  	}
   123  	return extractedFolder, nil
   124  }
   125  
   126  func cleanup(root string) error {
   127  	if _, err := os.Stat(root); os.IsNotExist(err) {
   128  		return nil
   129  	}
   130  	return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
   131  		if err != nil {
   132  			return err
   133  		}
   134  		if info.IsDir() {
   135  			if err := os.RemoveAll(path); err != nil {
   136  				return err
   137  			}
   138  		} else {
   139  			if err := os.Remove(path); err != nil {
   140  				return err
   141  			}
   142  		}
   143  		return nil
   144  	})
   145  }