v4 #1
110
scripts/backup-db.sh
Executable file
110
scripts/backup-db.sh
Executable file
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env bash
|
||||
# -----------------------------------------------------------------------------
|
||||
# Encoach / Odoo 19 — full database backup
|
||||
#
|
||||
# Produces an Odoo-format .zip backup that can be restored on the live VPS via
|
||||
# /web/database/manager (the "Restore Database" button) or via the matching
|
||||
# /web/database/restore HTTP endpoint.
|
||||
#
|
||||
# The zip layout matches what Odoo writes itself:
|
||||
#
|
||||
# <db>.zip
|
||||
# ├── dump.sql # plain-text pg_dump (--no-owner --format=p)
|
||||
# ├── manifest.json # Odoo metadata: db, version, modules
|
||||
# └── filestore/ # contents of <data_dir>/filestore/<db>/
|
||||
#
|
||||
# We do this manually (instead of POST /web/database/backup) because Odoo's
|
||||
# subprocess PATH on this host doesn't always contain pg_dump 18, which makes
|
||||
# the HTTP endpoint return "Command pg_dump not found".
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/backup-db.sh # backs up encoach_v2 → ./backups/
|
||||
# DB=mydb OUT_DIR=/tmp ./scripts/backup-db.sh
|
||||
#
|
||||
# The output filename is encoach_v2_YYYYMMDD_HHMMSS.zip (UTC-local clock).
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PGBIN="${PGBIN:-/Users/yamenahmad/micromamba/envs/odoo19/bin}"
|
||||
DB="${DB:-encoach_v2}"
|
||||
DB_HOST="${DB_HOST:-localhost}"
|
||||
DB_PORT="${DB_PORT:-5432}"
|
||||
DB_USER="${DB_USER:-yamenahmad}"
|
||||
DATA_DIR="${DATA_DIR:-/tmp/odoo-data}"
|
||||
OUT_DIR="${OUT_DIR:-$(cd "$(dirname "$0")/.." && pwd)/backups}"
|
||||
|
||||
if [[ ! -x "$PGBIN/pg_dump" ]]; then
|
||||
echo "ERROR: pg_dump not found at $PGBIN/pg_dump" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
TS=$(date +%Y%m%d_%H%M%S)
|
||||
OUT="$OUT_DIR/${DB}_${TS}.zip"
|
||||
WORK=$(mktemp -d -t encoach_backup_XXXXXX)
|
||||
trap 'rm -rf "$WORK"' EXIT
|
||||
|
||||
echo "[1/4] pg_dump ${DB} -> ${WORK}/dump.sql"
|
||||
"$PGBIN/pg_dump" \
|
||||
--host="$DB_HOST" --port="$DB_PORT" --username="$DB_USER" \
|
||||
--no-owner --no-privileges --format=p \
|
||||
--dbname="$DB" \
|
||||
> "$WORK/dump.sql"
|
||||
|
||||
DUMP_BYTES=$(wc -c < "$WORK/dump.sql" | tr -d ' ')
|
||||
echo " dump.sql = $((DUMP_BYTES / 1024)) KB"
|
||||
|
||||
echo "[2/4] copy filestore -> ${WORK}/filestore/"
|
||||
if [[ -d "$DATA_DIR/filestore/$DB" ]]; then
|
||||
mkdir -p "$WORK/filestore"
|
||||
# cp -R preserves symlinks/timestamps; the leading /. copies *contents*
|
||||
cp -R "$DATA_DIR/filestore/$DB/." "$WORK/filestore/"
|
||||
FS_BYTES=$(du -sk "$WORK/filestore" | awk '{print $1}')
|
||||
echo " filestore = $((FS_BYTES / 1024)) MB"
|
||||
else
|
||||
echo " WARN: no filestore at $DATA_DIR/filestore/$DB (continuing without it)"
|
||||
fi
|
||||
|
||||
echo "[3/4] write manifest.json"
|
||||
PG_VERSION=$("$PGBIN/psql" -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB" -At -c "SHOW server_version;")
|
||||
ODOO_VERSION=$("$PGBIN/psql" -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB" -At -c "SELECT latest_version FROM ir_module_module WHERE name='base';")
|
||||
MAJOR=$(echo "$ODOO_VERSION" | cut -d. -f1-2)
|
||||
|
||||
# Build modules JSON object: {"name": "version", ...} for installed modules.
|
||||
MODULES_JSON=$("$PGBIN/psql" -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB" -At -c \
|
||||
"SELECT json_object_agg(name, latest_version) FROM ir_module_module WHERE state='installed';")
|
||||
|
||||
cat > "$WORK/manifest.json" <<EOF
|
||||
{
|
||||
"odoo_dump": "1",
|
||||
"db_name": "${DB}",
|
||||
"version": "${MAJOR}",
|
||||
"version_info": [${MAJOR%%.*}, ${MAJOR#*.}, 0, "final", 0, ""],
|
||||
"major_version": "${MAJOR}",
|
||||
"pg_version": "${PG_VERSION}",
|
||||
"modules": ${MODULES_JSON}
|
||||
}
|
||||
EOF
|
||||
|
||||
echo " odoo=${MAJOR} pg=${PG_VERSION} modules=$(echo "$MODULES_JSON" | tr ',' '\n' | wc -l | tr -d ' ')"
|
||||
|
||||
echo "[4/4] zip -> ${OUT}"
|
||||
( cd "$WORK" && zip -qr "$OUT" dump.sql manifest.json filestore 2>/dev/null || \
|
||||
zip -qr "$OUT" dump.sql manifest.json )
|
||||
|
||||
OUT_BYTES=$(wc -c < "$OUT" | tr -d ' ')
|
||||
echo
|
||||
echo "OK: $OUT"
|
||||
echo " size = $((OUT_BYTES / 1024 / 1024)) MB"
|
||||
echo
|
||||
echo "Restore on live VPS:"
|
||||
echo " 1) Open https://<vps>/web/database/manager"
|
||||
echo " 2) Click 'Restore Database', enter the master password,"
|
||||
echo " pick this .zip, set the destination DB name (e.g. encoach_v2),"
|
||||
echo " and submit."
|
||||
echo
|
||||
echo "Or via curl:"
|
||||
echo " curl -F 'master_pwd=<MASTER>' -F 'name=encoach_v2' -F 'copy=true' \\"
|
||||
echo " -F 'backup_file=@${OUT}' https://<vps>/web/database/restore"
|
||||
Reference in New Issue
Block a user