ソースを参照

:art: 桌面端自动下载更新安装包 https://github.com/siyuan-note/siyuan/issues/5837

Liang Ding 2 年 前
コミット
e2a6780cec

+ 2 - 0
app/appearance/langs/en_US.json

@@ -1,4 +1,6 @@
 {
+  "autoDownloadUpdatePkg": "Automatically download update installation package",
+  "autoDownloadUpdatePkgTip": "After enabling, it will automatically check the version update every two hours. If there is an updated version, it will automatically download the installation package and prompt for installation",
   "downloaded": "Downloaded",
   "allOp": "All operations",
   "htmlBlockError": "The execution of the following script will affect the interface display, and the script has stopped running",

+ 2 - 0
app/appearance/langs/es_ES.json

@@ -1,4 +1,6 @@
 {
+  "autoDownloadUpdatePkg": "Descargar automáticamente el paquete de instalación de actualizaciones",
+  "autoDownloadUpdatePkgTip": "Después de abrir, verificará automáticamente la actualización de la versión cada dos horas. Si hay una versión actualizada, descargará automáticamente el paquete de instalación y solicitará la instalación",
   "downloaded": "Descargado",
   "allOp": "Todas las operaciones",
   "htmlBlockError": "La ejecución del siguiente script afectará la visualización de la interfaz y el script ha dejado de ejecutarse",

+ 2 - 0
app/appearance/langs/fr_FR.json

@@ -1,4 +1,6 @@
 {
+  "autoDownloadUpdatePkg": "Télécharger automatiquement le package d'installation de la mise à jour",
+  "autoDownloadUpdatePkgTip": "Après l'ouverture, il vérifiera automatiquement la mise à jour de la version toutes les deux heures. S'il existe une version mise à jour, il téléchargera automatiquement le package d'installation et demandera l'installation",
   "downloaded": "Téléchargé",
   "allOp": "Toutes les opérations",
   "htmlBlockError": "L'exécution du script suivant affectera l'affichage de l'interface et le script a cessé de s'exécuter",

+ 2 - 0
app/appearance/langs/zh_CHT.json

@@ -1,4 +1,6 @@
 {
+  "autoDownloadUpdatePkg": "自動下載更新安裝包",
+  "autoDownloadUpdatePkgTip": "開啟後會每隔兩小時自動檢查版本更新,如果有更新版本則自動下載安裝包並提示安裝",
   "downloaded": "已下載",
   "allOp": "所有操作",
   "htmlBlockError": "以下 script 執行會影響界面顯示,已經停止運行該腳本",

+ 2 - 0
app/appearance/langs/zh_CN.json

@@ -1,4 +1,6 @@
 {
+  "autoDownloadUpdatePkg": "自动下载更新安装包",
+  "autoDownloadUpdatePkgTip": "开启后会每隔两小时自动检查版本更新,如果有更新版本则自动下载安装包并提示安装",
   "downloaded": "已下载",
   "allOp": "所有操作",
   "htmlBlockError": "以下 script 执行会影响界面显示,已经停止运行该脚本",

+ 14 - 0
app/src/config/about.ts

@@ -125,6 +125,14 @@ export const about = {
         <svg><use xlink:href="#iconRefresh"></use></svg>${window.siyuan.languages.checkUpdate}
     </button>
 </div>
+<div class="fn__flex b3-label${isBrowser() ? " fn__none" : ""}">
+    <div class="fn__flex-1">
+        ${window.siyuan.languages.autoDownloadUpdatePkg}
+        <div class="b3-label__text">${window.siyuan.languages.autoDownloadUpdatePkgTip}</div>
+    </div>
+    <div class="fn__space"></div>
+    <input class="b3-switch fn__flex-center" id="downloadInstallPkg" type="checkbox"${window.siyuan.config.system.downloadInstallPkg ? " checked" : ""}>
+</div>
 <div class="fn__flex b3-label">
     <div class="fn__flex-1">
         ${window.siyuan.languages.systemLog}
@@ -339,6 +347,12 @@ export const about = {
                 });
             });
         });
+        const downloadInstallPkgElement = about.element.querySelector("#downloadInstallPkg") as HTMLInputElement;
+        downloadInstallPkgElement.addEventListener("change", () => {
+            fetchPost("/api/system/setDownloadInstallPkg", {downloadInstallPkg: downloadInstallPkgElement.checked}, () => {
+                window.siyuan.config.system.downloadInstallPkg = downloadInstallPkgElement.checked
+            });
+        });
         about.element.querySelector("#aboutConfim").addEventListener("click", () => {
             fetchPost("/api/system/setNetworkProxy", {
                 scheme: (about.element.querySelector("#aboutScheme") as HTMLInputElement).value,

+ 1 - 0
app/src/types/index.d.ts

@@ -304,6 +304,7 @@ declare interface IConfig {
         xanadu: boolean
         udanax: boolean
         uploadErrLog: boolean
+        downloadInstallPkg: boolean
         networkServe: boolean
         useExistingDB: boolean
     }

+ 1 - 0
kernel/api/router.go

@@ -40,6 +40,7 @@ func ServeAPI(ginServer *gin.Engine) {
 	ginServer.Handle("POST", "/api/system/setAccessAuthCode", model.CheckAuth, setAccessAuthCode)
 	ginServer.Handle("POST", "/api/system/setNetworkServe", model.CheckAuth, setNetworkServe)
 	ginServer.Handle("POST", "/api/system/setUploadErrLog", model.CheckAuth, setUploadErrLog)
+	ginServer.Handle("POST", "/api/system/setDownloadInstallPkg", model.CheckAuth, setDownloadInstallPkg)
 	ginServer.Handle("POST", "/api/system/setNetworkProxy", model.CheckAuth, setNetworkProxy)
 	ginServer.Handle("POST", "/api/system/setWorkspaceDir", model.CheckAuth, setWorkspaceDir)
 	ginServer.Handle("POST", "/api/system/listWorkspaceDirs", model.CheckAuth, listWorkspaceDirs)

+ 14 - 0
kernel/api/system.go

@@ -291,6 +291,20 @@ func setUploadErrLog(c *gin.Context) {
 	time.Sleep(time.Second * 3)
 }
 
+func setDownloadInstallPkg(c *gin.Context) {
+	ret := gulu.Ret.NewResult()
+	defer c.JSON(http.StatusOK, ret)
+
+	arg, ok := util.JsonArg(c, ret)
+	if !ok {
+		return
+	}
+
+	downloadInstallPkg := arg["downloadInstallPkg"].(bool)
+	model.Conf.System.DownloadInstallPkg = downloadInstallPkg
+	model.Conf.Save()
+}
+
 func setNetworkProxy(c *gin.Context) {
 	ret := gulu.Ret.NewResult()
 	defer c.JSON(http.StatusOK, ret)

+ 6 - 4
kernel/conf/system.go

@@ -36,14 +36,16 @@ type System struct {
 	NetworkServe bool          `json:"networkServe"`
 	NetworkProxy *NetworkProxy `json:"networkProxy"`
 
-	UploadErrLog bool `json:"uploadErrLog"`
+	UploadErrLog       bool `json:"uploadErrLog"`
+	DownloadInstallPkg bool `json:"downloadInstallPkg"`
 }
 
 func NewSystem() *System {
 	return &System{
-		ID:            util.GetDeviceID(),
-		KernelVersion: util.Ver,
-		NetworkProxy:  &NetworkProxy{},
+		ID:                 util.GetDeviceID(),
+		KernelVersion:      util.Ver,
+		NetworkProxy:       &NetworkProxy{},
+		DownloadInstallPkg: true,
 	}
 }
 

+ 1 - 1
kernel/main.go

@@ -46,7 +46,7 @@ func main() {
 	go model.HookResident()
 	util.SetBooted()
 	util.ClearPushProgress(100)
-	go model.AutoRefreshUser()
+	go model.AutoRefreshCheck()
 	go model.AutoFlushTx()
 	go sql.AutoFlushTreeQueue()
 	go treenode.AutoFlushBlockTree()

+ 1 - 1
kernel/mobile/kernel.go

@@ -59,7 +59,7 @@ func StartKernel(container, appDir, workspaceDir, nativeLibDir, privateDataDir,
 		go model.AutoStat()
 		util.SetBooted()
 		util.ClearPushProgress(100)
-		go model.AutoRefreshUser()
+		go model.AutoRefreshCheck()
 		go model.AutoFlushTx()
 		go sql.AutoFlushTreeQueue()
 		go treenode.AutoFlushBlockTree()

+ 8 - 3
kernel/model/liandi.go

@@ -144,11 +144,11 @@ func LoadUploadToken() (err error) {
 }
 
 var (
-	refreshUserTicker              = time.NewTicker(2 * time.Hour)
+	refreshCheckTicker             = time.NewTicker(2 * time.Hour)
 	subscriptionExpirationReminded bool
 )
 
-func AutoRefreshUser() {
+func AutoRefreshCheck() {
 	for {
 		if !subscriptionExpirationReminded {
 			subscriptionExpirationReminded = true
@@ -235,7 +235,12 @@ func AutoRefreshUser() {
 			}
 		}()
 
-		<-refreshUserTicker.C
+		go func() {
+			defer logging.Recover()
+			checkDownloadInstallPkg()
+		}()
+
+		<-refreshCheckTicker.C
 	}
 }
 

+ 99 - 7
kernel/model/updater.go

@@ -17,16 +17,111 @@
 package model
 
 import (
+	"bufio"
+	"crypto/sha256"
 	"fmt"
-	"sync"
+	"io"
+	"os"
+	"path/filepath"
+	"runtime"
 
+	"github.com/88250/gulu"
+	"github.com/imroc/req/v3"
 	"github.com/siyuan-note/logging"
 	"github.com/siyuan-note/siyuan/kernel/util"
 )
 
-var (
-	checkUpdateLock = &sync.Mutex{}
-)
+func checkDownloadInstallPkg() {
+	if !Conf.System.DownloadInstallPkg {
+		return
+	}
+
+	result, err := util.GetRhyResult(false)
+	if nil != err {
+		return
+	}
+
+	installPkgSite := result["installPkg"].(string)
+	ver := result["ver"].(string)
+	if ver == util.Ver {
+		return
+	}
+
+	var suffix string
+	if gulu.OS.IsWindows() {
+		if "386" == runtime.GOARCH {
+			suffix = "win32.exe"
+		} else {
+			suffix = "win.exe"
+		}
+	} else if gulu.OS.IsDarwin() {
+		if "arm64" == runtime.GOARCH {
+			suffix = "mac-arm64.dmg"
+		} else {
+			suffix = "mac.dmg"
+		}
+	} else if gulu.OS.IsLinux() {
+		suffix = "linux.AppImage"
+	}
+	pkg := "siyuan-" + ver + "-" + suffix
+	installPkg := "siyuan/" + pkg
+	downloadPkgURL := installPkgSite + installPkg
+	localPkgPath := filepath.Join(util.TempDir, "install", pkg)
+	checksums := result["checksums"].(map[string]interface{})
+	checksum := checksums[pkg].(string)
+
+	downloadInstallPkg(downloadPkgURL, checksum, localPkgPath)
+}
+
+func downloadInstallPkg(pkgURL, checksum, savePath string) {
+	if gulu.File.IsExist(savePath) {
+		localChecksum, _ := sha256Hash(savePath)
+		if localChecksum == checksum {
+			return
+		}
+	}
+
+	client := req.C()
+	callback := func(info req.DownloadInfo) {
+		logging.LogInfof("downloading install package from [%s] %.2f%%", pkgURL, float64(info.DownloadedSize)/float64(info.Response.ContentLength)*100.0)
+	}
+	resp, err := client.R().SetOutputFile(savePath).SetDownloadCallback(callback).Get(pkgURL)
+	if nil != err {
+		logging.LogErrorf("download install package failed: %s", err)
+		return
+	}
+	if 200 != resp.StatusCode {
+		logging.LogErrorf("download install package [%s] failed [sc=%d]", pkgURL, resp.StatusCode)
+		return
+	}
+	logging.LogInfof("downloaded install package [%s] to [%s]", pkgURL, savePath)
+	localChecksum, _ := sha256Hash(savePath)
+	if checksum != localChecksum {
+		logging.LogErrorf("verify checksum failed, download install package [%s] checksum [%s] not equal to downloaded [%s] checksum [%s]", pkgURL, checksum, savePath, localChecksum)
+	}
+}
+
+func sha256Hash(filename string) (ret string, err error) {
+	file, err := os.Open(filename)
+	if nil != err {
+		return
+	}
+	defer file.Close()
+
+	hash := sha256.New()
+	reader := bufio.NewReader(file)
+	buf := make([]byte, 1024*1024*4)
+	for {
+		switch n, readErr := reader.Read(buf); readErr {
+		case nil:
+			hash.Write(buf[:n])
+		case io.EOF:
+			return fmt.Sprintf("%x", hash.Sum(nil)), nil
+		default:
+			return "", err
+		}
+	}
+}
 
 type Announcement struct {
 	Id    string `json:"id"`
@@ -58,9 +153,6 @@ func CheckUpdate(showMsg bool) {
 		return
 	}
 
-	checkUpdateLock.Lock()
-	defer checkUpdateLock.Unlock()
-
 	result, err := util.GetRhyResult(showMsg)
 	if nil != err {
 		return