mirror of
https://github.com/FunnyWolf/agentic-soc-platform.git
synced 2026-08-22 13:12:56 +02:00
177 lines
5.0 KiB
Bash
Executable File
177 lines
5.0 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
umask 077
|
|
|
|
PROJECT_NAME="asp-compose"
|
|
ARCHIVE_IMAGE="alpine:3.22"
|
|
FORMAT_VERSION="asp-full-backup-v1"
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: ./scripts/restore.sh <backup-directory>
|
|
|
|
Replaces the current ASP deployment files and Docker named volumes with a
|
|
stopped full backup created by backup.sh.
|
|
EOF
|
|
}
|
|
|
|
if [ "$#" -ne 1 ]; then
|
|
usage >&2
|
|
exit 1
|
|
fi
|
|
|
|
case "$1" in
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
esac
|
|
|
|
for command_name in awk basename cp docker grep mktemp rm sha256sum tar; do
|
|
if ! command -v "$command_name" >/dev/null 2>&1; then
|
|
echo "Required command is not available: $command_name" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
deployment_dir="$(pwd -P)"
|
|
if [ "$(basename "$deployment_dir")" != "$PROJECT_NAME" ]; then
|
|
echo "Run this script from a deployment directory named $PROJECT_NAME." >&2
|
|
exit 1
|
|
fi
|
|
if [ ! -f compose.yaml ]; then
|
|
echo "Run this script from an ASP Compose release directory." >&2
|
|
exit 1
|
|
fi
|
|
if [ -n "${COMPOSE_PROJECT_NAME:-}" ] ||
|
|
{ [ -f .env ] && grep -q '^COMPOSE_PROJECT_NAME=' .env; }; then
|
|
echo "Custom COMPOSE_PROJECT_NAME values are not supported by this restore script." >&2
|
|
exit 1
|
|
fi
|
|
if grep -q '^name:' compose.yaml ||
|
|
{ [ -f compose.override.yaml ] && grep -q '^name:' compose.override.yaml; }; then
|
|
echo "Custom top-level Compose project names are not supported by this restore script." >&2
|
|
exit 1
|
|
fi
|
|
|
|
backup_dir="$1"
|
|
if [ ! -d "$backup_dir" ]; then
|
|
echo "Backup directory does not exist: $backup_dir" >&2
|
|
exit 1
|
|
fi
|
|
backup_dir="$(cd "$backup_dir" && pwd -P)"
|
|
|
|
for file_name in manifest.txt SHA256SUMS files.tar.gz volumes.tar.gz; do
|
|
if [ ! -f "$backup_dir/$file_name" ]; then
|
|
echo "Backup file is missing: $backup_dir/$file_name" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
if ! grep -qx "format=$FORMAT_VERSION" "$backup_dir/manifest.txt"; then
|
|
echo "Unsupported or incomplete backup format." >&2
|
|
exit 1
|
|
fi
|
|
if ! grep -qx "project_name=$PROJECT_NAME" "$backup_dir/manifest.txt"; then
|
|
echo "The backup does not belong to the $PROJECT_NAME Compose project." >&2
|
|
exit 1
|
|
fi
|
|
|
|
(
|
|
cd "$backup_dir"
|
|
sha256sum -c SHA256SUMS
|
|
)
|
|
tar -tzf "$backup_dir/files.tar.gz" >/dev/null
|
|
tar -tzf "$backup_dir/volumes.tar.gz" >/dev/null
|
|
|
|
if ! tar -tzf "$backup_dir/files.tar.gz" |
|
|
awk '
|
|
/^\// || /(^|\/)\.\.(\/|$)/ { invalid = 1 }
|
|
END { exit invalid ? 1 : 0 }
|
|
'
|
|
then
|
|
echo "The files archive contains an unsafe path." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! tar -tzf "$backup_dir/volumes.tar.gz" |
|
|
awk '
|
|
/^\// || /(^|\/)\.\.(\/|$)/ { invalid = 1 }
|
|
!/^(postgres-data|redis-data|rustfs-data|custom-python-packages|static-files)(\/|$)/ { invalid = 1 }
|
|
END { exit invalid ? 1 : 0 }
|
|
'
|
|
then
|
|
echo "The volumes archive contains an unexpected or unsafe path." >&2
|
|
exit 1
|
|
fi
|
|
for directory in postgres-data redis-data rustfs-data custom-python-packages static-files; do
|
|
if ! tar -tzf "$backup_dir/volumes.tar.gz" | grep -Eq "^${directory}(/|$)"; then
|
|
echo "The volumes archive is missing: $directory" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
stage_dir="$(mktemp -d)"
|
|
cleanup() {
|
|
rm -rf "$stage_dir"
|
|
}
|
|
trap cleanup EXIT
|
|
trap 'exit 1' HUP INT TERM
|
|
|
|
tar -xzf "$backup_dir/files.tar.gz" -C "$stage_dir"
|
|
|
|
if [ ! -f "$stage_dir/.env" ] ||
|
|
[ ! -f "$stage_dir/compose.yaml" ] ||
|
|
[ ! -x "$stage_dir/scripts/doctor.sh" ]; then
|
|
echo "The files archive is not a complete ASP Compose deployment." >&2
|
|
exit 1
|
|
fi
|
|
if grep -q '^COMPOSE_PROJECT_NAME=' "$stage_dir/.env"; then
|
|
echo "The backup uses an unsupported custom COMPOSE_PROJECT_NAME." >&2
|
|
exit 1
|
|
fi
|
|
if grep -q '^name:' "$stage_dir/compose.yaml" ||
|
|
{ [ -f "$stage_dir/compose.override.yaml" ] &&
|
|
grep -q '^name:' "$stage_dir/compose.override.yaml"; }; then
|
|
echo "The backup uses an unsupported custom top-level Compose project name." >&2
|
|
exit 1
|
|
fi
|
|
|
|
(
|
|
cd "$stage_dir"
|
|
docker compose config --quiet
|
|
)
|
|
|
|
if ! docker image inspect "$ARCHIVE_IMAGE" >/dev/null 2>&1; then
|
|
docker pull "$ARCHIVE_IMAGE"
|
|
fi
|
|
|
|
echo "Stopping the current ASP deployment..."
|
|
if [ -f .env ]; then
|
|
docker compose down --remove-orphans
|
|
fi
|
|
|
|
rm -f .env .env.example README.md README.zh.md compose.yaml compose.override.yaml
|
|
rm -rf scripts custom certs logs
|
|
cp -a "$stage_dir"/. "$deployment_dir"/
|
|
|
|
docker run --rm \
|
|
-v "${PROJECT_NAME}_postgres-data:/volumes/postgres-data" \
|
|
-v "${PROJECT_NAME}_redis-data:/volumes/redis-data" \
|
|
-v "${PROJECT_NAME}_rustfs-data:/volumes/rustfs-data" \
|
|
-v "${PROJECT_NAME}_custom-python-packages:/volumes/custom-python-packages" \
|
|
-v "${PROJECT_NAME}_static-files:/volumes/static-files" \
|
|
-v "$backup_dir:/backup:ro" \
|
|
"$ARCHIVE_IMAGE" \
|
|
sh -euc '
|
|
for directory in postgres-data redis-data rustfs-data custom-python-packages static-files; do
|
|
find "/volumes/$directory" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
|
|
done
|
|
tar -xzf /backup/volumes.tar.gz -C /volumes
|
|
'
|
|
|
|
docker compose up -d
|
|
./scripts/doctor.sh
|
|
|
|
echo "ASP restored from: $backup_dir"
|