86c48288a0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
117 lines
3.5 KiB
Go
117 lines
3.5 KiB
Go
package lkgateway
|
|
|
|
// Ход обновления для окна с индикатором на странице: этапы (проверка, скачивание, установка,
|
|
// перезапуск) и байты скачанного. Страница опрашивает GET /admin/setup/update/progress; когда
|
|
// обновляется ядро, bj-server перезапускается — страница видит обрыв связи, ждёт и сверяет
|
|
// версию работающего ядра с целевой.
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type updateProgressState struct {
|
|
Seq int64 `json:"seq"`
|
|
Active bool `json:"active"`
|
|
Finished bool `json:"finished"`
|
|
Step int `json:"step"`
|
|
Steps int `json:"steps"`
|
|
Stage string `json:"stage"`
|
|
Done int64 `json:"done"`
|
|
Total int64 `json:"total"`
|
|
Target string `json:"target,omitempty"` // версия ядра после обновления (если ядро обновляется)
|
|
Result string `json:"result,omitempty"`
|
|
Note string `json:"note,omitempty"` // пояснение, которое нужно показать и после перезапуска ядра
|
|
Err string `json:"error,omitempty"`
|
|
Started time.Time `json:"started"`
|
|
Version string `json:"version"` // версия работающего bj-server
|
|
}
|
|
|
|
var updProg = struct {
|
|
sync.Mutex
|
|
s updateProgressState
|
|
}{}
|
|
|
|
func progressBegin(steps int) {
|
|
updProg.Lock()
|
|
defer updProg.Unlock()
|
|
updProg.s = updateProgressState{Seq: updProg.s.Seq + 1, Active: true, Steps: steps, Started: time.Now()}
|
|
}
|
|
|
|
func progressStage(step int, stage string) {
|
|
updProg.Lock()
|
|
defer updProg.Unlock()
|
|
updProg.s.Step, updProg.s.Stage = step, stage
|
|
}
|
|
|
|
func progressBytes(done, total int64) {
|
|
updProg.Lock()
|
|
defer updProg.Unlock()
|
|
updProg.s.Done, updProg.s.Total = done, total
|
|
}
|
|
|
|
func progressTarget(v string) {
|
|
updProg.Lock()
|
|
defer updProg.Unlock()
|
|
updProg.s.Target = v
|
|
}
|
|
|
|
func progressNote(n string) {
|
|
updProg.Lock()
|
|
defer updProg.Unlock()
|
|
updProg.s.Note = n
|
|
}
|
|
|
|
func progressEnd(result string, err error) {
|
|
updProg.Lock()
|
|
defer updProg.Unlock()
|
|
updProg.s.Active, updProg.s.Finished, updProg.s.Result = false, true, result
|
|
if err != nil {
|
|
updProg.s.Err = err.Error()
|
|
}
|
|
}
|
|
|
|
// progressWatchDir — пока идёт скачивание, раз в полсекунды считает байты в каталоге подготовки.
|
|
// Возвращает функцию остановки.
|
|
func progressWatchDir(dir string, total int64) func() {
|
|
stop := make(chan struct{})
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
t := time.NewTicker(500 * time.Millisecond)
|
|
defer t.Stop()
|
|
for {
|
|
var n int64
|
|
_ = filepath.Walk(dir, func(_ string, fi os.FileInfo, err error) error {
|
|
if err == nil && fi.Mode().IsRegular() {
|
|
n += fi.Size()
|
|
}
|
|
return nil
|
|
})
|
|
progressBytes(n, total)
|
|
select {
|
|
case <-stop:
|
|
return
|
|
case <-t.C:
|
|
}
|
|
}
|
|
}()
|
|
return func() { close(stop); wg.Wait(); progressBytes(total, total) }
|
|
}
|
|
|
|
// updateProgressHandler — GET /admin/setup/update/progress.
|
|
func (h *setupHandlers) updateProgress(w http.ResponseWriter, r *http.Request) {
|
|
updProg.Lock()
|
|
s := updProg.s
|
|
updProg.Unlock()
|
|
s.Version = BuildVersion
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_ = json.NewEncoder(w).Encode(s)
|
|
}
|