A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

132 lines
3.6 KiB

  1. /*
  2. * Copyright © 2019-2020 A Bunch Tell LLC.
  3. *
  4. * This file is part of WriteFreely.
  5. *
  6. * WriteFreely is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License, included
  8. * in the LICENSE file in this source code package.
  9. */
  10. package writefreely
  11. import (
  12. "github.com/writeas/web-core/log"
  13. "io/ioutil"
  14. "net/http"
  15. "strings"
  16. "sync"
  17. "time"
  18. )
  19. // updatesCacheTime is the default interval between cache updates for new
  20. // software versions
  21. const defaultUpdatesCacheTime = 12 * time.Hour
  22. // updatesCache holds data about current and new releases of the writefreely
  23. // software
  24. type updatesCache struct {
  25. mu sync.Mutex
  26. frequency time.Duration
  27. lastCheck time.Time
  28. latestVersion string
  29. currentVersion string
  30. checkError error
  31. }
  32. // CheckNow asks for the latest released version of writefreely and updates
  33. // the cache last checked time. If the version postdates the current 'latest'
  34. // the version value is replaced.
  35. func (uc *updatesCache) CheckNow() error {
  36. if debugging {
  37. log.Info("[update check] Checking for update now.")
  38. }
  39. uc.mu.Lock()
  40. defer uc.mu.Unlock()
  41. uc.lastCheck = time.Now()
  42. latestRemote, err := newVersionCheck()
  43. if err != nil {
  44. log.Error("[update check] Failed: %v", err)
  45. uc.checkError = err
  46. return err
  47. }
  48. if CompareSemver(latestRemote, uc.latestVersion) == 1 {
  49. uc.latestVersion = latestRemote
  50. }
  51. return nil
  52. }
  53. // AreAvailable updates the cache if the frequency duration has passed
  54. // then returns if the latest release is newer than the current running version.
  55. func (uc updatesCache) AreAvailable() bool {
  56. if time.Since(uc.lastCheck) > uc.frequency {
  57. uc.CheckNow()
  58. }
  59. return CompareSemver(uc.latestVersion, uc.currentVersion) == 1
  60. }
  61. // AreAvailableNoCheck returns if the latest release is newer than the current
  62. // running version.
  63. func (uc updatesCache) AreAvailableNoCheck() bool {
  64. return CompareSemver(uc.latestVersion, uc.currentVersion) == 1
  65. }
  66. // LatestVersion returns the latest stored version available.
  67. func (uc updatesCache) LatestVersion() string {
  68. return uc.latestVersion
  69. }
  70. func (uc updatesCache) ReleaseURL() string {
  71. return "https://writefreely.org/releases/" + uc.latestVersion
  72. }
  73. // ReleaseNotesURL returns the full URL to the blog.writefreely.org release notes
  74. // for the latest version as stored in the cache.
  75. func (uc updatesCache) ReleaseNotesURL() string {
  76. return wfReleaseNotesURL(uc.latestVersion)
  77. }
  78. func wfReleaseNotesURL(v string) string {
  79. ver := strings.TrimPrefix(v, "v")
  80. ver = strings.TrimSuffix(ver, ".0")
  81. // hack until go 1.12 in build/travis
  82. seg := strings.Split(ver, ".")
  83. return "https://blog.writefreely.org/version-" + strings.Join(seg, "-")
  84. }
  85. // newUpdatesCache returns an initialized updates cache
  86. func newUpdatesCache(expiry time.Duration) *updatesCache {
  87. cache := updatesCache{
  88. frequency: expiry,
  89. currentVersion: "v" + softwareVer,
  90. }
  91. go cache.CheckNow()
  92. return &cache
  93. }
  94. // InitUpdates initializes the updates cache, if the config value is set
  95. // It uses the defaultUpdatesCacheTime for the cache expiry
  96. func (app *App) InitUpdates() {
  97. if app.cfg.App.UpdateChecks {
  98. app.updates = newUpdatesCache(defaultUpdatesCacheTime)
  99. }
  100. }
  101. func newVersionCheck() (string, error) {
  102. res, err := http.Get("https://version.writefreely.org")
  103. if debugging {
  104. log.Info("[update check] GET https://version.writefreely.org")
  105. }
  106. // TODO: return error if statusCode != OK
  107. if err == nil && res.StatusCode == http.StatusOK {
  108. defer res.Body.Close()
  109. body, err := ioutil.ReadAll(res.Body)
  110. if err != nil {
  111. return "", err
  112. }
  113. return string(body), nil
  114. }
  115. return "", err
  116. }