mirror of
https://github.com/safing/portbase
synced 2025-04-19 17:09:09 +00:00
180 lines
4.4 KiB
Go
180 lines
4.4 KiB
Go
package updater
|
|
|
|
import (
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/safing/portbase/utils"
|
|
)
|
|
|
|
// Registry States.
|
|
const (
|
|
StateReady = "ready" // Default idle state.
|
|
StateChecking = "checking" // Downloading indexes.
|
|
StateDownloading = "downloading" // Downloading updates.
|
|
StateFetching = "fetching" // Fetching a single file.
|
|
)
|
|
|
|
// RegistryState describes the registry state.
|
|
type RegistryState struct {
|
|
sync.Mutex
|
|
reg *ResourceRegistry
|
|
|
|
// ID holds the ID of the state the registry is currently in.
|
|
ID string
|
|
|
|
// Details holds further information about the current state.
|
|
Details any
|
|
|
|
// Updates holds generic information about the current status of pending
|
|
// and recently downloaded updates.
|
|
Updates UpdateState
|
|
|
|
// operationLock locks the operation of any state changing operation.
|
|
// This is separate from the registry lock, which locks access to the
|
|
// registry struct.
|
|
operationLock sync.Mutex
|
|
}
|
|
|
|
// StateDownloadingDetails holds details of the downloading state.
|
|
type StateDownloadingDetails struct {
|
|
// Resources holds the resource IDs that are being downloaded.
|
|
Resources []string
|
|
|
|
// FinishedUpTo holds the index of Resources that is currently being
|
|
// downloaded. Previous resources have finished downloading.
|
|
FinishedUpTo int
|
|
}
|
|
|
|
// UpdateState holds generic information about the current status of pending
|
|
// and recently downloaded updates.
|
|
type UpdateState struct {
|
|
// LastCheckAt holds the time of the last update check.
|
|
LastCheckAt *time.Time
|
|
// LastCheckError holds the error of the last check.
|
|
LastCheckError error
|
|
// PendingDownload holds the resources that are pending download.
|
|
PendingDownload []string
|
|
|
|
// LastDownloadAt holds the time when resources were downloaded the last time.
|
|
LastDownloadAt *time.Time
|
|
// LastDownloadError holds the error of the last download.
|
|
LastDownloadError error
|
|
// LastDownload holds the resources that we downloaded the last time updates
|
|
// were downloaded.
|
|
LastDownload []string
|
|
|
|
// LastSuccessAt holds the time of the last successful update (check).
|
|
LastSuccessAt *time.Time
|
|
}
|
|
|
|
// GetState returns the current registry state.
|
|
// The returned data must not be modified.
|
|
func (reg *ResourceRegistry) GetState() RegistryState {
|
|
reg.state.Lock()
|
|
defer reg.state.Unlock()
|
|
|
|
return RegistryState{
|
|
ID: reg.state.ID,
|
|
Details: reg.state.Details,
|
|
Updates: reg.state.Updates,
|
|
}
|
|
}
|
|
|
|
// StartOperation starts an operation.
|
|
func (s *RegistryState) StartOperation(id string) bool {
|
|
defer s.notify()
|
|
|
|
s.operationLock.Lock()
|
|
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
|
|
s.ID = id
|
|
return true
|
|
}
|
|
|
|
// UpdateOperationDetails updates the details of an operation.
|
|
// The supplied struct should be a copy and must not be changed after calling
|
|
// this function.
|
|
func (s *RegistryState) UpdateOperationDetails(details any) {
|
|
defer s.notify()
|
|
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
|
|
s.Details = details
|
|
}
|
|
|
|
// EndOperation ends an operation.
|
|
func (s *RegistryState) EndOperation() {
|
|
defer s.notify()
|
|
defer s.operationLock.Unlock()
|
|
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
|
|
s.ID = StateReady
|
|
s.Details = nil
|
|
}
|
|
|
|
// ReportUpdateCheck reports an update check to the registry state.
|
|
func (s *RegistryState) ReportUpdateCheck(pendingDownload []string, failed error) {
|
|
defer s.notify()
|
|
|
|
sort.Strings(pendingDownload)
|
|
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
|
|
now := time.Now()
|
|
s.Updates.LastCheckAt = &now
|
|
s.Updates.LastCheckError = failed
|
|
s.Updates.PendingDownload = pendingDownload
|
|
|
|
if failed == nil {
|
|
s.Updates.LastSuccessAt = &now
|
|
}
|
|
}
|
|
|
|
// ReportDownloads reports downloaded updates to the registry state.
|
|
func (s *RegistryState) ReportDownloads(downloaded []string, failed error) {
|
|
defer s.notify()
|
|
|
|
sort.Strings(downloaded)
|
|
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
|
|
now := time.Now()
|
|
s.Updates.LastDownloadAt = &now
|
|
s.Updates.LastDownloadError = failed
|
|
s.Updates.LastDownload = downloaded
|
|
|
|
// Remove downloaded resources from the pending list.
|
|
if len(s.Updates.PendingDownload) > 0 {
|
|
newPendingDownload := make([]string, 0, len(s.Updates.PendingDownload))
|
|
for _, pending := range s.Updates.PendingDownload {
|
|
if !utils.StringInSlice(downloaded, pending) {
|
|
newPendingDownload = append(newPendingDownload, pending)
|
|
}
|
|
}
|
|
s.Updates.PendingDownload = newPendingDownload
|
|
}
|
|
|
|
if failed == nil {
|
|
s.Updates.LastSuccessAt = &now
|
|
}
|
|
}
|
|
|
|
func (s *RegistryState) notify() {
|
|
switch {
|
|
case s.reg == nil:
|
|
return
|
|
case s.reg.StateNotifyFunc == nil:
|
|
return
|
|
}
|
|
|
|
s.reg.StateNotifyFunc(s)
|
|
}
|