68b3a3932e
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017CDMFpFx84pfviCTVuGGhf
72 lines
2.4 KiB
Go
72 lines
2.4 KiB
Go
package backup
|
|
|
|
import "time"
|
|
|
|
// Restore op-status (Part B): a lightweight, in-memory surface for the ASYNC restore family so the
|
|
// backups page can show a progress banner (running → success/failure) instead of blocking the HTTP
|
|
// request until the restore completes. It is display-only and mutex-guarded on the Manager's `mu`;
|
|
// it does NOT gate concurrency (that stays the restore functions' internal single-flight acquire).
|
|
// In-memory only — lost on a controller restart (same precedent as notification cooldowns); a page
|
|
// load mid-op after a restart simply shows no banner.
|
|
|
|
// RestoreOpResult is the terminal record of the most recent restore op.
|
|
type RestoreOpResult struct {
|
|
Op string `json:"op"` // "restore" | "tier2-restore" | "offbox-restore"
|
|
Stack string `json:"stack"`
|
|
OK bool `json:"ok"`
|
|
Message string `json:"message"`
|
|
FinishedAt time.Time `json:"finished_at"`
|
|
}
|
|
|
|
// RestoreOpStatus is the shape served at GET /api/backup/restore-status.
|
|
type RestoreOpStatus struct {
|
|
Running bool `json:"running"`
|
|
Op string `json:"op,omitempty"`
|
|
Stack string `json:"stack,omitempty"`
|
|
StartedAt time.Time `json:"started_at,omitempty"`
|
|
Last *RestoreOpResult `json:"last,omitempty"`
|
|
}
|
|
|
|
// BeginRestoreOp marks a restore op in flight (called by the handler just before launching the
|
|
// background goroutine). Idempotent enough for display; concurrency is enforced elsewhere.
|
|
func (m *Manager) BeginRestoreOp(op, stack string) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.opRunning = true
|
|
m.opName = op
|
|
m.opStack = stack
|
|
m.opStartedAt = time.Now()
|
|
}
|
|
|
|
// EndRestoreOp records the terminal result (called from the goroutine on completion, success or
|
|
// failure). Message carries the error (failure) or a human note like the scratch path (offbox).
|
|
func (m *Manager) EndRestoreOp(ok bool, message string) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.opLast = &RestoreOpResult{
|
|
Op: m.opName,
|
|
Stack: m.opStack,
|
|
OK: ok,
|
|
Message: message,
|
|
FinishedAt: time.Now(),
|
|
}
|
|
m.opRunning = false
|
|
}
|
|
|
|
// RestoreStatus returns a deep copy of the current restore op-status for the page/API.
|
|
func (m *Manager) RestoreStatus() RestoreOpStatus {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
st := RestoreOpStatus{
|
|
Running: m.opRunning,
|
|
Op: m.opName,
|
|
Stack: m.opStack,
|
|
StartedAt: m.opStartedAt,
|
|
}
|
|
if m.opLast != nil {
|
|
cp := *m.opLast
|
|
st.Last = &cp
|
|
}
|
|
return st
|
|
}
|