package agent import ( "context" "log" "net/http" "strconv" "sync" "time" "github.com/x-grk/longpolling-remote-access/internal/proto" ) // Auto-update, the agent's part: the server tells (with every poll, on its // config and on the WebSocket) its own version and how often to compare - // in minutes, 0 for never. When the server's version is newer, the agent // takes the new one from it, as the panel's «Обновить» does. // autoUpdate is what the agent was told, and when it last looked. type autoUpdate struct { mu sync.Mutex minutes int serverVer string last time.Time } func (a *Agent) applyUpdateInfo(minutes int, serverVersion string) { a.auto.mu.Lock() defer a.auto.mu.Unlock() if minutes < 0 { minutes = 0 } a.auto.minutes = minutes if serverVersion != "" { a.auto.serverVer = serverVersion } } // applyUpdateHeaders takes the settings from the headers of an answer; an // answer without them (an older server) changes nothing. func (a *Agent) applyUpdateHeaders(h http.Header) { v := h.Get(proto.AutoUpdateHeader) if v == "" { return } m, err := strconv.Atoi(v) if err != nil { m = 0 } a.applyUpdateInfo(m, h.Get(proto.ServerVersionHeader)) } func (a *Agent) updateInfo() (int, string) { a.auto.mu.Lock() defer a.auto.mu.Unlock() return a.auto.minutes, a.auto.serverVer } func (a *Agent) autoUpdateOn() bool { m, _ := a.updateInfo(); return m > 0 } // autoUpdateTick: one look at the clock. Out of the interval since the last // look, it looks; the server's version newer than its own, it updates. // Reports whether it did. func (a *Agent) autoUpdateTick(now time.Time, update func() proto.TaskResult) bool { a.auto.mu.Lock() if a.auto.minutes <= 0 || (!a.auto.last.IsZero() && now.Sub(a.auto.last) < time.Duration(a.auto.minutes)*time.Minute) { a.auto.mu.Unlock() return false } a.auto.last = now server := a.auto.serverVer a.auto.mu.Unlock() if !proto.VersionNewer(server, Version) { return false } log.Printf("auto-update: server has %s, this is %s - updating", server, Version) if res := update(); !res.OK { log.Printf("auto-update: %s", res.Error) } return true } // autoUpdateLoop watches the clock until ctx ends. func (a *Agent) autoUpdateLoop(ctx context.Context) { t := time.NewTicker(30 * time.Second) defer t.Stop() for { select { case <-ctx.Done(): return case now := <-t.C: a.autoUpdateTick(now, a.doUpdate) } } }