feat: add pos system backup — encrypted folder snapshots with --service picker

This commit is contained in:
ciya
2026-08-03 08:58:38 -04:00
parent db7bf4de86
commit e7c8c176ed
5 changed files with 125 additions and 1252 deletions
+4 -2
View File
@@ -40,7 +40,7 @@ CATEGORIES
network ip | checkport | scan | hotspot
docker ps | compose
media mp3 | mp4
system firewall
system firewall | backup
ssh load-keys
vbox create | enter | stop | start | rm | ls
@@ -58,6 +58,8 @@ EXAMPLES
pos media mp4 <url> Download video as MP4
pos system firewall Interactive UFW manager
pos system backup /srv/project Encrypted (AES-256) folder snapshot
pos system backup --service Pick a folder from /srv or ~/srv
pos ssh load-keys Load all SSH keys into agent
@@ -110,7 +112,7 @@ MAIN_LOG="$LOG_DIR/pos.log"
log_cmd() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $* → exit $2" >> "$MAIN_LOG"; }
# Commands that read from stdin interactively — only log invocation
INTERACTIVE_CMDS="system-firewall media-mp4"
INTERACTIVE_CMDS="system-firewall media-mp4 system-backup"
for ((i=n-1; i>=0; i--)); do
cmd="pos"
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env bash
set -euo pipefail
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
usage() {
cat <<EOF
Usage: pos system backup <folder-path>
pos system backup --service
Create a gpg-encrypted (AES-256) tar.gz snapshot of a folder and verify it.
The archive password is prompted twice and never stored.
Modes:
<folder-path> Back up that folder directly.
--service List folders under /srv and ~/srv, pick one, back it up.
The final artifact <name>_<date>.tar.gz.gpg is written to the current directory.
Environment:
BACKUP_SERVICE_ROOTS Space-separated roots for --service
(default: /srv \$HOME/srv)
EOF
exit 0
}
command -v tar &>/dev/null || err "tar not found"
command -v gpg &>/dev/null || err "gpg not found (install gnupg)"
SERVICE=0
case "${1:-}" in
-h|--help) usage ;;
--service) SERVICE=1 ;;
"") err "Missing folder path (or use --service)" ;;
*) FOLDER="$1" ;;
esac
if [ "$SERVICE" -eq 1 ]; then
if [ -n "${BACKUP_SERVICE_ROOTS:-}" ]; then
read -r -a roots <<< "$BACKUP_SERVICE_ROOTS"
else
roots=(/srv "$HOME/srv")
fi
idx=0
for root in "${roots[@]}"; do
[ -d "$root" ] || { warn "Root not found: $root"; continue; }
dirs=()
while IFS= read -r d; do
dirs+=("$d")
done < <(find "$root" -maxdepth 1 -mindepth 1 -type d | sort)
[ ${#dirs[@]} -gt 0 ] || { warn "No folders in $root"; continue; }
echo
echo "${root}:"
for d in "${dirs[@]}"; do
idx=$((idx + 1))
names[$idx]="$d"
printf "%2d) %s\n" "$idx" "$d"
done
done
[ "$idx" -gt 0 ] || err "No folders found under: ${roots[*]}"
read -rp "Select folder number: " choice
if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > idx )); then
err "Invalid selection: $choice"
fi
FOLDER="${names[$choice]}"
fi
[ -d "$FOLDER" ] || err "Folder not found: $FOLDER"
NAME="$(basename "$FOLDER")"
DATE="$(date +%Y-%m-%d_%H-%M-%S)"
ARCHIVE="${NAME}_${DATE}.tar.gz"
echo
log "Creating backup..."
echo "Source : $FOLDER"
echo "Output : $ARCHIVE"
sudo tar -czvf "$ARCHIVE" -C "$(dirname "$FOLDER")" "$NAME"
log "Verifying archive..."
tar -tzf "$ARCHIVE" > /dev/null
log "Archive verified"
while true; do
read -s -rp "Enter backup password: " PASS
echo
read -s -rp "Confirm backup password: " CONFIRM
echo
if [ -n "$PASS" ] && [ "$PASS" = "$CONFIRM" ]; then
break
fi
warn "Passwords are empty or do not match — try again"
done
unset CONFIRM
log "Encrypting backup..."
gpg --batch --yes --passphrase "$PASS" --symmetric --cipher-algo AES256 "$ARCHIVE"
rm -f "$ARCHIVE"
ARCHIVE="${ARCHIVE}.gpg"
chmod 600 "$ARCHIVE"
log "Verifying encrypted backup..."
gpg --batch --quiet --passphrase "$PASS" --decrypt "$ARCHIVE" | tar -tzf - > /dev/null
unset PASS
echo
log "Backup completed: $ARCHIVE"