feat: Настройки › Автообновление - clients compare their version with the server's every N and update themselves; the server looks at GitLab every N and, when a newer release is out, offers it to an administrator at login (after the PIN) with «Обновить» and «Отказаться»; the words of a made picture's result are the model's only

This commit is contained in:
redtea committed 2026-10-18 12:33:24 +03:00
1 parent 916a49958b
commit 3d2f886f04
22 files changed
+1074 -7

No files matched your search

+95
View File
@@ -0,0 +1,95 @@
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)
}
}
}