#!/usr/bin/env bash
# Restores a backup made by backup-server.sh onto a fresh Ubuntu server.
# Usage: ./restore-server.sh BACKUP.tar.gz SERVER_IP
set -euo pipefail

if [ $# -ne 2 ]; then
	echo "Usage: $0 BACKUP.tar.gz SERVER_IP" >&2
	exit 1
fi

archive="$1"
host="$2"
target="root@$host"
remote_script=/root/restore-from-backup.sh

os_field() {
	sed -n "s/^$1=\"\{0,1\}\([^\"]*\)\"\{0,1\}$/\1/p"
}

echo "Reading OS version from $archive..."
backup_release=$(tar -xzOf "$archive" --occurrence=1 usr/lib/os-release)
target_release=$(ssh -n "$target" cat /usr/lib/os-release)

backup_os="$(os_field ID <<<"$backup_release") $(os_field VERSION_ID <<<"$backup_release")"
target_os="$(os_field ID <<<"$target_release") $(os_field VERSION_ID <<<"$target_release")"
target_arch=$(ssh -n "$target" uname -m)
backup_lib_dir=$(tar -tzf "$archive" 2>/dev/null | grep -m1 -xF "usr/lib/$target_arch-linux-gnu/" || true)

echo "Backup: $backup_os"
echo "Target: $target_os $target_arch"

if [ "$backup_os" != "$target_os" ]; then
	echo "Stop: the target must run the same Ubuntu version as the backup." >&2
	exit 1
fi
if [ -z "$backup_lib_dir" ]; then
	echo "Stop: the target CPU architecture does not match the backup." >&2
	exit 1
fi

echo
echo "This overwrites the system on $host with the backup. It cannot be undone."
read -r -p "Type the server address ($host) to continue: " answer
if [ "$answer" != "$host" ]; then
	echo "Cancelled."
	exit 1
fi

ssh "$target" "cat > $remote_script && chmod 700 $remote_script" <<'REMOTE'
set -euo pipefail
cd /

dump_dir=/root/db-dumps
saved_keys=/root/authorized_keys.pre-restore

if [ -f /root/.ssh/authorized_keys ]; then
	cp /root/.ssh/authorized_keys "$saved_keys"
fi

echo "Extracting files..."
tar_failed=""
tar -xzpf - -C / --numeric-owner --anchored \
	--exclude=boot \
	--exclude=etc/fstab \
	--exclude=etc/crypttab \
	--exclude=etc/netplan \
	--exclude=etc/network \
	--exclude=etc/resolv.conf \
	--exclude=etc/cloud \
	--exclude=var/lib/cloud \
	--exclude=etc/machine-id \
	--exclude='etc/ssh/ssh_host_*' \
	|| tar_failed=1
if [ -f "$saved_keys" ]; then
	mkdir -p -m 700 /root/.ssh
	cat "$saved_keys" >> /root/.ssh/authorized_keys
	sort -u -o /root/.ssh/authorized_keys /root/.ssh/authorized_keys
	chmod 600 /root/.ssh/authorized_keys
	rm -f "$saved_keys"
fi

systemctl daemon-reload

if [ -f "$dump_dir/mysql.sql" ]; then
	echo "Restoring MySQL..."
	systemctl stop mariadb mysql 2>/dev/null || true
	rm -rf /var/lib/mysql
	install -d -m 700 -o mysql -g mysql /var/lib/mysql
	if command -v mariadb-install-db >/dev/null; then
		mariadb-install-db --user=mysql --datadir=/var/lib/mysql >/dev/null
		systemctl start mariadb
	else
		mysqld --initialize-insecure --user=mysql --datadir=/var/lib/mysql
		systemctl start mysql
	fi
	{ cat "$dump_dir/mysql.sql"; echo 'FLUSH PRIVILEGES;'; } | mysql --no-defaults -u root
fi

if [ -f "$dump_dir/postgres.sql" ]; then
	echo "Restoring PostgreSQL..."
	systemctl stop postgresql 2>/dev/null || true
	for conf_dir in /etc/postgresql/*/*/; do
		version=$(basename "$(dirname "$conf_dir")")
		cluster=$(basename "$conf_dir")
		data_dir="/var/lib/postgresql/$version/$cluster"
		rm -rf "$data_dir"
		install -d -m 700 -o postgres -g postgres "/var/lib/postgresql/$version" "$data_dir"
		runuser -u postgres -- "/usr/lib/postgresql/$version/bin/initdb" -D "$data_dir" \
			--auth-local=peer --auth-host=scram-sha-256 >/dev/null
	done
	systemctl start postgresql
	runuser -u postgres -- psql -X -q < "$dump_dir/postgres.sql" >/dev/null
fi

rm -rf "$dump_dir" "$0"
if [ -n "$tar_failed" ]; then
	echo "Warning: some files were not restored. See the tar errors above."
fi
echo "Restore finished. Rebooting."
systemctl reboot
REMOTE

echo "Streaming backup to $host. This can take a while..."
ssh "$target" "bash $remote_script" < "$archive"

echo "Done. The server is rebooting. Wait a minute, then check your sites."
