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

This commit is contained in:
Liang Ding 2022-09-07 22:43:14 +08:00
parent 9531bdd01c
commit e2a6780cec
No known key found for this signature in database
GPG key ID: 136F30F901A2231D
14 changed files with 155 additions and 16 deletions

View file

@ -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", "downloaded": "Downloaded",
"allOp": "All operations", "allOp": "All operations",
"htmlBlockError": "The execution of the following script will affect the interface display, and the script has stopped running", "htmlBlockError": "The execution of the following script will affect the interface display, and the script has stopped running",

View file

@ -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", "downloaded": "Descargado",
"allOp": "Todas las operaciones", "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", "htmlBlockError": "La ejecución del siguiente script afectará la visualización de la interfaz y el script ha dejado de ejecutarse",

View file

@ -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é", "downloaded": "Téléchargé",
"allOp": "Toutes les opérations", "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", "htmlBlockError": "L'exécution du script suivant affectera l'affichage de l'interface et le script a cessé de s'exécuter",

View file

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

View file

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

View file

@ -125,6 +125,14 @@ export const about = {
<svg><use xlink:href="#iconRefresh"></use></svg>${window.siyuan.languages.checkUpdate} <svg><use xlink:href="#iconRefresh"></use></svg>${window.siyuan.languages.checkUpdate}
</button> </button>
</div> </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 b3-label">
<div class="fn__flex-1"> <div class="fn__flex-1">
${window.siyuan.languages.systemLog} ${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", () => { about.element.querySelector("#aboutConfim").addEventListener("click", () => {
fetchPost("/api/system/setNetworkProxy", { fetchPost("/api/system/setNetworkProxy", {
scheme: (about.element.querySelector("#aboutScheme") as HTMLInputElement).value, scheme: (about.element.querySelector("#aboutScheme") as HTMLInputElement).value,

View file

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

View file

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

View file

@ -291,6 +291,20 @@ func setUploadErrLog(c *gin.Context) {
time.Sleep(time.Second * 3) 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) { func setNetworkProxy(c *gin.Context) {
ret := gulu.Ret.NewResult() ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret) defer c.JSON(http.StatusOK, ret)

View file

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

View file

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

View file

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

View file

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

View file

@ -17,16 +17,111 @@
package model package model
import ( import (
"bufio"
"crypto/sha256"
"fmt" "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/logging"
"github.com/siyuan-note/siyuan/kernel/util" "github.com/siyuan-note/siyuan/kernel/util"
) )
var ( func checkDownloadInstallPkg() {
checkUpdateLock = &sync.Mutex{} 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 { type Announcement struct {
Id string `json:"id"` Id string `json:"id"`
@ -58,9 +153,6 @@ func CheckUpdate(showMsg bool) {
return return
} }
checkUpdateLock.Lock()
defer checkUpdateLock.Unlock()
result, err := util.GetRhyResult(showMsg) result, err := util.GetRhyResult(showMsg)
if nil != err { if nil != err {
return return