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 }