package web import ( "errors" "net/http" "strings" "gitea.dooplex.hu/admin/felhom-controller/internal/backup" ) // D25 (operator ruling 2026-10-21; R-1175 + R-1181): the restore page lists the second-drive copies whose source is // gone and lets the household release one with „Eltávolítás". Nothing else removes them. // keptCopyRow is one line of the „Megőrzött másolatok" list. type keptCopyRow struct { ID string Source string Line string // „a(z) meghajtóról megőrzött másolat · " SizeHuman string Since string } func (s *Server) keptCopyRows(lang string) []keptCopyRow { if s.backupMgr == nil { return nil } var out []keptCopyRow for _, k := range s.backupMgr.KeptCopies() { why := s.msgLang(lang, "kept.reason."+k.Reason) out = append(out, keptCopyRow{ ID: k.ID, Source: k.Source, Line: s.msgLang(lang, "kept.from_drive", k.Drive) + " · " + why, SizeHuman: backup.HumanizeBytes(k.SizeBytes), Since: k.Since, }) } return out } // keptCopyRemoveHandler is „Eltávolítás": POST /backup/kept/remove (id, confirm=1). It removes ONE kept copy by its // record (backup.RemoveKeptCopy: the recorded path, inside its copy tree, same device + inode) and nothing else. func (s *Server) keptCopyRemoveHandler(w http.ResponseWriter, r *http.Request) { if s.backupMgr == nil { offboxRedirectTo(w, r, "/backups/restore", "flash.offbox.mgr_unreachable", true) return } _ = r.ParseForm() id := strings.TrimSpace(r.FormValue("id")) if r.FormValue("confirm") != "1" { offboxRedirectTo(w, r, "/backups/restore", "flash.kept.needs_confirmation", true) return } if msg, blocked := s.restoreOpBlocked(); blocked { s.logger.Printf("[WARN] [web] kept-copy removal refused (%s): a backup/restore op is running", id) offboxRedirectTo(w, r, "/backups/restore", msg, true) return } switch err := s.backupMgr.RemoveKeptCopy(id); { case err == nil: offboxRedirectTo(w, r, "/backups/restore", "flash.kept.removed", false) case errors.Is(err, backup.ErrKeptCopyNotFound): offboxRedirectTo(w, r, "/backups/restore", "flash.kept.not_found", true) case errors.Is(err, backup.ErrKeptCopyChanged): offboxRedirectTo(w, r, "/backups/restore", "flash.kept.changed", true) default: s.logger.Printf("[WARN] [web] kept-copy removal %s failed: %v", id, err) offboxRedirectTo(w, r, "/backups/restore", "flash.kept.remove_failed", true, err.Error()) } }