Initial backup skill implementation

This commit is contained in:
Azat
2026-02-02 23:44:24 +01:00
commit 5a92cac546
5 changed files with 633 additions and 0 deletions

91
scripts/autorun.sh Normal file
View File

@@ -0,0 +1,91 @@
#!/bin/bash
set -e
BACKUP_TARGET="${BACKUP_TARGET:-/backups}"
BACKUP_PASSWORD="${BACKUP_PASSWORD:-}"
# Idempotent restic installation
install_restic() {
if command -v restic &>/dev/null; then
echo "restic already installed: $(restic version)"
return 0
fi
echo "Installing restic..."
# Install from official repository
apt-get update
apt-get install -y restic
# Update to latest version
restic self-update 2>/dev/null || true
echo "restic installed: $(restic version)"
}
# Install cron for scheduled backups
install_cron() {
if command -v cron &>/dev/null; then
echo "cron already installed"
return 0
fi
echo "Installing cron..."
apt-get update
apt-get install -y cron
echo "cron installed"
}
# Setup directories
setup_dirs() {
# Create local backup directory if using local target
if [[ "$BACKUP_TARGET" == /* ]]; then
mkdir -p "$BACKUP_TARGET"
echo "Local backup directory: $BACKUP_TARGET"
fi
# Create status directory
mkdir -p /run/vibestack
}
# Initialize restic repository
init_repository() {
if [ -z "$BACKUP_PASSWORD" ]; then
echo "WARNING: BACKUP_PASSWORD not set - skipping repository init"
echo "Set BACKUP_PASSWORD to enable backups"
return 0
fi
export RESTIC_PASSWORD="$BACKUP_PASSWORD"
export RESTIC_REPOSITORY="$BACKUP_TARGET"
# Setup cloud credentials if provided
if [ -n "$BACKUP_S3_ACCESS_KEY" ]; then
export AWS_ACCESS_KEY_ID="$BACKUP_S3_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="$BACKUP_S3_SECRET_KEY"
fi
if [ -n "$BACKUP_B2_ACCOUNT_ID" ]; then
export B2_ACCOUNT_ID="$BACKUP_B2_ACCOUNT_ID"
export B2_ACCOUNT_KEY="$BACKUP_B2_ACCOUNT_KEY"
fi
# Check if repository exists
if restic cat config &>/dev/null; then
echo "Backup repository already initialized"
return 0
fi
echo "Initializing backup repository at $BACKUP_TARGET..."
restic init
echo "Backup repository initialized"
}
install_restic
install_cron
setup_dirs
init_repository
echo "Backup setup complete"

146
scripts/backup.sh Normal file
View File

@@ -0,0 +1,146 @@
#!/bin/bash
set -e
BACKUP_TARGET="${BACKUP_TARGET:-/backups}"
BACKUP_PASSWORD="${BACKUP_PASSWORD:-}"
BACKUP_RETENTION="${BACKUP_RETENTION:-7d}"
# Paths to backup
BACKUP_PATHS=(
"/data/postgres"
"/data/redis"
"/data/duckdb"
"/data/loki"
"/data/caddy"
"/personalities"
"/workspaces"
)
# Validate password
if [ -z "$BACKUP_PASSWORD" ]; then
echo "ERROR: BACKUP_PASSWORD is required"
exit 1
fi
# Setup restic environment
export RESTIC_PASSWORD="$BACKUP_PASSWORD"
export RESTIC_REPOSITORY="$BACKUP_TARGET"
# Setup cloud credentials if provided
[ -n "$BACKUP_S3_ACCESS_KEY" ] && export AWS_ACCESS_KEY_ID="$BACKUP_S3_ACCESS_KEY"
[ -n "$BACKUP_S3_SECRET_KEY" ] && export AWS_SECRET_ACCESS_KEY="$BACKUP_S3_SECRET_KEY"
[ -n "$BACKUP_B2_ACCOUNT_ID" ] && export B2_ACCOUNT_ID="$BACKUP_B2_ACCOUNT_ID"
[ -n "$BACKUP_B2_ACCOUNT_KEY" ] && export B2_ACCOUNT_KEY="$BACKUP_B2_ACCOUNT_KEY"
START_TIME=$(date +%s)
echo "=== VibeStack Backup ==="
echo "Time: $(date -Iseconds)"
echo "Target: $BACKUP_TARGET"
echo ""
# Pre-backup: dump PostgreSQL if running
pre_backup_postgres() {
if command -v pg_dump &>/dev/null && [ -d "/data/postgres" ]; then
echo "Creating PostgreSQL dump..."
# Source postgres env if available
[ -f /run/vibestack/postgres.env ] && source /run/vibestack/postgres.env
local pg_user="${POSTGRES_USER:-vibestack}"
local pg_db="${POSTGRES_DB:-vibestack}"
local dump_file="/data/postgres/backup.sql"
if pg_dump -U "$pg_user" -d "$pg_db" -f "$dump_file" 2>/dev/null; then
echo " PostgreSQL dump created: $dump_file"
else
echo " PostgreSQL dump skipped (database not running or accessible)"
fi
fi
}
# Pre-backup: trigger Redis BGSAVE if running
pre_backup_redis() {
if command -v redis-cli &>/dev/null; then
echo "Triggering Redis BGSAVE..."
# Source redis env if available
[ -f /run/vibestack/redis.env ] && source /run/vibestack/redis.env
local redis_pass="${REDIS_PASSWORD:-}"
local auth_args=""
[ -n "$redis_pass" ] && auth_args="-a $redis_pass"
if redis-cli $auth_args BGSAVE 2>/dev/null; then
# Wait for save to complete
sleep 2
echo " Redis BGSAVE triggered"
else
echo " Redis BGSAVE skipped (not running)"
fi
fi
}
# Run pre-backup hooks
echo "Running pre-backup hooks..."
pre_backup_postgres
pre_backup_redis
echo ""
# Build list of paths that exist
existing_paths=()
for path in "${BACKUP_PATHS[@]}"; do
if [ -e "$path" ]; then
existing_paths+=("$path")
echo "Including: $path"
fi
done
# Allow overriding with specific path
if [ -n "$1" ] && [ -e "$1" ]; then
existing_paths=("$1")
echo "Backing up only: $1"
fi
if [ ${#existing_paths[@]} -eq 0 ]; then
echo "WARNING: No paths to backup"
exit 0
fi
echo ""
echo "Starting backup..."
# Run restic backup
BACKUP_OUTPUT=$(restic backup "${existing_paths[@]}" --json 2>&1 | tail -1)
# Parse results
SNAPSHOT_ID=$(echo "$BACKUP_OUTPUT" | jq -r '.snapshot_id // empty' 2>/dev/null || echo "")
BYTES_ADDED=$(echo "$BACKUP_OUTPUT" | jq -r '.data_added // 0' 2>/dev/null || echo "0")
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
echo ""
echo "Backup complete!"
echo " Snapshot: ${SNAPSHOT_ID:-unknown}"
echo " Duration: ${DURATION}s"
echo " Data added: $BYTES_ADDED bytes"
# Apply retention policy
echo ""
echo "Applying retention policy: keep within $BACKUP_RETENTION..."
restic forget --keep-within "$BACKUP_RETENTION" --prune
# Write status
cat > /run/vibestack/backup-status.json << EOF
{
"status": "idle",
"last_backup": "$(date -Iseconds)",
"last_status": "success",
"snapshot_id": "$SNAPSHOT_ID",
"duration_seconds": $DURATION,
"bytes_added": $BYTES_ADDED
}
EOF
echo ""
echo "=== Backup Complete ==="

160
scripts/restore.sh Normal file
View File

@@ -0,0 +1,160 @@
#!/bin/bash
set -e
BACKUP_TARGET="${BACKUP_TARGET:-/backups}"
BACKUP_PASSWORD="${BACKUP_PASSWORD:-}"
# Validate password
if [ -z "$BACKUP_PASSWORD" ]; then
echo "ERROR: BACKUP_PASSWORD is required"
exit 1
fi
# Setup restic environment
export RESTIC_PASSWORD="$BACKUP_PASSWORD"
export RESTIC_REPOSITORY="$BACKUP_TARGET"
# Setup cloud credentials if provided
[ -n "$BACKUP_S3_ACCESS_KEY" ] && export AWS_ACCESS_KEY_ID="$BACKUP_S3_ACCESS_KEY"
[ -n "$BACKUP_S3_SECRET_KEY" ] && export AWS_SECRET_ACCESS_KEY="$BACKUP_S3_SECRET_KEY"
[ -n "$BACKUP_B2_ACCOUNT_ID" ] && export B2_ACCOUNT_ID="$BACKUP_B2_ACCOUNT_ID"
[ -n "$BACKUP_B2_ACCOUNT_KEY" ] && export B2_ACCOUNT_KEY="$BACKUP_B2_ACCOUNT_KEY"
usage() {
cat << EOF
VibeStack Restore Tool
Usage: restore.sh [OPTIONS]
Options:
--list List available snapshots
--latest Restore from latest snapshot
--snapshot ID Restore from specific snapshot
--path PATH Restore only specific path
--target DIR Restore to specific directory (default: /)
--dry-run Show what would be restored without doing it
--help Show this help message
Examples:
# List all snapshots
restore.sh --list
# Restore everything from latest snapshot
restore.sh --latest
# Restore specific path from latest
restore.sh --latest --path /data/postgres
# Restore from specific snapshot
restore.sh --snapshot abc123def
# Restore to different directory
restore.sh --latest --target /tmp/restore
# Preview restore
restore.sh --latest --dry-run
EOF
}
# Parse arguments
SNAPSHOT=""
RESTORE_PATH=""
TARGET_DIR="/"
DRY_RUN=""
LIST_ONLY=""
while [[ $# -gt 0 ]]; do
case $1 in
--list)
LIST_ONLY="true"
shift
;;
--latest)
SNAPSHOT="latest"
shift
;;
--snapshot)
SNAPSHOT="$2"
shift 2
;;
--path)
RESTORE_PATH="$2"
shift 2
;;
--target)
TARGET_DIR="$2"
shift 2
;;
--dry-run)
DRY_RUN="--dry-run"
shift
;;
--help)
usage
exit 0
;;
*)
echo "Unknown option: $1"
usage
exit 1
;;
esac
done
echo "=== VibeStack Restore ==="
echo "Repository: $BACKUP_TARGET"
echo ""
# List snapshots
if [ "$LIST_ONLY" = "true" ]; then
echo "Available snapshots:"
echo ""
restic snapshots
exit 0
fi
# Validate snapshot
if [ -z "$SNAPSHOT" ]; then
echo "ERROR: Specify --latest or --snapshot ID"
echo ""
usage
exit 1
fi
# Show what we're restoring
echo "Snapshot: $SNAPSHOT"
[ -n "$RESTORE_PATH" ] && echo "Path: $RESTORE_PATH"
echo "Target: $TARGET_DIR"
[ -n "$DRY_RUN" ] && echo "Mode: DRY RUN"
echo ""
# Build restore command
restore_args=("restore" "$SNAPSHOT" "--target" "$TARGET_DIR")
[ -n "$RESTORE_PATH" ] && restore_args+=("--include" "$RESTORE_PATH")
[ -n "$DRY_RUN" ] && restore_args+=("$DRY_RUN")
# Confirm unless dry run
if [ -z "$DRY_RUN" ]; then
echo "WARNING: This will overwrite existing files!"
echo ""
read -p "Continue? [y/N] " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted"
exit 0
fi
fi
echo "Restoring..."
restic "${restore_args[@]}"
# Post-restore for PostgreSQL
if [ -z "$DRY_RUN" ] && [ -f "$TARGET_DIR/data/postgres/backup.sql" ]; then
echo ""
echo "PostgreSQL dump found at $TARGET_DIR/data/postgres/backup.sql"
echo "To restore the database, run:"
echo " psql -U vibestack -d vibestack -f $TARGET_DIR/data/postgres/backup.sql"
fi
echo ""
echo "=== Restore Complete ==="

67
scripts/run.sh Normal file
View File

@@ -0,0 +1,67 @@
#!/bin/bash
set -e
BACKUP_SCHEDULE="${BACKUP_SCHEDULE:-0 3 * * *}"
SKILL_DIR="$(dirname "$(dirname "$0")")"
# Validate password is set
if [ -z "$BACKUP_PASSWORD" ]; then
echo "ERROR: BACKUP_PASSWORD is required"
echo "Set BACKUP_PASSWORD environment variable to enable backups"
exit 1
fi
# Setup cron job for scheduled backups
setup_cron() {
local cron_file="/etc/cron.d/vibestack-backup"
local backup_script="$SKILL_DIR/scripts/backup.sh"
# Build environment exports for cron
local env_exports=""
env_exports+="BACKUP_PASSWORD='$BACKUP_PASSWORD' "
env_exports+="BACKUP_TARGET='${BACKUP_TARGET:-/backups}' "
env_exports+="BACKUP_RETENTION='${BACKUP_RETENTION:-7d}' "
[ -n "$BACKUP_S3_ACCESS_KEY" ] && env_exports+="BACKUP_S3_ACCESS_KEY='$BACKUP_S3_ACCESS_KEY' "
[ -n "$BACKUP_S3_SECRET_KEY" ] && env_exports+="BACKUP_S3_SECRET_KEY='$BACKUP_S3_SECRET_KEY' "
[ -n "$BACKUP_B2_ACCOUNT_ID" ] && env_exports+="BACKUP_B2_ACCOUNT_ID='$BACKUP_B2_ACCOUNT_ID' "
[ -n "$BACKUP_B2_ACCOUNT_KEY" ] && env_exports+="BACKUP_B2_ACCOUNT_KEY='$BACKUP_B2_ACCOUNT_KEY' "
# Create cron job
cat > "$cron_file" << EOF
# VibeStack automated backup
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
$BACKUP_SCHEDULE root $env_exports $backup_script >> /var/log/vibestack-backup.log 2>&1
EOF
chmod 644 "$cron_file"
echo "Cron job configured: $BACKUP_SCHEDULE"
}
# Write initial status
write_status() {
cat > /run/vibestack/backup-status.json << EOF
{
"status": "running",
"schedule": "$BACKUP_SCHEDULE",
"target": "${BACKUP_TARGET:-/backups}",
"last_backup": null,
"last_status": null
}
EOF
}
setup_cron
write_status
echo "Starting cron daemon for scheduled backups..."
echo "Schedule: $BACKUP_SCHEDULE"
echo "Target: ${BACKUP_TARGET:-/backups}"
echo ""
echo "Manual backup: $SKILL_DIR/scripts/backup.sh"
echo "Manual restore: $SKILL_DIR/scripts/restore.sh --list"
# Start cron in foreground
exec cron -f