updateCheck.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. package updatecheck
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. config "github.com/OliveTin/OliveTin/internal/config"
  7. "github.com/google/uuid"
  8. "github.com/robfig/cron/v3"
  9. log "github.com/sirupsen/logrus"
  10. "io/ioutil"
  11. "net/http"
  12. "os"
  13. "runtime"
  14. )
  15. type updateRequest struct {
  16. CurrentVersion string
  17. CurrentCommit string
  18. OS string
  19. Arch string
  20. InstallationID string
  21. InContainer bool
  22. }
  23. // AvailableVersion is updated when checking with the update service.
  24. var AvailableVersion = "none"
  25. // CurrentVersion is set by the main cmd (which is in tern set as a compile constant)
  26. var CurrentVersion = "?"
  27. func installationID(filename string) string {
  28. var content string
  29. contentBytes, err := ioutil.ReadFile(filename)
  30. if err != nil {
  31. fileHandle, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
  32. if err != nil {
  33. log.Warnf("Could not read + create installation ID file: %v", err)
  34. return "cant-create"
  35. }
  36. content = uuid.NewString()
  37. fileHandle.WriteString(content)
  38. fileHandle.Close()
  39. } else {
  40. content = string(contentBytes)
  41. _, err := uuid.Parse(content)
  42. if err != nil {
  43. log.Errorf("Invalid installation ID, %v", err)
  44. content = "invalid-installation-id"
  45. }
  46. }
  47. log.WithFields(log.Fields{
  48. "content": content,
  49. "from": filename,
  50. }).Infof("Installation ID")
  51. return content
  52. }
  53. func isInContainer() bool {
  54. if _, err := os.Stat("/.dockerenv"); errors.Is(err, os.ErrNotExist) {
  55. return false
  56. }
  57. return true
  58. }
  59. // StartUpdateChecker will start a job that runs periodically, checking
  60. // for updates.
  61. func StartUpdateChecker(currentVersion string, currentCommit string, cfg *config.Config, configDir string) {
  62. CurrentVersion = currentVersion
  63. if !cfg.CheckForUpdates {
  64. log.Warn("Update checking is disabled")
  65. return
  66. }
  67. payload := updateRequest{
  68. CurrentVersion: currentVersion,
  69. CurrentCommit: currentCommit,
  70. OS: runtime.GOOS,
  71. Arch: runtime.GOARCH,
  72. InstallationID: installationID(configDir + "/installation-id.txt"),
  73. InContainer: isInContainer(),
  74. }
  75. s := cron.New(cron.WithSeconds())
  76. // Several values have been tried here.
  77. // 1st: Every 24h - very spammy.
  78. // 2nd: Every 7d - (168 hours - much more reasonable, but it checks in at the same time/day each week.
  79. // Current: Every 100h is not so spammy, and has the advantage that the checkin time "shifts" hours.
  80. s.AddFunc("@every 100h", func() {
  81. actualCheckForUpdate(payload)
  82. })
  83. go actualCheckForUpdate(payload) // On startup
  84. go s.Start()
  85. }
  86. func doRequest(jsonUpdateRequest []byte) string {
  87. req, err := http.NewRequest("POST", "http://update-check.olivetin.app", bytes.NewBuffer(jsonUpdateRequest))
  88. if err != nil {
  89. log.Errorf("Update check failed %v", err)
  90. return ""
  91. }
  92. req.Header.Set("Content-Type", "application/json")
  93. resp, err := http.DefaultClient.Do(req)
  94. if err != nil {
  95. log.Errorf("Update check failed %v", err)
  96. return ""
  97. }
  98. newVersion, _ := ioutil.ReadAll(resp.Body)
  99. defer resp.Body.Close()
  100. return string(newVersion)
  101. }
  102. func actualCheckForUpdate(payload updateRequest) {
  103. jsonUpdateRequest, err := json.Marshal(payload)
  104. log.Debugf("Update request payload: %+v", payload)
  105. if err != nil {
  106. log.Errorf("Update check failed %v", err)
  107. return
  108. }
  109. AvailableVersion = doRequest(jsonUpdateRequest)
  110. log.WithFields(log.Fields{
  111. "NewVersion": AvailableVersion,
  112. }).Infof("Update check complete")
  113. }