generated from john/python-template
This commit is contained in:
@@ -1,68 +1,40 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.production.yml}"
|
||||
ENV_FILE="${ENV_FILE:-.env.production}"
|
||||
BACKUP_DIR="${BACKUP_DIR:-${DATABASE_BACKUP_DIR:-./data/backups}}"
|
||||
APP_DATA_BACKUP_DIR="${APP_DATA_BACKUP_DIR:-${BACKUP_DIR}/data}"
|
||||
UPLOADS_BACKUP_DIR="${UPLOADS_BACKUP_DIR:-${BACKUP_DIR}/uploads}"
|
||||
BACKUP_DIR="${BACKUP_DIR:-./data/backups}"
|
||||
RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-14}"
|
||||
SYNOLOGY_BACKUP_DIR="${SYNOLOGY_BACKUP_DIR:-}"
|
||||
UPLOAD_DIR="${UPLOAD_DIR:-/app/uploads}"
|
||||
PROMPT_DIR="${PROMPT_DIR:-/app/prompts}"
|
||||
|
||||
timestamp="$(date -u +%Y%m%d-%H%M%S)"
|
||||
postgres_file="postgres-${timestamp}.dump"
|
||||
config_file="config-${timestamp}.tar.gz"
|
||||
logs_file="logs-${timestamp}.tar.gz"
|
||||
manifest_file="backup-${timestamp}.manifest"
|
||||
|
||||
mkdir -p "${BACKUP_DIR}"
|
||||
mkdir -p "${APP_DATA_BACKUP_DIR}"
|
||||
app_data_backup_abs="$(cd "${APP_DATA_BACKUP_DIR}" && pwd -P)"
|
||||
|
||||
# If SYNOLOGY_BACKUP_DIR wasn't exported in the shell, read it from ENV_FILE.
|
||||
if [ -z "${SYNOLOGY_BACKUP_DIR}" ] && [ -f "${ENV_FILE}" ]; then
|
||||
SYNOLOGY_BACKUP_DIR="$(
|
||||
sed -n 's/^SYNOLOGY_BACKUP_DIR=//p' "${ENV_FILE}" | tail -n 1
|
||||
)"
|
||||
: "${POSTGRES_HOST:=postgres}"
|
||||
: "${POSTGRES_PORT:=5432}"
|
||||
if [ -z "${POSTGRES_DB:-}" ] || [ -z "${POSTGRES_USER:-}" ] || [ -z "${POSTGRES_PASSWORD:-}" ]; then
|
||||
echo "POSTGRES_DB, POSTGRES_USER, and POSTGRES_PASSWORD must be set." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
use_compose_backup=0
|
||||
if command -v docker >/dev/null 2>&1 && [ -S /var/run/docker.sock ] && [ -f "${COMPOSE_FILE}" ]; then
|
||||
use_compose_backup=1
|
||||
fi
|
||||
|
||||
if [ "${use_compose_backup}" -eq 1 ]; then
|
||||
docker compose --env-file "${ENV_FILE}" -f "${COMPOSE_FILE}" exec -T postgres sh -lc \
|
||||
"PGPASSWORD=\"\$POSTGRES_PASSWORD\" pg_dump -U \"\$POSTGRES_USER\" -d \"\$POSTGRES_DB\" -Fc" \
|
||||
> "${BACKUP_DIR}/${postgres_file}"
|
||||
else
|
||||
: "${POSTGRES_HOST:=postgres}"
|
||||
: "${POSTGRES_PORT:=5432}"
|
||||
if [ -z "${POSTGRES_DB:-}" ] || [ -z "${POSTGRES_USER:-}" ] || [ -z "${POSTGRES_PASSWORD:-}" ]; then
|
||||
echo "POSTGRES_DB, POSTGRES_USER, and POSTGRES_PASSWORD must be set for in-container backup mode." >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v pg_dump >/dev/null 2>&1; then
|
||||
echo "pg_dump is not installed in this environment." >&2
|
||||
exit 1
|
||||
fi
|
||||
PGPASSWORD="${POSTGRES_PASSWORD}" pg_dump \
|
||||
-h "${POSTGRES_HOST}" \
|
||||
-p "${POSTGRES_PORT}" \
|
||||
-U "${POSTGRES_USER}" \
|
||||
-d "${POSTGRES_DB}" \
|
||||
-Fc \
|
||||
> "${BACKUP_DIR}/${postgres_file}"
|
||||
if ! command -v pg_dump >/dev/null 2>&1; then
|
||||
echo "pg_dump is not installed in this environment." >&2
|
||||
exit 1
|
||||
fi
|
||||
PGPASSWORD="${POSTGRES_PASSWORD}" pg_dump \
|
||||
-h "${POSTGRES_HOST}" \
|
||||
-p "${POSTGRES_PORT}" \
|
||||
-U "${POSTGRES_USER}" \
|
||||
-d "${POSTGRES_DB}" \
|
||||
-Fc \
|
||||
> "${BACKUP_DIR}/${postgres_file}"
|
||||
|
||||
cat > "${BACKUP_DIR}/${manifest_file}" <<EOF
|
||||
created_at_utc=${timestamp}
|
||||
postgres_dump=${postgres_file}
|
||||
backup_dir=${BACKUP_DIR}
|
||||
synology_backup_dir=${SYNOLOGY_BACKUP_DIR}
|
||||
media_sync_mode=synology-incremental-new-files-only
|
||||
compose_file=${COMPOSE_FILE}
|
||||
env_file=${ENV_FILE}
|
||||
uploads_backup_dir=${BACKUP_DIR}/uploads
|
||||
prompts_backup_dir=${BACKUP_DIR}/prompts
|
||||
EOF
|
||||
|
||||
find "${BACKUP_DIR}" -type f \( \
|
||||
@@ -70,87 +42,26 @@ find "${BACKUP_DIR}" -type f \( \
|
||||
-name 'backup-*.manifest' \
|
||||
\) -mtime +"${RETENTION_DAYS}" -delete
|
||||
|
||||
# Always keep a rolling mirror of app data in BACKUP_DIR, unless that target
|
||||
# is nested under /app/data (which would recursively copy into itself).
|
||||
if [ -d /app/data ]; then
|
||||
case "${app_data_backup_abs}" in
|
||||
/app/data|/app/data/*)
|
||||
echo "Skipping app data mirror because APP_DATA_BACKUP_DIR is nested under /app/data: ${APP_DATA_BACKUP_DIR}"
|
||||
;;
|
||||
*)
|
||||
cp -a /app/data/. "${APP_DATA_BACKUP_DIR}/"
|
||||
;;
|
||||
esac
|
||||
uploads_backup_dir="${BACKUP_DIR}/uploads"
|
||||
mkdir -p "${uploads_backup_dir}/documents" "${uploads_backup_dir}/photos"
|
||||
if [ -f "${UPLOAD_DIR}/homepage.md" ] && [ ! -f "${uploads_backup_dir}/homepage.md" ]; then
|
||||
cp "${UPLOAD_DIR}/homepage.md" "${uploads_backup_dir}/homepage.md"
|
||||
fi
|
||||
if [ -d "${UPLOAD_DIR}/documents" ]; then
|
||||
cp -an "${UPLOAD_DIR}/documents/." "${uploads_backup_dir}/documents/"
|
||||
fi
|
||||
if [ -d "${UPLOAD_DIR}/photos" ]; then
|
||||
cp -an "${UPLOAD_DIR}/photos/." "${uploads_backup_dir}/photos/"
|
||||
fi
|
||||
|
||||
mkdir -p "${UPLOADS_BACKUP_DIR}/documents" "${UPLOADS_BACKUP_DIR}/photos"
|
||||
if [ -f /app/uploads/homepage.md ] && [ ! -f "${UPLOADS_BACKUP_DIR}/homepage.md" ]; then
|
||||
cp /app/uploads/homepage.md "${UPLOADS_BACKUP_DIR}/homepage.md"
|
||||
fi
|
||||
if [ -d /app/uploads/documents ]; then
|
||||
cp -an /app/uploads/documents/. "${UPLOADS_BACKUP_DIR}/documents/"
|
||||
fi
|
||||
if [ -d /app/uploads/photos ]; then
|
||||
cp -an /app/uploads/photos/. "${UPLOADS_BACKUP_DIR}/photos/"
|
||||
fi
|
||||
|
||||
if [ -n "${SYNOLOGY_BACKUP_DIR}" ]; then
|
||||
mkdir -p "${SYNOLOGY_BACKUP_DIR}"
|
||||
|
||||
# 1) Always copy the PostgreSQL dump to Synology.
|
||||
cp "${BACKUP_DIR}/${postgres_file}" "${SYNOLOGY_BACKUP_DIR}/${postgres_file}"
|
||||
|
||||
# 2) Snapshot deployment config and logs to Synology each run.
|
||||
set --
|
||||
[ -f "${ENV_FILE}" ] && set -- "$@" "${ENV_FILE}"
|
||||
[ -f "${COMPOSE_FILE}" ] && set -- "$@" "${COMPOSE_FILE}"
|
||||
[ -f deploy/cloudflared/config.yml ] && set -- "$@" deploy/cloudflared/config.yml
|
||||
if [ "$#" -gt 0 ]; then
|
||||
tar -C . -czf "${SYNOLOGY_BACKUP_DIR}/${config_file}" "$@"
|
||||
else
|
||||
: > "${SYNOLOGY_BACKUP_DIR}/${config_file}"
|
||||
fi
|
||||
|
||||
if [ "${use_compose_backup}" -eq 1 ]; then
|
||||
docker compose --env-file "${ENV_FILE}" -f "${COMPOSE_FILE}" run --rm --no-deps \
|
||||
-v "${SYNOLOGY_BACKUP_DIR}:/backup" \
|
||||
--entrypoint sh app -lc \
|
||||
"if [ -d /app/data/logs ]; then tar -C /app/data/logs -czf /backup/${logs_file} .; else : > /backup/${logs_file}; fi"
|
||||
|
||||
else
|
||||
if [ -d /app/data/logs ]; then
|
||||
tar -C /app/data/logs -czf "${SYNOLOGY_BACKUP_DIR}/${logs_file}" .
|
||||
else
|
||||
: > "${SYNOLOGY_BACKUP_DIR}/${logs_file}"
|
||||
fi
|
||||
|
||||
mkdir -p "${SYNOLOGY_BACKUP_DIR}/uploads"
|
||||
cp -an "${UPLOADS_BACKUP_DIR}/." "${SYNOLOGY_BACKUP_DIR}/uploads/"
|
||||
fi
|
||||
|
||||
cp "${BACKUP_DIR}/${manifest_file}" "${SYNOLOGY_BACKUP_DIR}/${manifest_file}"
|
||||
|
||||
# Keep rolling dump/config/log/manifest snapshots on Synology.
|
||||
find "${SYNOLOGY_BACKUP_DIR}" -maxdepth 1 -type f \( \
|
||||
-name 'postgres-*.dump' -o \
|
||||
-name 'config-*.tar.gz' -o \
|
||||
-name 'logs-*.tar.gz' -o \
|
||||
-name 'backup-*.manifest' \
|
||||
\) -mtime +"${RETENTION_DAYS}" -delete
|
||||
prompts_backup_dir="${BACKUP_DIR}/prompts"
|
||||
mkdir -p "${prompts_backup_dir}"
|
||||
if [ -d "${PROMPT_DIR}" ]; then
|
||||
cp -a "${PROMPT_DIR}/." "${prompts_backup_dir}/"
|
||||
fi
|
||||
|
||||
echo "Created backup set:"
|
||||
echo " ${BACKUP_DIR}/${postgres_file}"
|
||||
echo " ${BACKUP_DIR}/${manifest_file}"
|
||||
echo " ${APP_DATA_BACKUP_DIR}/ (incremental app data mirror)"
|
||||
echo " ${UPLOADS_BACKUP_DIR}/ (incremental uploads mirror)"
|
||||
if [ -n "${SYNOLOGY_BACKUP_DIR}" ]; then
|
||||
echo "Synology artifacts:"
|
||||
echo " ${SYNOLOGY_BACKUP_DIR}/${postgres_file}"
|
||||
echo " ${SYNOLOGY_BACKUP_DIR}/${config_file}"
|
||||
echo " ${SYNOLOGY_BACKUP_DIR}/${logs_file}"
|
||||
echo " ${SYNOLOGY_BACKUP_DIR}/uploads/ (copied from local incremental mirror)"
|
||||
echo " ${SYNOLOGY_BACKUP_DIR}/${manifest_file}"
|
||||
else
|
||||
echo "SYNOLOGY_BACKUP_DIR not set; skipped Synology copy."
|
||||
fi
|
||||
echo " ${uploads_backup_dir}/ (incremental uploads mirror)"
|
||||
echo " ${prompts_backup_dir}/ (prompts mirror)"
|
||||
|
||||
Reference in New Issue
Block a user