github.com/safing/portbase@v0.19.5/updater/state.go (about) 1 package updater 2 3 import ( 4 "sort" 5 "sync" 6 "time" 7 8 "github.com/safing/portbase/utils" 9 ) 10 11 // Registry States. 12 const ( 13 StateReady = "ready" // Default idle state. 14 StateChecking = "checking" // Downloading indexes. 15 StateDownloading = "downloading" // Downloading updates. 16 StateFetching = "fetching" // Fetching a single file. 17 ) 18 19 // RegistryState describes the registry state. 20 type RegistryState struct { 21 sync.Mutex 22 reg *ResourceRegistry 23 24 // ID holds the ID of the state the registry is currently in. 25 ID string 26 27 // Details holds further information about the current state. 28 Details any 29 30 // Updates holds generic information about the current status of pending 31 // and recently downloaded updates. 32 Updates UpdateState 33 34 // operationLock locks the operation of any state changing operation. 35 // This is separate from the registry lock, which locks access to the 36 // registry struct. 37 operationLock sync.Mutex 38 } 39 40 // StateDownloadingDetails holds details of the downloading state. 41 type StateDownloadingDetails struct { 42 // Resources holds the resource IDs that are being downloaded. 43 Resources []string 44 45 // FinishedUpTo holds the index of Resources that is currently being 46 // downloaded. Previous resources have finished downloading. 47 FinishedUpTo int 48 } 49 50 // UpdateState holds generic information about the current status of pending 51 // and recently downloaded updates. 52 type UpdateState struct { 53 // LastCheckAt holds the time of the last update check. 54 LastCheckAt *time.Time 55 // LastCheckError holds the error of the last check. 56 LastCheckError error 57 // PendingDownload holds the resources that are pending download. 58 PendingDownload []string 59 60 // LastDownloadAt holds the time when resources were downloaded the last time. 61 LastDownloadAt *time.Time 62 // LastDownloadError holds the error of the last download. 63 LastDownloadError error 64 // LastDownload holds the resources that we downloaded the last time updates 65 // were downloaded. 66 LastDownload []string 67 68 // LastSuccessAt holds the time of the last successful update (check). 69 LastSuccessAt *time.Time 70 } 71 72 // GetState returns the current registry state. 73 // The returned data must not be modified. 74 func (reg *ResourceRegistry) GetState() RegistryState { 75 reg.state.Lock() 76 defer reg.state.Unlock() 77 78 return RegistryState{ 79 ID: reg.state.ID, 80 Details: reg.state.Details, 81 Updates: reg.state.Updates, 82 } 83 } 84 85 // StartOperation starts an operation. 86 func (s *RegistryState) StartOperation(id string) bool { 87 defer s.notify() 88 89 s.operationLock.Lock() 90 91 s.Lock() 92 defer s.Unlock() 93 94 s.ID = id 95 return true 96 } 97 98 // UpdateOperationDetails updates the details of an operation. 99 // The supplied struct should be a copy and must not be changed after calling 100 // this function. 101 func (s *RegistryState) UpdateOperationDetails(details any) { 102 defer s.notify() 103 104 s.Lock() 105 defer s.Unlock() 106 107 s.Details = details 108 } 109 110 // EndOperation ends an operation. 111 func (s *RegistryState) EndOperation() { 112 defer s.notify() 113 defer s.operationLock.Unlock() 114 115 s.Lock() 116 defer s.Unlock() 117 118 s.ID = StateReady 119 s.Details = nil 120 } 121 122 // ReportUpdateCheck reports an update check to the registry state. 123 func (s *RegistryState) ReportUpdateCheck(pendingDownload []string, failed error) { 124 defer s.notify() 125 126 sort.Strings(pendingDownload) 127 128 s.Lock() 129 defer s.Unlock() 130 131 now := time.Now() 132 s.Updates.LastCheckAt = &now 133 s.Updates.LastCheckError = failed 134 s.Updates.PendingDownload = pendingDownload 135 136 if failed == nil { 137 s.Updates.LastSuccessAt = &now 138 } 139 } 140 141 // ReportDownloads reports downloaded updates to the registry state. 142 func (s *RegistryState) ReportDownloads(downloaded []string, failed error) { 143 defer s.notify() 144 145 sort.Strings(downloaded) 146 147 s.Lock() 148 defer s.Unlock() 149 150 now := time.Now() 151 s.Updates.LastDownloadAt = &now 152 s.Updates.LastDownloadError = failed 153 s.Updates.LastDownload = downloaded 154 155 // Remove downloaded resources from the pending list. 156 if len(s.Updates.PendingDownload) > 0 { 157 newPendingDownload := make([]string, 0, len(s.Updates.PendingDownload)) 158 for _, pending := range s.Updates.PendingDownload { 159 if !utils.StringInSlice(downloaded, pending) { 160 newPendingDownload = append(newPendingDownload, pending) 161 } 162 } 163 s.Updates.PendingDownload = newPendingDownload 164 } 165 166 if failed == nil { 167 s.Updates.LastSuccessAt = &now 168 } 169 } 170 171 func (s *RegistryState) notify() { 172 switch { 173 case s.reg == nil: 174 return 175 case s.reg.StateNotifyFunc == nil: 176 return 177 } 178 179 s.reg.StateNotifyFunc(s) 180 }