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) }