The /tasks page's right-side tick + dynamic Select all / Clear All ⇄ Delete Selected layout now covers the other three management surfaces: - App detail → Tasks tab: filter bar gains the Clear All button and master tick; Clear All there scopes to that app's tasks only. The selection set is resolved through window.tasksManager everywhere — TasksManager is constructed in several places, and ticks previously landed on one instance while Delete Selected read another's empty set. - Apps overview → Updates: the header's Update all button now morphs to Update Selected (N) + Clear in place as rows are ticked, replacing the separate selection bar between toolbar and list. - App detail → Backups: each snapshot row gains Delete + a right-side tick; a toolbar atop the list morphs Delete All ⇄ Delete Selected (N). The whole selection rides in ONE task (delete <app> 1:a,2:b,…) since the backup surfaces hold one task per subject at a time. - CLI: backup app delete accepts comma-separated <idx>:<snap> pairs, and both delete and delete_all now regenerate the WebUI backup JSON so deleted snapshots leave the screen instead of lingering until the next backup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
160 lines
6.9 KiB
Bash
Executable File
160 lines
6.9 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
cliHandleBackupCommands()
|
|
{
|
|
local backup_type="$initial_command2"
|
|
local action="$initial_command3"
|
|
local name="$initial_command4"
|
|
local extra="$initial_command5"
|
|
local extra2="$initial_command6"
|
|
|
|
if [[ -z "$backup_type" ]]; then
|
|
cliShowBackupHelp
|
|
return
|
|
fi
|
|
|
|
case "$backup_type" in
|
|
app)
|
|
case "$action" in
|
|
""|help) cliShowBackupHelp ;;
|
|
create)
|
|
[[ -z "$name" ]] && { isNotice "No app name provided."; cliShowBackupHelp; return; }
|
|
if [[ "$LIBREPORTAL_TASK_EXEC" == "1" ]]; then
|
|
backupAppStart "$name"
|
|
else
|
|
cliTaskRun "libreportal backup app create $name" "backup" "$name"
|
|
fi
|
|
;;
|
|
schedule)
|
|
[[ -z "$name" ]] && { isNotice "No app name provided."; cliShowBackupHelp; return; }
|
|
backupAppSchedule "$name"
|
|
;;
|
|
delete)
|
|
[[ -z "$name" ]] && { isNotice "No app name provided."; cliShowBackupHelp; return; }
|
|
[[ -z "$extra" ]] && { isNotice "No <location_idx>:<snapshot_id> provided (e.g. 1:abc123)."; cliShowBackupHelp; return; }
|
|
# One or many: <idx>:<snap>[,<idx>:<snap>…]. The WebUI's
|
|
# bulk delete sends the whole selection as ONE task — the
|
|
# backup surfaces allow one task per subject at a time, so
|
|
# N queued single-snapshot tasks would be refused after
|
|
# the first.
|
|
local _del_pair _del_idx _del_snap _del_ok=0 _del_failed=0
|
|
local _del_pairs
|
|
IFS=',' read -ra _del_pairs <<< "$extra"
|
|
for _del_pair in "${_del_pairs[@]}"; do
|
|
[[ -z "$_del_pair" ]] && continue
|
|
_del_idx="${_del_pair%%:*}"
|
|
_del_snap="${_del_pair##*:}"
|
|
if backupAppDeleteSnapshot "$_del_idx" "$_del_snap"; then
|
|
_del_ok=$((_del_ok + 1))
|
|
else
|
|
_del_failed=$((_del_failed + 1))
|
|
fi
|
|
done
|
|
# The generated JSON is what the WebUI renders — without a
|
|
# regen the deleted snapshots keep showing until the next
|
|
# backup rewrites it.
|
|
if [[ "$CFG_REQUIREMENT_WEBUI" == "true" && $_del_ok -gt 0 ]]; then
|
|
webuiGenerateBackupDashboard
|
|
webuiGenerateBackupSnapshots all
|
|
webuiGenerateBackupAppStatus "$name"
|
|
fi
|
|
if [[ $_del_failed -gt 0 ]]; then
|
|
isError "Deleted $_del_ok snapshot(s); $_del_failed failed"
|
|
false
|
|
elif [[ $_del_ok -gt 1 ]]; then
|
|
isSuccessful "Deleted $_del_ok snapshots for $name"
|
|
fi
|
|
;;
|
|
delete_all)
|
|
[[ -z "$name" ]] && { isNotice "No app name provided."; cliShowBackupHelp; return; }
|
|
backupAppDeleteAll "$name"
|
|
# Same staleness rule as `delete`: the WebUI renders the
|
|
# generated JSON, so regenerate it now that the snapshots
|
|
# are gone.
|
|
if [[ "$CFG_REQUIREMENT_WEBUI" == "true" ]]; then
|
|
webuiGenerateBackupDashboard
|
|
webuiGenerateBackupSnapshots all
|
|
webuiGenerateBackupAppStatus "$name"
|
|
fi
|
|
;;
|
|
list)
|
|
[[ -z "$name" ]] && { isNotice "No app name provided."; cliShowBackupHelp; return; }
|
|
local idx
|
|
while IFS= read -r idx; do
|
|
[[ -z "$idx" ]] && continue
|
|
echo "--- $(resticLocationName "$idx") (loc $idx) ---"
|
|
engineSnapshotsJson "$idx" "$name"
|
|
done < <(resticEnabledLocations)
|
|
;;
|
|
*)
|
|
isNotice "Invalid app backup action: $action"
|
|
cliShowBackupHelp
|
|
;;
|
|
esac
|
|
;;
|
|
all)
|
|
backupAllApps
|
|
;;
|
|
system)
|
|
backupSystemConfig
|
|
;;
|
|
scheduled)
|
|
backupScheduleEnabledApps
|
|
;;
|
|
location)
|
|
case "$action" in
|
|
add)
|
|
[[ -z "$name" ]] && { isNotice "Usage: backup location add <name> [type]"; cliShowBackupHelp; return; }
|
|
locationAdd "$name" "$extra"
|
|
;;
|
|
remove)
|
|
[[ -z "$name" ]] && { isNotice "Usage: backup location remove <idx>"; cliShowBackupHelp; return; }
|
|
locationRemove "$name" "$extra"
|
|
;;
|
|
list)
|
|
local idx
|
|
while IFS= read -r idx; do
|
|
[[ -z "$idx" ]] && continue
|
|
local enabled="no"; resticLocationEnabled "$idx" && enabled="yes"
|
|
echo "[$idx] $(resticLocationName "$idx") — type=$(resticLocationType "$idx"), enabled=$enabled"
|
|
done < <(resticAllLocationIndices)
|
|
;;
|
|
init)
|
|
engineInitAllLocations
|
|
;;
|
|
check)
|
|
engineCheckAllLocations "${name:-5}"
|
|
;;
|
|
stats)
|
|
[[ -z "$name" ]] && name="1"
|
|
engineLocationStats "$name"
|
|
;;
|
|
ssh-key-set)
|
|
[[ -z "$name" || -z "$extra" ]] && { isNotice "Usage: backup location ssh-key-set <idx> <base64-key>"; return; }
|
|
backupSshKeySet "$name" "$extra"
|
|
;;
|
|
ssh-key-generate)
|
|
[[ -z "$name" ]] && { isNotice "Usage: backup location ssh-key-generate <idx>"; return; }
|
|
backupSshKeyGenerate "$name"
|
|
;;
|
|
ssh-key-public)
|
|
[[ -z "$name" ]] && { isNotice "Usage: backup location ssh-key-public <idx>"; return; }
|
|
backupSshKeyPublic "$name"
|
|
;;
|
|
ssh-key-delete)
|
|
[[ -z "$name" ]] && { isNotice "Usage: backup location ssh-key-delete <idx>"; return; }
|
|
backupSshKeyDelete "$name"
|
|
;;
|
|
*) isNotice "Invalid location action: $action"; cliShowBackupHelp ;;
|
|
esac
|
|
;;
|
|
verify)
|
|
engineCheckAllLocations "${action:-5}"
|
|
;;
|
|
*)
|
|
isNotice "Invalid backup type: $backup_type"
|
|
cliShowBackupHelp
|
|
;;
|
|
esac
|
|
}
|