A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 364 lines 9.3 kB view raw
1// SiYuan - Refactor your thinking 2// Copyright (c) 2020-present, b3log.org 3// 4// This program is free software: you can redistribute it and/or modify 5// it under the terms of the GNU Affero General Public License as published by 6// the Free Software Foundation, either version 3 of the License, or 7// (at your option) any later version. 8// 9// This program is distributed in the hope that it will be useful, 10// but WITHOUT ANY WARRANTY; without even the implied warranty of 11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12// GNU Affero General Public License for more details. 13// 14// You should have received a copy of the GNU Affero General Public License 15// along with this program. If not, see <https://www.gnu.org/licenses/>. 16 17package model 18 19import ( 20 "bufio" 21 "crypto/sha256" 22 "fmt" 23 "io" 24 "os" 25 "os/exec" 26 "path" 27 "path/filepath" 28 "runtime" 29 "strconv" 30 "strings" 31 "sync" 32 "time" 33 34 "github.com/88250/gulu" 35 "github.com/imroc/req/v3" 36 "github.com/siyuan-note/logging" 37 "github.com/siyuan-note/siyuan/kernel/util" 38) 39 40func execNewVerInstallPkg(newVerInstallPkgPath string) { 41 logging.LogInfof("installing the new version [%s]", newVerInstallPkgPath) 42 var cmd *exec.Cmd 43 if gulu.OS.IsWindows() { 44 cmd = exec.Command(newVerInstallPkgPath) 45 } else if gulu.OS.IsDarwin() { 46 exec.Command("chmod", "+x", newVerInstallPkgPath).CombinedOutput() 47 cmd = exec.Command("open", newVerInstallPkgPath) 48 } 49 gulu.CmdAttr(cmd) 50 cmdErr := cmd.Run() 51 if nil != cmdErr { 52 logging.LogErrorf("exec install new version failed: %s", cmdErr) 53 return 54 } 55} 56 57var newVerInstallPkgPath string 58 59func getNewVerInstallPkgPath() string { 60 if skipNewVerInstallPkg() { 61 newVerInstallPkgPath = "" 62 return "" 63 } 64 65 downloadPkgURLs, checksum, err := getUpdatePkg() 66 if err != nil || 1 > len(downloadPkgURLs) || "" == checksum { 67 newVerInstallPkgPath = "" 68 return "" 69 } 70 71 pkg := path.Base(downloadPkgURLs[0]) 72 newVerInstallPkgPath = filepath.Join(util.TempDir, "install", pkg) 73 localChecksum, _ := sha256Hash(newVerInstallPkgPath) 74 if checksum != localChecksum { 75 newVerInstallPkgPath = "" 76 return "" 77 } 78 return newVerInstallPkgPath 79} 80 81var checkDownloadInstallPkgLock = sync.Mutex{} 82 83func checkDownloadInstallPkg() { 84 defer logging.Recover() 85 86 if skipNewVerInstallPkg() { 87 return 88 } 89 90 if !checkDownloadInstallPkgLock.TryLock() { 91 return 92 } 93 defer checkDownloadInstallPkgLock.Unlock() 94 95 downloadPkgURLs, checksum, err := getUpdatePkg() 96 if err != nil || 1 > len(downloadPkgURLs) || "" == checksum { 97 return 98 } 99 100 msgId := util.PushMsg(Conf.Language(103), 1000*7) 101 succ := false 102 for _, downloadPkgURL := range downloadPkgURLs { 103 err = downloadInstallPkg(downloadPkgURL, checksum) 104 if err == nil { 105 succ = true 106 break 107 108 } 109 } 110 if !succ { 111 util.PushUpdateMsg(msgId, Conf.Language(104), 7000) 112 } 113} 114 115func getUpdatePkg() (downloadPkgURLs []string, checksum string, err error) { 116 defer logging.Recover() 117 result, err := util.GetRhyResult(false) 118 if err != nil { 119 return 120 } 121 122 ver := result["ver"].(string) 123 if isVersionUpToDate(ver) { 124 return 125 } 126 127 var suffix string 128 if gulu.OS.IsWindows() { 129 if "arm64" == runtime.GOARCH { 130 suffix = "win-arm64.exe" 131 } else { 132 suffix = "win.exe" 133 } 134 } else if gulu.OS.IsDarwin() { 135 if "arm64" == runtime.GOARCH { 136 suffix = "mac-arm64.dmg" 137 } else { 138 suffix = "mac.dmg" 139 } 140 } 141 pkg := "siyuan-" + ver + "-" + suffix 142 143 b3logURL := "https://release.b3log.org/siyuan/" + pkg 144 liuyunURL := "https://release.liuyun.io/siyuan/" + pkg 145 githubURL := "https://github.com/siyuan-note/siyuan/releases/download/v" + ver + "/" + pkg 146 ghproxyURL := "https://ghfast.top/" + githubURL 147 if util.IsChinaCloud() { 148 downloadPkgURLs = append(downloadPkgURLs, b3logURL) 149 downloadPkgURLs = append(downloadPkgURLs, liuyunURL) 150 downloadPkgURLs = append(downloadPkgURLs, ghproxyURL) 151 downloadPkgURLs = append(downloadPkgURLs, githubURL) 152 } else { 153 downloadPkgURLs = append(downloadPkgURLs, b3logURL) 154 downloadPkgURLs = append(downloadPkgURLs, liuyunURL) 155 downloadPkgURLs = append(downloadPkgURLs, githubURL) 156 downloadPkgURLs = append(downloadPkgURLs, ghproxyURL) 157 } 158 159 checksums := result["checksums"].(map[string]interface{}) 160 checksum = checksums[pkg].(string) 161 return 162} 163 164func downloadInstallPkg(pkgURL, checksum string) (err error) { 165 if "" == pkgURL || "" == checksum { 166 return 167 } 168 169 pkg := path.Base(pkgURL) 170 savePath := filepath.Join(util.TempDir, "install", pkg) 171 if gulu.File.IsExist(savePath) { 172 localChecksum, _ := sha256Hash(savePath) 173 if localChecksum == checksum { 174 return 175 } 176 } 177 178 err = os.MkdirAll(filepath.Join(util.TempDir, "install"), 0755) 179 if err != nil { 180 logging.LogErrorf("create temp install dir failed: %s", err) 181 return 182 } 183 184 logging.LogInfof("downloading install package [%s]", pkgURL) 185 client := req.C().SetTLSHandshakeTimeout(7 * time.Second).SetTimeout(10 * time.Minute).DisableInsecureSkipVerify() 186 callback := func(info req.DownloadInfo) { 187 progress := fmt.Sprintf("%.2f%%", float64(info.DownloadedSize)/float64(info.Response.ContentLength)*100.0) 188 // logging.LogDebugf("downloading install package [%s %s]", pkgURL, progress) 189 util.PushStatusBar(fmt.Sprintf(Conf.Language(133), progress)) 190 } 191 _, err = client.R().SetOutputFile(savePath).SetDownloadCallbackWithInterval(callback, 1*time.Second).Get(pkgURL) 192 if err != nil { 193 logging.LogErrorf("download install package [%s] failed: %s", pkgURL, err) 194 return 195 } 196 197 localChecksum, _ := sha256Hash(savePath) 198 if checksum != localChecksum { 199 logging.LogErrorf("verify checksum failed, download install package [%s] checksum [%s] not equal to downloaded [%s] checksum [%s]", pkgURL, checksum, savePath, localChecksum) 200 return 201 } 202 logging.LogInfof("downloaded install package [%s] to [%s]", pkgURL, savePath) 203 util.PushStatusBar(Conf.Language(62)) 204 return 205} 206 207func sha256Hash(filename string) (ret string, err error) { 208 file, err := os.Open(filename) 209 if err != nil { 210 return 211 } 212 defer file.Close() 213 214 hash := sha256.New() 215 reader := bufio.NewReader(file) 216 buf := make([]byte, 1024*1024*4) 217 for { 218 switch n, readErr := reader.Read(buf); readErr { 219 case nil: 220 hash.Write(buf[:n]) 221 case io.EOF: 222 return fmt.Sprintf("%x", hash.Sum(nil)), nil 223 default: 224 return "", err 225 } 226 } 227} 228 229type Announcement struct { 230 Id string `json:"id"` 231 Title string `json:"title"` 232 URL string `json:"url"` 233 Region int `json:"region"` 234} 235 236func getAnnouncements() (ret []*Announcement) { 237 result, err := util.GetRhyResult(false) 238 if err != nil { 239 logging.LogErrorf("get announcement failed: %s", err) 240 return 241 } 242 243 if nil == result["announcement"] { 244 return 245 } 246 247 announcements := result["announcement"].([]interface{}) 248 for _, announcement := range announcements { 249 ann := announcement.(map[string]interface{}) 250 ret = append(ret, &Announcement{ 251 Id: ann["id"].(string), 252 Title: ann["title"].(string), 253 URL: ann["url"].(string), 254 Region: int(ann["region"].(float64)), 255 }) 256 } 257 return 258} 259 260func CheckUpdate(showMsg bool) { 261 if !showMsg { 262 return 263 } 264 265 if Conf.System.IsMicrosoftStore { 266 return 267 } 268 269 result, err := util.GetRhyResult(showMsg) 270 if err != nil { 271 return 272 } 273 274 ver := result["ver"].(string) 275 releaseLang := result["release"].(string) 276 if releaseLangArg := result["release_"+Conf.Lang]; nil != releaseLangArg { 277 releaseLang = releaseLangArg.(string) 278 } 279 280 var msg string 281 var timeout int 282 if isVersionUpToDate(ver) { 283 msg = Conf.Language(10) 284 timeout = 3000 285 } else { 286 msg = fmt.Sprintf(Conf.Language(9), "<a href=\""+releaseLang+"\">"+releaseLang+"</a>") 287 showMsg = true 288 timeout = 15000 289 } 290 if showMsg { 291 util.PushMsg(msg, timeout) 292 go func() { 293 defer logging.Recover() 294 checkDownloadInstallPkg() 295 if "" != getNewVerInstallPkgPath() { 296 util.PushMsg(Conf.Language(62), 15*1000) 297 } 298 }() 299 } 300} 301 302func isVersionUpToDate(releaseVer string) bool { 303 return ver2num(releaseVer) <= ver2num(util.Ver) 304} 305 306func skipNewVerInstallPkg() bool { 307 if !gulu.OS.IsWindows() && !gulu.OS.IsDarwin() { 308 return true 309 } 310 if util.ISMicrosoftStore || util.ContainerStd != util.Container { 311 return true 312 } 313 if !Conf.System.DownloadInstallPkg { 314 return true 315 } 316 if gulu.OS.IsWindows() { 317 plat := strings.ToLower(Conf.System.OSPlatform) 318 // Windows 7, 8 and Server 2012 are no longer supported https://github.com/siyuan-note/siyuan/issues/7347 319 if strings.Contains(plat, " 7 ") || strings.Contains(plat, " 8 ") || strings.Contains(plat, "2012") { 320 return true 321 } 322 } 323 return false 324} 325 326func ver2num(a string) int { 327 var version string 328 var suffixpos int 329 var suffixStr string 330 var suffix string 331 a = strings.Trim(a, " ") 332 if strings.Contains(a, "alpha") { 333 suffixpos = strings.Index(a, "-alpha") 334 version = a[0:suffixpos] 335 suffixStr = a[suffixpos+6 : len(a)] 336 suffix = "0" + fmt.Sprintf("%03s", suffixStr) 337 } else if strings.Contains(a, "beta") { 338 suffixpos = strings.Index(a, "-beta") 339 version = a[0:suffixpos] 340 suffixStr = a[suffixpos+5 : len(a)] 341 suffix = "1" + fmt.Sprintf("%03s", suffixStr) 342 } else { 343 version = a 344 suffix = "5000" 345 } 346 split := strings.Split(version, ".") 347 var verArr []string 348 349 verArr = append(verArr, "1") 350 var tmp string 351 for i := 0; i < 3; i++ { 352 if i < len(split) { 353 tmp = split[i] 354 } else { 355 tmp = "0" 356 } 357 verArr = append(verArr, fmt.Sprintf("%04s", tmp)) 358 } 359 verArr = append(verArr, suffix) 360 361 ver := strings.Join(verArr, "") 362 verNum, _ := strconv.Atoi(ver) 363 return verNum 364}