Add WG/AWG client config ZIP export and backup restore.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
orohi
2026-07-26 05:54:32 +03:00
co-authored by Cursor
parent f2311c0795
commit 2b28892a9b
5 changed files with 306 additions and 5 deletions
+73
View File
@@ -168,3 +168,76 @@ printf '%s\n' "$archive"
path = (out or '').strip().splitlines()[-1] if (out or '').strip() else ''
name = path.rsplit('/', 1)[-1] if path else ''
return {'status': 'success', 'protocol': protocol, 'backup': {'name': name, 'path': path}}
def restore_backup(self, protocol, container_name, filename):
"""Restore protocol files from a previously created backup archive."""
safe_name = self.safe_filename(filename)
if not safe_name:
return {'status': 'error', 'message': 'Invalid backup filename'}
safe_proto = self.safe_protocol(protocol)
remote_archive = f'{self.BACKUP_ROOT}/{safe_proto}/{safe_name}'
paths = self._paths_for(protocol, container_name)
container_paths = ' '.join(shlex.quote(p) for p in paths['container'])
host_paths = ' '.join(shlex.quote(p) for p in paths['host'])
archive_q = shlex.quote(remote_archive)
container_q = shlex.quote(str(container_name or ''))
protocol_q = shlex.quote(str(protocol))
script = f"""
set -eu
umask 077
archive={archive_q}
container={container_q}
protocol={protocol_q}
if [ ! -f "$archive" ]; then
echo "Backup archive not found" >&2
exit 1
fi
work_dir=$(mktemp -d /tmp/amnezia-restore-XXXXXX)
cleanup() {{ rm -rf "$work_dir"; }}
trap cleanup EXIT
tar -xzf "$archive" -C "$work_dir"
if [ ! -f "$work_dir/backup-info.json" ]; then
echo "Invalid backup archive (missing backup-info.json)" >&2
exit 1
fi
restore_host_path() {{
src="$1"
staged="$work_dir/host$src"
if [ -e "$staged" ]; then
mkdir -p "$(dirname "$src")"
rm -rf "$src"
cp -a "$staged" "$src"
fi
}}
restore_container_path() {{
src="$1"
staged="$work_dir/container$src"
if [ -n "$container" ] && [ -e "$staged" ] && docker inspect "$container" >/dev/null 2>&1; then
if [ -d "$staged" ]; then
docker exec "$container" mkdir -p "$src" >/dev/null 2>&1 || true
docker cp "$staged/." "$container:$src/" >/dev/null 2>&1 || true
else
parent=$(dirname "$src")
docker exec "$container" mkdir -p "$parent" >/dev/null 2>&1 || true
docker cp "$staged" "$container:$src" >/dev/null 2>&1 || true
fi
fi
}}
for p in {host_paths}; do restore_host_path "$p"; done
for p in {container_paths}; do restore_container_path "$p"; done
if [ -n "$container" ] && docker inspect "$container" >/dev/null 2>&1; then
docker restart "$container" >/dev/null 2>&1 || true
# Prefer bringing the WireGuard/AWG interface back up after file restore
if docker exec "$container" test -x /opt/amnezia/start.sh >/dev/null 2>&1; then
docker exec "$container" /opt/amnezia/start.sh >/dev/null 2>&1 || true
fi
fi
printf 'restored:%s\\n' "$protocol"
""".strip()
out, err, code = self.ssh.run_sudo_command(script, timeout=180)
if code != 0:
return {'status': 'error', 'message': err or out or 'Failed to restore backup'}
return {'status': 'success', 'protocol': protocol, 'filename': safe_name}