R-729 + R-545: the household can remove its own off-site target („Távoli mentési cél törlése")
POST /backup/offbox/clear (reveal-then-confirm) forgets the target and deletes ssh_key + known_hosts; nothing on the target is touched. repo_password is kept whenever anything could depend on it (hub sealed package — the R-241 rule; escrowed; a successful run; snapshots) and deleted only otherwise. Refused for the Felhom tier (rclone-pinned), while the single-flight is held, and while an abandonment countdown runs. i18n parity fixtures of backups_remote gain the additive block only. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -331,6 +331,49 @@ func (s *Server) offboxResetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
offboxRedirect(w, r, "flash.offbox.restart_started", false)
|
||||
}
|
||||
|
||||
// offboxClearHandler is the household's „Távoli mentési cél törlése" press (R-729 / R-545): forget the
|
||||
// target and delete its transport secrets, never the repository. Reveal-then-confirm like the reset
|
||||
// (confirm=1). The refusals and what is kept are backup.ClearOffboxTarget's; this maps them to words.
|
||||
func (s *Server) offboxClearHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if s.backupMgr == nil {
|
||||
offboxRedirect(w, r, "flash.offbox.mgr_unavailable", true)
|
||||
return
|
||||
}
|
||||
_ = r.ParseForm()
|
||||
if r.FormValue("confirm") != "1" {
|
||||
offboxRedirect(w, r, "flash.offbox.clear_needs_confirmation", true)
|
||||
return
|
||||
}
|
||||
res, err := s.backupMgr.ClearOffboxTarget()
|
||||
switch {
|
||||
case errors.Is(err, backup.ErrOffboxClearNoTarget):
|
||||
offboxRedirect(w, r, "flash.offbox.target_not_set", true)
|
||||
return
|
||||
case errors.Is(err, backup.ErrOffboxClearHubTier):
|
||||
offboxRedirect(w, r, "flash.offbox.clear_hub_tier", true)
|
||||
return
|
||||
case errors.Is(err, backup.ErrOffboxClearBusy):
|
||||
offboxRedirect(w, r, "flash.offbox.clear_busy", true)
|
||||
return
|
||||
case errors.Is(err, backup.ErrOffboxClearAbandonOn):
|
||||
offboxRedirect(w, r, "flash.offbox.clear_abandon_pending", true)
|
||||
return
|
||||
case err != nil:
|
||||
s.logger.Printf("[ERROR] [web] off-site target clear failed: %v", err)
|
||||
offboxRedirect(w, r, "flash.offbox.save_failed", true)
|
||||
return
|
||||
}
|
||||
if !res.KeptRepoPassword {
|
||||
// The key is gone, so a copy still staged on the agent for an unfinished ceremony is a stale
|
||||
// secret with nothing behind it. Best-effort, like the confirm path's wipe.
|
||||
if werr := s.wipeStagedEscrow(r.Context()); werr != nil {
|
||||
s.logger.Printf("[WARN] [web] off-site target cleared, but the agent-staged escrow copy was not wiped: %v", werr)
|
||||
}
|
||||
}
|
||||
s.reportTriggerNow()
|
||||
offboxRedirect(w, r, "flash.offbox.target_cleared", false)
|
||||
}
|
||||
|
||||
// offboxStatusHandler (Part C) is the poll source for the remote-backup run status — the page polls it
|
||||
// after "Távoli mentés most" and flips to the terminal state without a manual reload. Session-auth'd.
|
||||
func (s *Server) offboxStatusHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user