update_ui.go 2.9 KB

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