Шаг «Проверка» не сбрасывается после перезапуска bj

Итог проверки готовности (шаг 10 мастера) жил только в памяти и 24 ч: после
каждого перезапуска (обновления) главная снова показывала «Требуется
настройка — Проверка», пока не откроешь мастер. Теперь итог в
readiness.json и держится до следующей проверки; если файла ещё нет —
проверка в фоне через минуту после запуска.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
fontvielle
2026-10-22 12:24:23 +03:00
parent da302c6d69
commit 5c4a12fb0b
+48 -7
View File
@@ -453,20 +453,63 @@ func readyDepo(ctx context.Context, rc *RuntimeConfig) ReadyCheck {
return c
}
// lastReadiness — итог последней проверки готовности: по нему отмечается шаг 10 мастера.
// lastReadiness — итог последней проверки готовности: по нему отмечается шаг 10 мастера. Хранится в
// файле: после перезапуска bj (обновления) шаг не должен снова становиться «не выполнен», а главная —
// показывать «Требуется настройка». Пройденная проверка держится, пока следующая не покажет ошибку.
var lastReadiness struct {
sync.Mutex
ready bool
at time.Time
ready bool
at time.Time
loaded bool
}
const readinessFile = "/var/lib/bj/.bj/readiness.json"
func readinessDone() bool {
lastReadiness.Lock()
defer lastReadiness.Unlock()
return lastReadiness.ready && time.Since(lastReadiness.at) < 24*time.Hour
if !lastReadiness.loaded {
lastReadiness.loaded = true
var f struct {
Ready bool `json:"ready"`
At time.Time `json:"at"`
}
if b, err := os.ReadFile(readinessFile); err == nil && json.Unmarshal(b, &f) == nil && lastReadiness.at.IsZero() {
lastReadiness.ready, lastReadiness.at = f.Ready, f.At
}
}
return lastReadiness.ready
}
// readinessSave — запомнить итог проверки готовности (память и файл).
func readinessSave(ready bool, at time.Time) {
lastReadiness.Lock()
lastReadiness.ready, lastReadiness.at, lastReadiness.loaded = ready, at, true
lastReadiness.Unlock()
if b, err := json.Marshal(map[string]any{"ready": ready, "at": at}); err == nil {
tmp := readinessFile + ".tmp"
if os.WriteFile(tmp, b, 0o640) == nil {
_ = os.Rename(tmp, readinessFile)
}
}
}
func registerReadiness(mux *http.ServeMux, rc *RuntimeConfig) {
go func() { // итога ещё нет (первый запуск с файлом) — проверить в фоне, не дожидаясь мастера
time.Sleep(time.Minute)
if _, err := os.Stat(readinessFile); err == nil {
return
}
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
ready := true
for _, c := range readinessChecks(ctx, rc) {
if c.Required && c.State != "ok" && c.State != "warn" {
ready = false
}
}
readinessSave(ready, time.Now())
}()
mux.HandleFunc("/admin/setup/readiness", func(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
defer cancel()
@@ -477,9 +520,7 @@ func registerReadiness(mux *http.ServeMux, rc *RuntimeConfig) {
ready = false
}
}
lastReadiness.Lock()
lastReadiness.ready, lastReadiness.at = ready, time.Now()
lastReadiness.Unlock()
readinessSave(ready, time.Now())
jsonOut(w, map[string]any{"checks": checks, "ready": ready, "at": time.Now().Format("02.01.2006 15:04:05")})
})
}