#!/usr/bin/env bash set -euo pipefail NAS_ROOT="${NAS_ROOT:-/Volumes/docker/nodedc-platform}" NAS_PLATFORM_DIR="${NAS_PLATFORM_DIR:-/volume1/docker/nodedc-platform/platform}" NAS_TASKER_DIR="${NAS_TASKER_DIR:-/volume1/docker/nodedc-platform/tasker/plane-app}" NAS_GATEWAY_DIR="${NAS_GATEWAY_DIR:-/volume1/docker/nodedc-platform/ops-agents}" BACKUP_ROOT="${BACKUP_ROOT:-${NAS_ROOT}/backups}" TIMESTAMP="${TIMESTAMP:-$(date +%Y%m%d-%H%M%S)}" BACKUP_DIR="${BACKUP_DIR:-${BACKUP_ROOT}/platform-current-${TIMESTAMP}}" DOCKER_BIN="${DOCKER_BIN:-/usr/local/bin/docker}" COMPOSE_FILE="${COMPOSE_FILE:-${NAS_PLATFORM_DIR}/docker-compose.platform-http.yml}" ENV_FILE="${ENV_FILE:-${NAS_PLATFORM_DIR}/.env.synology}" mkdir -p "${BACKUP_DIR}/files/platform" \ "${BACKUP_DIR}/files/launcher" \ "${BACKUP_DIR}/files/authentik" \ "${BACKUP_DIR}/files/tasker" \ "${BACKUP_DIR}/files/ops-agents" \ "${BACKUP_DIR}/files/bim-viewer" rsync_dir() { local source="$1" local destination="$2" if [[ -e "${source}" ]]; then rsync -a --delete "${source}" "${destination}" else echo "skip missing: ${source}" | tee -a "${BACKUP_DIR}/warnings.log" >&2 fi } rsync_file() { local source="$1" local destination="$2" if [[ -f "${source}" ]]; then rsync -a "${source}" "${destination}" else echo "skip missing: ${source}" | tee -a "${BACKUP_DIR}/warnings.log" >&2 fi } rsync_dir "${NAS_ROOT}/launcher/server-storage/" "${BACKUP_DIR}/files/launcher/server-storage/" rsync_dir "${NAS_ROOT}/launcher/uploads/" "${BACKUP_DIR}/files/launcher/uploads/" rsync_dir "${NAS_ROOT}/authentik/custom-templates/" "${BACKUP_DIR}/files/authentik/custom-templates/" rsync_dir "${NAS_ROOT}/platform/authentik/" "${BACKUP_DIR}/files/platform/authentik/" rsync_dir "${NAS_ROOT}/platform/notification-core/" "${BACKUP_DIR}/files/platform/notification-core/" rsync_dir "${NAS_ROOT}/platform/ai-workspace-hub/" "${BACKUP_DIR}/files/platform/ai-workspace-hub/" rsync_dir "${NAS_ROOT}/platform/ai-workspace-assistant/" "${BACKUP_DIR}/files/platform/ai-workspace-assistant/" rsync_dir "${NAS_ROOT}/platform/ontology-core/" "${BACKUP_DIR}/files/platform/ontology-core/" rsync_dir "${NAS_ROOT}/bim-viewer/source/server/data/" "${BACKUP_DIR}/files/bim-viewer/server-data/" rsync_file "${NAS_ROOT}/platform/.env.synology" "${BACKUP_DIR}/files/platform/" rsync_file "${NAS_ROOT}/platform/.env.synology.example" "${BACKUP_DIR}/files/platform/" rsync_file "${NAS_ROOT}/platform/docker-compose.platform-http.yml" "${BACKUP_DIR}/files/platform/" rsync_file "${NAS_ROOT}/platform/Caddyfile.http" "${BACKUP_DIR}/files/platform/" rsync_file "${NAS_ROOT}/tasker/plane-app/.env.synology" "${BACKUP_DIR}/files/tasker/" rsync_file "${NAS_ROOT}/tasker/plane-app/docker-compose.yaml" "${BACKUP_DIR}/files/tasker/" rsync_file "${NAS_ROOT}/tasker/plane-app/docker-compose.synology.override.yml" "${BACKUP_DIR}/files/tasker/" rsync_file "${NAS_ROOT}/ops-agents/.env" "${BACKUP_DIR}/files/ops-agents/" rsync_file "${NAS_ROOT}/ops-agents/.env.synology.example" "${BACKUP_DIR}/files/ops-agents/" rsync_file "${NAS_ROOT}/ops-agents/docker-compose.synology.yml" "${BACKUP_DIR}/files/ops-agents/" rsync_file "${NAS_ROOT}/bim-viewer/source/.env" "${BACKUP_DIR}/files/bim-viewer/" rsync_file "${NAS_ROOT}/bim-viewer/source/.env.synology.example" "${BACKUP_DIR}/files/bim-viewer/" rsync_file "${NAS_ROOT}/bim-viewer/source/docker-compose.beam.yml" "${BACKUP_DIR}/files/bim-viewer/" cat > "${BACKUP_DIR}/manifest.txt" < "${BACKUP_DIR}/run-authentik-db-dump-on-synology.sh" < "\${BACKUP_DIR}/authentik-postgres.dump" sudo "${DOCKER_BIN}" compose \\ --env-file "${ENV_FILE}" \\ -f "${COMPOSE_FILE}" \\ exec -T postgresql-authentik \\ rm -f /tmp/authentik-postgres.dump sudo "${DOCKER_BIN}" compose \\ --env-file "${ENV_FILE}" \\ -f "${COMPOSE_FILE}" \\ cp "\${BACKUP_DIR}/authentik-postgres.dump" postgresql-authentik:/tmp/authentik-postgres.dump sudo "${DOCKER_BIN}" compose \\ --env-file "${ENV_FILE}" \\ -f "${COMPOSE_FILE}" \\ exec -T postgresql-authentik \\ pg_restore --list /tmp/authentik-postgres.dump >/dev/null sudo "${DOCKER_BIN}" compose \\ --env-file "${ENV_FILE}" \\ -f "${COMPOSE_FILE}" \\ exec -T postgresql-authentik \\ rm -f /tmp/authentik-postgres.dump if command -v sha256sum >/dev/null 2>&1; then (cd "\${BACKUP_DIR}" && sha256sum authentik-postgres.dump > SHA256SUMS) else (cd "\${BACKUP_DIR}" && shasum -a 256 authentik-postgres.dump > SHA256SUMS) fi echo "authentik-db-dump-ok: \${BACKUP_DIR}/authentik-postgres.dump" EOF chmod +x "${BACKUP_DIR}/run-authentik-db-dump-on-synology.sh" cat > "${BACKUP_DIR}/run-notification-db-dump-on-synology.sh" < "\${BACKUP_DIR}/notification-postgres.dump" sudo "${DOCKER_BIN}" compose \\ --env-file "${ENV_FILE}" \\ -f "${COMPOSE_FILE}" \\ exec -T notification-postgres \\ rm -f /tmp/notification-postgres.dump sudo "${DOCKER_BIN}" compose \\ --env-file "${ENV_FILE}" \\ -f "${COMPOSE_FILE}" \\ cp "\${BACKUP_DIR}/notification-postgres.dump" notification-postgres:/tmp/notification-postgres.dump sudo "${DOCKER_BIN}" compose \\ --env-file "${ENV_FILE}" \\ -f "${COMPOSE_FILE}" \\ exec -T notification-postgres \\ pg_restore --list /tmp/notification-postgres.dump >/dev/null sudo "${DOCKER_BIN}" compose \\ --env-file "${ENV_FILE}" \\ -f "${COMPOSE_FILE}" \\ exec -T notification-postgres \\ rm -f /tmp/notification-postgres.dump echo "notification-db-dump-ok: \${BACKUP_DIR}/notification-postgres.dump" EOF chmod +x "${BACKUP_DIR}/run-notification-db-dump-on-synology.sh" cat > "${BACKUP_DIR}/run-tasker-db-dump-on-synology.sh" < "\${BACKUP_DIR}/tasker-postgres.dump" sudo "${DOCKER_BIN}" "\${compose_args[@]}" \\ exec -T plane-db \\ rm -f /tmp/tasker-postgres.dump sudo "${DOCKER_BIN}" "\${compose_args[@]}" \\ cp "\${BACKUP_DIR}/tasker-postgres.dump" plane-db:/tmp/tasker-postgres.dump sudo "${DOCKER_BIN}" "\${compose_args[@]}" \\ exec -T plane-db \\ pg_restore --list /tmp/tasker-postgres.dump >/dev/null sudo "${DOCKER_BIN}" "\${compose_args[@]}" \\ exec -T plane-db \\ rm -f /tmp/tasker-postgres.dump echo "tasker-db-dump-ok: \${BACKUP_DIR}/tasker-postgres.dump" EOF chmod +x "${BACKUP_DIR}/run-tasker-db-dump-on-synology.sh" cat > "${BACKUP_DIR}/run-ops-agents-db-dump-on-synology.sh" < "\${BACKUP_DIR}/ops-agents-postgres.dump" sudo "${DOCKER_BIN}" compose \\ --env-file .env \\ -f docker-compose.synology.yml \\ exec -T postgres \\ rm -f /tmp/ops-agents-postgres.dump sudo "${DOCKER_BIN}" compose \\ --env-file .env \\ -f docker-compose.synology.yml \\ cp "\${BACKUP_DIR}/ops-agents-postgres.dump" postgres:/tmp/ops-agents-postgres.dump sudo "${DOCKER_BIN}" compose \\ --env-file .env \\ -f docker-compose.synology.yml \\ exec -T postgres \\ pg_restore --list /tmp/ops-agents-postgres.dump >/dev/null sudo "${DOCKER_BIN}" compose \\ --env-file .env \\ -f docker-compose.synology.yml \\ exec -T postgres \\ rm -f /tmp/ops-agents-postgres.dump echo "ops-agents-db-dump-ok: \${BACKUP_DIR}/ops-agents-postgres.dump" EOF chmod +x "${BACKUP_DIR}/run-ops-agents-db-dump-on-synology.sh" find "${BACKUP_DIR}" -name @eaDir -prune -o -type d -exec chmod 700 {} \; find "${BACKUP_DIR}" -name @eaDir -prune -o -type f -exec chmod 600 {} \; chmod 700 "${BACKUP_DIR}/run-authentik-db-dump-on-synology.sh" chmod 700 "${BACKUP_DIR}/run-notification-db-dump-on-synology.sh" chmod 700 "${BACKUP_DIR}/run-tasker-db-dump-on-synology.sh" chmod 700 "${BACKUP_DIR}/run-ops-agents-db-dump-on-synology.sh" if [[ -x "${DOCKER_BIN}" && "${NAS_ROOT}" == /volume1/* ]]; then "${BACKUP_DIR}/run-authentik-db-dump-on-synology.sh" "${BACKUP_DIR}/run-notification-db-dump-on-synology.sh" else cat <