#!/bin/bash # Restic backup script for Hördle deployment # Creates a backup snapshot with tags and handles errors gracefully set -e if [ -f "$HOME/.restic-env" ]; then # shellcheck source=/dev/null . "$HOME/.restic-env" fi # Load .env file if present (for Gotify variables) if [ -f ".env" ]; then # shellcheck source=/dev/null set -a . ".env" set +a fi # Extract Gotify variables from docker-compose.yml if not set if [ -z "$GOTIFY_URL" ] && [ -f "docker-compose.yml" ]; then GOTIFY_URL=$(grep -oP 'GOTIFY_URL=\K[^\s]+' docker-compose.yml | head -1 | tr -d '"' | tr -d "'" || echo "") fi if [ -z "$GOTIFY_APP_TOKEN" ] && [ -f "docker-compose.yml" ]; then GOTIFY_APP_TOKEN=$(grep -oP 'GOTIFY_APP_TOKEN=\K[^\s]+' docker-compose.yml | head -1 | tr -d '"' | tr -d "'" || echo "") fi # Function to send Gotify notification send_gotify_notification() { local title="$1" local message="$2" local priority="${3:-5}" # Check if Gotify is configured if [ -z "$GOTIFY_URL" ] || [ -z "$GOTIFY_APP_TOKEN" ]; then echo "⚠️ Gotify not configured (GOTIFY_URL or GOTIFY_APP_TOKEN not set), skipping notification" return 0 fi echo "📢 Sending Gotify notification..." # Send notification (fire and forget, don't fail on error) # Use jq if available for proper JSON encoding, otherwise use simple approach if command -v jq >/dev/null 2>&1; then local json_payload json_payload=$(jq -n \ --arg title "$title" \ --arg message "$message" \ --argjson priority "$priority" \ '{title: $title, message: $message, priority: $priority}') local curl_exit_code=0 curl -sSf -X POST "${GOTIFY_URL}/message?token=${GOTIFY_APP_TOKEN}" \ -H "Content-Type: application/json" \ -d "$json_payload" \ >/dev/null 2>&1 || curl_exit_code=$? if [ $curl_exit_code -eq 0 ]; then echo "✅ Gotify notification sent successfully" else echo "⚠️ Failed to send Gotify notification (curl exit code: $curl_exit_code)" fi else # Fallback: simple JSON encoding (replace " with \" and newlines with \n) local escaped_title escaped_message escaped_title=$(echo "$title" | sed 's/"/\\"/g') escaped_message=$(echo "$message" | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/\\n/g') local curl_exit_code=0 curl -sSf -X POST "${GOTIFY_URL}/message?token=${GOTIFY_APP_TOKEN}" \ -H "Content-Type: application/json" \ -d "{\"title\":\"${escaped_title}\",\"message\":\"${escaped_message}\",\"priority\":${priority}}" \ >/dev/null 2>&1 || curl_exit_code=$? if [ $curl_exit_code -eq 0 ]; then echo "✅ Gotify notification sent successfully" else echo "⚠️ Failed to send Gotify notification (curl exit code: $curl_exit_code)" fi fi } echo "💾 Creating Restic backup..." if ! command -v restic >/dev/null 2>&1; then echo "⚠️ restic not found in PATH, skipping Restic backup" exit 0 fi # Check required environment variables if [ -z "$RESTIC_PASSWORD" ]; then echo "⚠️ RESTIC_PASSWORD not set, skipping Restic backup" exit 0 fi if [ -z "$RESTIC_AUTH_USER" ] || [ -z "$RESTIC_AUTH_PASSWORD" ]; then echo "⚠️ RESTIC_AUTH_USER or RESTIC_AUTH_PASSWORD not set, skipping Restic backup" exit 0 fi # Build repository URL RESTIC_REPO="rest:https://${RESTIC_AUTH_USER}:${RESTIC_AUTH_PASSWORD}@restic.elpatron.me/" # Get current commit hash for tagging CURRENT_COMMIT_SHORT="$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')" CURRENT_DATE="$(date +%Y-%m-%d)" # Export password for restic export RESTIC_PASSWORD # Check if repository exists, initialize if not if ! restic -r "$RESTIC_REPO" snapshots >/dev/null 2>&1; then echo " Initializing Restic repository..." if ! restic -r "$RESTIC_REPO" init >/dev/null 2>&1; then echo "⚠️ Failed to initialize Restic repository, skipping backup" exit 0 fi fi # Create backup with tags # Backup important directories: backups, config files, but exclude node_modules, .git, etc. echo " Creating Restic snapshot..." RESTIC_EXIT_CODE=0 restic -r "$RESTIC_REPO" backup \ --tag deployment \ --tag hoerdle \ --tag "date:${CURRENT_DATE}" \ --tag "commit:${CURRENT_COMMIT_SHORT}" \ --exclude='.git' \ --exclude='node_modules' \ --exclude='.next' \ --exclude='*.log' \ ./backups \ ./data \ ./public/uploads \ docker-compose.yml \ .env \ package.json \ prisma/schema.prisma \ prisma/migrations \ scripts/ || RESTIC_EXIT_CODE=$? if [ $RESTIC_EXIT_CODE -eq 0 ]; then echo "✅ Restic backup completed successfully" # Send success notification send_gotify_notification \ "Hördle Backup: Erfolgreich" \ "Restic Backup wurde erfolgreich abgeschlossen.\nDatum: ${CURRENT_DATE}\nCommit: ${CURRENT_COMMIT_SHORT}" \ 5 exit 0 elif [ $RESTIC_EXIT_CODE -eq 3 ]; then echo "⚠️ Restic backup completed with warnings (some files could not be read), continuing..." # Send warning notification send_gotify_notification \ "Hördle Backup: Mit Warnungen" \ "Restic Backup wurde mit Warnungen abgeschlossen (einige Dateien konnten nicht gelesen werden).\nDatum: ${CURRENT_DATE}\nCommit: ${CURRENT_COMMIT_SHORT}" \ 7 exit 0 else echo "⚠️ Restic backup failed (exit code: $RESTIC_EXIT_CODE), continuing deployment..." # Send error notification send_gotify_notification \ "Hördle Backup: Fehlgeschlagen" \ "Restic Backup ist fehlgeschlagen (Exit Code: ${RESTIC_EXIT_CODE}).\nDatum: ${CURRENT_DATE}\nCommit: ${CURRENT_COMMIT_SHORT}" \ 9 exit 0 fi