Files
SODEW/sodew-bash-main/libs/commands/storage.sh
T
2026-08-18 09:40:08 +02:00

375 lines
14 KiB
Bash

storageLabel="[Storage]"
# Lists all backup directories on external storage with type labels (archive, daily, or unknown).
function cmdStorageBackupList() {
local dirList error dirCount archiveList dailyList
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
dirCount=$(grep -c . <<< "$dirList" || true)
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
printRow
local i=0 num dir label
while read -r dir; do
((++i))
num=$(printf "%0${#dirCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if listContains "$dir" "$archiveList"; then
printDotText "$label" "${fontGreen}archive$fontReset"
elif listContains "$dir" "$dailyList"; then
printDotText "$label" "${fontGreen}daily$fontReset"
else
printDotText "$label" "$labelUnknown"
fi
done < <(awk 'NF' <<< "$dirList")
}
# Rotates archive backups on external storage: deletes old entries exceeding $storageArchiveKeep.
function cmdStorageBackupArchiveDispatch() {
local dirList error
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
printSection "Config"
printDotText "Type" "$storageType"
printDotText "Path" "$rsyncPath"
printDotText "Archive keep" "$storageArchiveKeep"
printDotText "Archive pattern" "$backupArchiveDirPattern"
printSection "Directories found"
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
local archiveCount archiveRemoveCount i=0 num label dirDate dirDays archiveRemoveList=""
archiveCount=$(grep -c . <<< "$archiveList" || true)
if [[ "$archiveCount" -gt 0 ]]; then
while read -r dir; do
((++i))
num=$(printf "%0${#archiveCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if (( archiveCount - storageArchiveKeep >= i )); then
printDotText "$label" "${fontRed}delete$fontReset"
archiveRemoveList+=$'\n'"$dir"
else
printDotText "$label" "${fontGreen}save$fontReset"
fi
done < <(awk 'NF' <<< "$archiveList")
archiveRemoveCount=$(grep -c . <<< "$archiveRemoveList" || true)
if [[ "$archiveRemoveCount" -gt 0 ]]; then
printSection "Deleting Directories"
while read -r dir; do
label="$dir"
if ! runError error storageBackupRemove "$dir"; then
printDotText "$label" "$labelFail"
printDanger "$error"
else
printDotText "$label" "$labelDone"
fi
done < <(awk 'NF' <<< "$archiveRemoveList")
fi
fi
}
# Rotates daily backups on external storage: deletes old entries exceeding $storageDailyKeep, skips today.
function cmdStorageBackupDailyDispatch() {
local dirList error
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
printSection "Config"
printDotText "Type" "$storageType"
printDotText "Path" "$rsyncPath"
printDotText "Daily keep" "$storageDailyKeep"
printDotText "Daily pattern" "$backupDailyDirPattern"
printSection "Directories found"
dailyList=$(printf '%s\n' "$dirList" | grep -v "$appDate" | grep -E -- "$backupDailyDirPattern" | sort || true)
local dailyCount dailyRemoveCount i=0 num label dailyRemoveList=""
dailyCount=$(grep -c . <<< "$dailyList" || true)
if [[ "$dailyCount" -gt 0 ]]; then
while read -r dir; do
((++i))
num=$(printf "%0${#dailyCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if [[ "$dir" == "$appDate" ]]; then
printDotText "$label" "${fontGray}skipped$fontReset"
elif (( dailyCount - storageDailyKeep >= i )); then
printDotText "$label" "${fontRed}delete$fontReset"
dailyRemoveList+=$'\n'"$dir"
else
printDotText "$label" "${fontGreen}save$fontReset"
fi
done < <(awk 'NF' <<< "$dailyList")
dailyRemoveCount=$(grep -c . <<< "$dailyRemoveList" || true)
if [[ "$dailyRemoveCount" -gt 0 ]]; then
printSection "Deleting Directories"
while read -r dir; do
label="$dir"
if ! runError error storageBackupRemove "$dir"; then
printDotText "$label" "$labelFail"
printDanger "$error"
else
printDotText "$label" "$labelDone"
fi
done < <(awk 'NF' <<< "$dailyRemoveList")
fi
fi
}
# Synchronizes the local path to external storage (rsync or SSH).
# $1 (sourcePath): local path to sync.
# [$2] (type): storage type (rsync or ssh); defaults to $storageType.
function cmdStoragePathSync() {
local sourcePath="$1"
[[ -n "$sourcePath" ]] || { printDanger "Source path not specified"; return 1; }
local type="${2:-$storageType}"
local error
printSection "Config"
printDotText "Source" "$sourcePath"
if [[ ! -e "$sourcePath" ]]; then
printDotText "$sourcePath" "${fontRed}not found$fontReset"
return 1
fi
case "$type" in
rsync)
printDotText "Type" "$type"
printDotText "rSync URI" "$rsyncUri"
printDotText "rSync path" "$rsyncPath"
printSection "Executing"
runError error storagePathSyncRsync "$sourcePath" || {
printDotText "synchronization" "$labelFail"
printDanger "$error"
return 1
}
;;
ssh)
printDotText "Type" "$type"
printDotText "SSH URI" "$sshUser@$sshHost"
printDotText "SSH path" "$sshPath"
printSection "Executing"
runError error storagePathSyncSSH "$sourcePath" || {
printDotText "synchronization" "$labelFail"
printDanger "$error"
return 1
}
;;
*)
printDotText "Type" "$type $labelUnknown"
return 1
;;
esac
printDotText "synchronization" "$labelDone"
}
# Syncs today's daily backup ($backupDailyPath/$appDate) to external storage.
function cmdStorageBackupDailySyncLast() {
cmdStoragePathSync "$backupDailyPath/$appDate"
}
# Syncs the newest archive backup directory to external storage.
function cmdStorageBackupArchiveSyncLast() {
local dirList archiveList archiveDir error
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
archiveDir=$(tail -n 1 <<< "$archiveList")
[[ -n "$archiveDir" ]] || { printDanger "No archive directories found"; return 1; }
cmdStoragePathSync "$backupArchivePath/$archiveDir"
}
# Interactively selects a local archive backup directory and syncs it to external storage.
function cmdStorageBackupArchiveSync() {
local dirList error archiveList archiveCount
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
archiveCount=$(grep -c . <<< "$archiveList" || true)
printRow
local i=0 num dir
while read -r dir; do
((++i))
num=$(printf "%0${#archiveCount}d" "$i")
printDotText "$num: $fontBlue$dir$fontReset" "${fontGreen}archive$fontReset"
done < <(awk 'NF' <<< "$archiveList")
printRow
local input item selected
read -r -p "Specify the backup number: " input
printRow
[[ -z "$input" ]] && input=0
[[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid backup number"; return 1; }
item=$((10#$input))
selected=$(awk 'NF {n++} n == item {print; exit}' item="$item" <<< "$archiveList")
[[ -n "$selected" ]] || { printDanger "Unknown backup number"; return 1; }
cmdStoragePathSync "$backupArchivePath/$selected"
}
# Interactively selects a local daily backup directory and syncs it to external storage.
function cmdStorageBackupDailySync() {
local dirList error dailyList dailyCount
run dirList error fileList "$backupDailyPath" d || { printDanger "$error"; return 1; }
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
dailyCount=$(grep -c . <<< "$dailyList" || true)
printRow
local i=0 num dir
while read -r dir; do
((++i))
num=$(printf "%0${#dailyCount}d" "$i")
printDotText "$num: $fontBlue$dir$fontReset" "${fontGreen}daily$fontReset"
done < <(awk 'NF' <<< "$dailyList")
printRow
local input item selected
read -r -p "Specify the backup number: " input
printRow
[[ -z "$input" ]] && input=0
[[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid backup number"; return 1; }
item=$((10#$input))
selected=$(awk 'NF {n++} n == item {print; exit}' item="$item" <<< "$dailyList")
[[ -n "$selected" ]] || { printDanger "Unknown backup number"; return 1; }
cmdStoragePathSync "$backupDailyPath/$selected"
}
# Dispatches interactive backup sync by type.
# $1 (type): backup type: archive or daily.
function cmdStorageBackupSync() {
local type
type="$1"
case "$type" in
archive)
cmdStorageBackupArchiveSync
;;
daily)
cmdStorageBackupDailySync
;;
*)
printDanger "Unknown type backup: $type";
return 1
;;
esac
}
# Interactively selects a backup directory on external storage and removes it.
function cmdStorageBackupRemove() {
local dirList error dirCount archiveList dailyList dir i=0 num label
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
dirCount=$(grep -c . <<< "$dirList" || true)
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
printRow
while read -r dir; do
((++i))
num=$(printf "%0${#dirCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if listContains "$dir" "$archiveList"; then
printDotText "$label" "${fontGreen}archive$fontReset"
elif listContains "$dir" "$dailyList"; then
printDotText "$label" "${fontGreen}daily$fontReset"
else
printDotText "$label" "$labelUnknown"
fi
done < <(awk 'NF' <<< "$dirList")
printRow
local input item selected
read -r -p "Specify the backup number: " input
printRow
[[ -z "$input" ]] && input=0
[[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid backup number"; return 1; }
item=$((10#$input))
selected=$(awk 'NF {n++} n == item {print; exit}' item="$item" <<< "$dirList")
[[ -n "$selected" ]] || { printDanger "Unknown backup number"; return 1; }
if ! runError error storageBackupRemove "$selected"; then
printDotText "$selected" "$labelFail"
printDanger "$error"
return 1
fi
printDotText "$selected" "$labelDone"
}
# Compares local and remote backup directories, showing which exist on each side.
function cmdStorageBackupCompare() {
local dirList error localArchiveList localDailyList remoteArchiveList remoteDailyList
run dirList error fileList "$backupArchivePath" d || { printDanger "Archive path: $error"; return 1; }
localArchiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
run dirList error fileList "$backupDailyPath" d || { printDanger "Archive path: $error"; return 1; }
localDailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
run dirList error storageFileList "/" d || { printDanger "$error"; return 1; }
remoteArchiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
remoteDailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
printSection "Local"
printDotText "Archive" "$(grep -c . <<< "$localArchiveList" || true)"
printDotText "Daily" "$(grep -c . <<< "$localDailyList" || true)"
printSection "Remote"
printDotText "Archive" "$(grep -c . <<< "$remoteArchiveList" || true)"
printDotText "Daily" "$(grep -c . <<< "$remoteDailyList" || true)"
local allDirs
allDirs=$(printf '%s\n' "$localArchiveList" "$localDailyList" "$remoteArchiveList" "$remoteDailyList" | awk 'NF' | sort -u)
printSection "Comparison"
local dir localStatus remoteStatus
out="${fontRed}X$fontReset"
localIn="${fontGreen}L$fontReset"
remoteIn="${fontGreen}R$fontReset"
while read -r dir; do
localStatus="$out"
remoteStatus="$out"
if listContains "$dir" "$localArchiveList"; then
localStatus="$localIn"
elif listContains "$dir" "$localDailyList"; then
localStatus="$localIn"
fi
if listContains "$dir" "$remoteArchiveList"; then
remoteStatus="$remoteIn"
elif listContains "$dir" "$remoteDailyList"; then
remoteStatus="$remoteIn"
fi
printDotText "$fontBlue$dir$fontReset" "[ $localStatus : $remoteStatus ]"
done < <(awk 'NF' <<< "$allDirs")
}
function cmdStorageBackupSize() {
local fileList file size total
printSection "FTP: $ftpPath"
total=0
fileList=$(ftpFileList | sort -n)
while IFS= read -r file; do
size=$(ftpPathSize "/$file" "gb")
printDotText "$file" "$size Gb"
(( total += size ))
done <<< "$fileList"
printRow
printDotText "total" "$total Gb"
}