Spaces:
Runtime error
Runtime error
| # | |
| # OpenWA backup. | |
| # | |
| # Captures the load-bearing state needed to restore a working install: | |
| # - main.sqlite β auth (API keys) + audit log, ALWAYS SQLite (see app.module.ts) | |
| # - data store β openwa.sqlite (SQLite) OR a pg_dump (when DATABASE_TYPE=postgres) | |
| # - sessions/ β whatsapp-web.js LocalAuth session data | |
| # - baileys/ β Baileys engine authentication state | |
| # - media/ β locally-stored media (skipped automatically when using S3) | |
| # - plugin-packages/ β installed plugin packages from PLUGINS_DIR | |
| # - plugin-state/ β registry and persisted ctx.storage state under OPENWA_DATA_DIR | |
| # - .env.generated and .api-key β dashboard config and plaintext bootstrap admin key | |
| # | |
| # The previous runbook backed up the wrong file (openwa.db) and omitted main.sqlite, | |
| # so a "successful" backup silently lost every API key and all audit history. | |
| # | |
| # Usage: | |
| # ./scripts/backup.sh | |
| # Environment: | |
| # OPENWA_DATA_DIR data directory (default: ./data) | |
| # BACKUP_DIR where archives are written (default: ./backups) | |
| # DATABASE_TYPE sqlite (default) | postgres | |
| # SESSION_DATA_PATH, BAILEYS_AUTH_DIR, STORAGE_LOCAL_PATH, PLUGINS_DIR | |
| # override the corresponding state directories | |
| # For postgres: DATABASE_URL, or DATABASE_HOST/PORT/USERNAME/PASSWORD/NAME | |
| # | |
| set -euo pipefail | |
| # The archive now contains bootstrap credentials and generated database secrets. Never inherit a | |
| # permissive operator umask for newly-created backup artifacts. | |
| umask 077 | |
| DATA_DIR="${OPENWA_DATA_DIR:-./data}" | |
| BACKUP_DIR="${BACKUP_DIR:-./backups}" | |
| DATABASE_TYPE="${DATABASE_TYPE:-sqlite}" | |
| TIMESTAMP="$(date +%Y%m%d-%H%M%S)" | |
| MAIN_DB="$DATA_DIR/main.sqlite" | |
| DATA_DB="$DATA_DIR/openwa.sqlite" | |
| SESSIONS_DIR="${SESSION_DATA_PATH:-$DATA_DIR/sessions}" | |
| BAILEYS_DIR="${BAILEYS_AUTH_DIR:-$DATA_DIR/baileys}" | |
| MEDIA_DIR="${STORAGE_LOCAL_PATH:-$DATA_DIR/media}" | |
| PLUGIN_PACKAGES_DIR="${PLUGINS_DIR:-./plugins}" | |
| PLUGIN_STATE_DIR="$DATA_DIR/plugins" | |
| GENERATED_ENV="$DATA_DIR/.env.generated" | |
| ADMIN_KEY_FILE="$DATA_DIR/.api-key" | |
| log() { echo "[backup] $*"; } | |
| STAGE="$(mktemp -d)" | |
| trap 'rm -rf "$STAGE"' EXIT | |
| # Online SQLite backup (consistent without stopping the app) when sqlite3 is present, | |
| # else a plain copy with a warning. | |
| backup_sqlite() { | |
| src="$1" | |
| dest="$2" | |
| if [ ! -f "$src" ]; then | |
| log "WARN: $src not found β skipping" | |
| return 0 | |
| fi | |
| if command -v sqlite3 >/dev/null 2>&1; then | |
| sqlite3 "$src" ".backup '$dest'" | |
| else | |
| log "WARN: sqlite3 not found β plain-copying $src (stop the app first for a consistent copy)" | |
| cp "$src" "$dest" | |
| fi | |
| } | |
| log "Backing up auth/audit DB (main.sqlite) β the API-key + audit store" | |
| backup_sqlite "$MAIN_DB" "$STAGE/main.sqlite" | |
| if [ "$DATABASE_TYPE" = "postgres" ]; then | |
| log "Backing up data store via pg_dump" | |
| if ! command -v pg_dump >/dev/null 2>&1; then | |
| log "ERROR: DATABASE_TYPE=postgres but pg_dump is not installed" | |
| exit 1 | |
| fi | |
| if [ -n "${DATABASE_URL:-}" ]; then | |
| pg_dump "$DATABASE_URL" >"$STAGE/database.sql" | |
| else | |
| PGPASSWORD="${DATABASE_PASSWORD:-}" pg_dump \ | |
| -h "${DATABASE_HOST:-localhost}" \ | |
| -p "${DATABASE_PORT:-5432}" \ | |
| -U "${DATABASE_USERNAME:-openwa}" \ | |
| "${DATABASE_NAME:-openwa}" >"$STAGE/database.sql" | |
| fi | |
| else | |
| log "Backing up data store (openwa.sqlite)" | |
| backup_sqlite "$DATA_DB" "$STAGE/openwa.sqlite" | |
| fi | |
| if [ -d "$SESSIONS_DIR" ]; then | |
| log "Backing up whatsapp-web.js sessions" | |
| cp -pR "$SESSIONS_DIR" "$STAGE/sessions" | |
| else | |
| log "WARN: $SESSIONS_DIR not found β skipping sessions" | |
| fi | |
| if [ -d "$BAILEYS_DIR" ]; then | |
| log "Backing up Baileys authentication state" | |
| cp -pR "$BAILEYS_DIR" "$STAGE/baileys" | |
| elif [ "${ENGINE_TYPE:-}" = "baileys" ]; then | |
| log "WARN: ENGINE_TYPE=baileys but $BAILEYS_DIR was not found β restored sessions will require pairing" | |
| fi | |
| if [ -d "$MEDIA_DIR" ]; then | |
| log "Backing up local media" | |
| cp -pR "$MEDIA_DIR" "$STAGE/media" | |
| fi | |
| if [ -d "$PLUGIN_PACKAGES_DIR" ]; then | |
| log "Backing up installed plugin packages" | |
| cp -pR "$PLUGIN_PACKAGES_DIR" "$STAGE/plugin-packages" | |
| fi | |
| if [ -d "$PLUGIN_STATE_DIR" ]; then | |
| log "Backing up plugin registry and persisted state" | |
| cp -pR "$PLUGIN_STATE_DIR" "$STAGE/plugin-state" | |
| fi | |
| if [ -f "$GENERATED_ENV" ]; then | |
| log "Backing up dashboard-generated configuration" | |
| cp -p "$GENERATED_ENV" "$STAGE/.env.generated" | |
| fi | |
| if [ -f "$ADMIN_KEY_FILE" ]; then | |
| log "Backing up plaintext admin key" | |
| cp -p "$ADMIN_KEY_FILE" "$STAGE/.api-key" | |
| fi | |
| mkdir -p "$BACKUP_DIR" | |
| ARCHIVE="$BACKUP_DIR/openwa-backup-$TIMESTAMP.tar.gz" | |
| tar -czf "$ARCHIVE" -C "$STAGE" . | |
| log "Backup complete: $ARCHIVE" | |
| log "SECURITY: this archive can contain database passwords, plugin secrets, and an admin API key; restrict and encrypt it" | |
| log "Contents:" | |
| tar -tzf "$ARCHIVE" | sed 's/^/[backup] /' | |