updateCheck.go 3.0 KB

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