Skip to content

Commit

Permalink
Merge pull request aleskxyz#132 from aleskxyz/backup
Browse files Browse the repository at this point in the history
Add backup and restore feature
  • Loading branch information
aleskxyz authored Dec 3, 2023
2 parents ea590be + 252076c commit 381a009
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 7 deletions.
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Features:
* Supports Cloudflare warp+
* Install with a single command
* Telegram bot for user management
* Create backup from users and configuration
* Restore users and configuration from backup

Supported OS:
* Ubuntu 22.04
Expand Down Expand Up @@ -76,7 +78,7 @@ Help message of the script:
Usage: reality-ezpz.sh [-t|--transport=tcp|http|grpc|ws|tuic|hysteria2] [-d|--domain=<domain>] [--server=<server>] [--regenerate] [--default]
[-r|--restart] [--enable-safenet=true|false] [--port=<port>] [-c|--core=xray|sing-box]
[--enable-warp=true|false] [--warp-license=<license>] [--security=reality|letsencrypt|selfsigned] [-m|--menu] [--show-server-config]
[--add-user=<username>] [--lists-users] [--show-user=<username>] [--delete-user=<username>] [-u|--uninstall]
[--add-user=<username>] [--lists-users] [--show-user=<username>] [--delete-user=<username>] [--backup] [--restore=<url|file>] [-u|--uninstall]
-t, --transport <tcp|http|grpc|ws|tuic|hysteria2> Transport protocol (tcp, http, grpc, ws, tuic, hysteria2, default: tcp)
-d, --domain <domain> Domain to use as SNI (default: www.google.com)
Expand All @@ -100,6 +102,8 @@ Usage: reality-ezpz.sh [-t|--transport=tcp|http|grpc|ws|tuic|hysteria2] [-d|--do
--list-users List all users
--show-user <username> Shows the config and QR code of the user
--delete-user <username> Delete the user
--backup Backup users and configuration and upload it to keep.sh
--restore <url|file> Restore backup from URL or file
-h, --help Display this help message
```

Expand Down Expand Up @@ -280,6 +284,30 @@ bash <(curl -sL https://bit.ly/realityez) -c xray
```
Valid options are `xray` and `sing-box`.

### Create backup
You can create a backup from users and configuration and upload it to free.keep.sh by using `--backup` option:
```
bash <(curl -sL https://bit.ly/realityez) --backup
```
This command will give you a URL to download you backup file. The URL is only valid for 24h.

### Restore backup
You can restore a previously created backup file with `--restore` option.

You need to give the path or URL of the backup file to restore:
```
bash <(curl -sL https://bit.ly/realityez) --restore /path/to/backup.tar.gz
```
or
```
bash <(curl -sL https://bit.ly/realityez) --restore "https://www.example.com/backup.tar.gz"
```

You can migrate users and configuration from one server to another by:

1. Create backup in the old server and copy the URL of backup file
1. Restore the URL of backup file in the new server

### Text-based user interface (TUI)
You can also use the TUI for changing the configuration of the service.

Expand Down
149 changes: 143 additions & 6 deletions reality-ezpz.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,15 @@ regex[ip]="^([0-9]{1,3}\.){3}[0-9]{1,3}$"
regex[tgbot_token]="^[0-9]{8,10}:[a-zA-Z0-9_-]{35}$"
regex[tgbot_admins]="^[a-zA-Z][a-zA-Z0-9_]{4,31}(,[a-zA-Z][a-zA-Z0-9_]{4,31})*$"
regex[domain_port]="^[a-zA-Z0-9]+([-.][a-zA-Z0-9]+)*\.[a-zA-Z]{2,}(:[1-9][0-9]*)?$"
regex[file_path]="^[a-zA-Z0-9_/.-]+$"
regex[url]="^(http|https)://([a-zA-Z0-9.-]+\.[a-zA-Z]{2,}|[0-9]{1,3}(\.[0-9]{1,3}){3})(:[0-9]{1,5})?(/.*)?$"

function show_help {
echo ""
echo "Usage: reality-ezpz.sh [-t|--transport=tcp|http|grpc|ws|tuic|hysteria2] [-d|--domain=<domain>] [--server=<server>] [--regenerate] [--default]
[-r|--restart] [--enable-safenet=true|false] [--port=<port>] [-c|--core=xray|sing-box]
[--enable-warp=true|false] [--warp-license=<license>] [--security=reality|letsencrypt|selfsigned] [-m|--menu] [--show-server-config]
[--add-user=<username>] [--lists-users] [--show-user=<username>] [--delete-user=<username>] [-u|--uninstall]"
[--add-user=<username>] [--lists-users] [--show-user=<username>] [--delete-user=<username>] [--backup] [--restore=<url|file>] [-u|--uninstall]"
echo ""
echo " -t, --transport <tcp|http|grpc|ws|tuic|hysteria2> Transport protocol (tcp, http, grpc, ws, tuic, hysteria2, default: ${defaults[transport]})"
echo " -d, --domain <domain> Domain to use as SNI (default: ${defaults[domain]})"
Expand All @@ -128,13 +130,15 @@ function show_help {
echo " --list-users List all users"
echo " --show-user <username> Shows the config and QR code of the user"
echo " --delete-user <username> Delete the user"
echo " --backup Backup users and configuration and upload it to keep.sh"
echo " --restore <url|file> Restore backup from URL or file"
echo " -h, --help Display this help message"
return 1
}

function parse_args {
local opts
opts=$(getopt -o t:d:ruc:mh --long transport:,domain:,server:,regenerate,default,restart,uninstall,enable-safenet:,port:,warp-license:,enable-warp:,core:,security:,menu,show-server-config,add-user:,list-users,show-user:,delete-user:,enable-tgbot:,tgbot-token:,tgbot-admins:,help -- "$@")
opts=$(getopt -o t:d:ruc:mh --long transport:,domain:,server:,regenerate,default,restart,uninstall,enable-safenet:,port:,warp-license:,enable-warp:,core:,security:,menu,show-server-config,add-user:,list-users,show-user:,delete-user:,backup,restore:,enable-tgbot:,tgbot-token:,tgbot-admins:,help -- "$@")
if [[ $? -ne 0 ]]; then
return 1
fi
Expand Down Expand Up @@ -312,6 +316,18 @@ function parse_args {
args[delete_user]="$2"
shift 2
;;
--backup)
args[backup]=true
shift
;;
--restore)
args[restore]="$2"
if [[ ! ${args[restore]} =~ ${regex[file_path]} ]] && [[ ! ${args[restore]} =~ ${regex[url]} ]]; then
echo "Invalid: Backup file path or URL is not valid."
return 1
fi
shift 2
;;
-h|--help)
return 1
;;
Expand All @@ -333,7 +349,49 @@ function parse_args {
if [[ -n ${args[warp_license]} ]]; then
args[warp]=ON
fi
}

function backup {
local backup_name
local backup_file_url
local exit_code
backup_name=reality-ezpz-backup-$(date +%Y-%m-%d_%H-%M-%S).tar.gz
tar -czf "/tmp/${backup_name}" -C "${config_path}" ./
if ! backup_file_url=$(curl -fsS --upload-file "/tmp/${backup_name}" https://free.keep.sh); then
rm -f "/tmp/${backup_name}"
echo "Error in uploading backup file" >&2
return 1
fi
rm -f "/tmp/${backup_name}"
echo "${backup_file_url}"
return
}

function restore {
local backup_file=$1
local temp_file
if [[ ! -r ${backup_file} ]]; then
temp_file=$(mktemp -u)
if ! curl -fSsL "${backup_file}" -o "${temp_file}"; then
echo "Cannot download or find backup file" >&2
return 1
fi
backup_file="${temp_file}"
fi
if ! tar -tzf "${backup_file}" | grep -q config; then
echo "The provided file is not a reality-ezpz backup file." >&2
rm -f "${temp_file}"
return 1
fi
rm -rf "${config_path}"
mkdir -p "${config_path}"
if ! tar -xzf "${backup_file}" -C "${config_path}"; then
echo "Error in backup restore." >&2
rm -f "${temp_file}"
return 1
fi
rm -f "${temp_file}"
return
}

function dict_expander {
Expand Down Expand Up @@ -707,7 +765,7 @@ services:
BOT_ADMIN: ${config[tgbot_admins]}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ../:/opt/reality-ezpz
- ../:${config_path}
- /etc/docker/:/etc/docker/
networks:
- tgbot
Expand Down Expand Up @@ -853,7 +911,7 @@ EOF
function generate_tgbot_dockerfile {
cat >"${path[tgbot_dockerfile]}" << EOF
FROM ${image[python]}
WORKDIR /opt/reality-ezpz/tgbot
WORKDIR ${config_path}/tgbot
RUN apk add --no-cache docker-cli-compose curl bash newt libqrencode sudo openssl jq
RUN pip install --no-cache-dir python-telegram-bot==13.5
CMD [ "python", "./tgbot.py" ]
Expand Down Expand Up @@ -1336,7 +1394,7 @@ function upgrade {
local warp_id
if [[ -e "${HOME}/reality/config" ]]; then
${docker_cmd} --project-directory "${HOME}/reality" down --remove-orphans --timeout 2
mv -f "${HOME}/reality" /opt/reality-ezpz
mv -f "${HOME}/reality" ${config_path}
fi
uuid=$(grep '^uuid=' "${path[config]}" 2>/dev/null | cut -d= -f2 || true)
if [[ -n $uuid ]]; then
Expand Down Expand Up @@ -1442,7 +1500,7 @@ function add_user_menu {
break
fi
view_user_menu "${username}"
done
done
}

function delete_user_menu {
Expand Down Expand Up @@ -1654,6 +1712,8 @@ function configuration_menu {
"10" "Restart Services" \
"11" "Regenerate Keys" \
"12" "Restore Defaults" \
"13" "Create Backup" \
"14" "Restore Backup" \
3>&1 1>&2 2>&3)
if [[ $? -ne 0 ]]; then
break
Expand Down Expand Up @@ -1695,6 +1755,12 @@ function configuration_menu {
12 )
restore_defaults_menu
;;
13 )
backup_menu
;;
14 )
restore_backup_menu
;;
esac
done
}
Expand Down Expand Up @@ -2030,6 +2096,64 @@ function config_tgbot_menu {
config[tgbot_admins]=$old_tgbot_admins
}

function backup_menu {
local result
whiptail \
--clear \
--backtitle "$BACKTITLE" \
--title "Backup" \
--yesno "Do you want to create a backup from users and configuration?" \
$HEIGHT $WIDTH \
3>&1 1>&2 2>&3
if [[ $? -ne 0 ]]; then
return
fi
if result=$(backup 2>&1); then
echo "Backup has been create and uploaded successfully."
echo "You can download the backup file from here:"
echo "${result}"
echo ""
echo "The URL is valid for 24h."
echo
echo "Press Enter to return ..."
read
else
message_box "Backup Failed" "${result}"
fi
}

function restore_backup_menu {
local backup_file
local result
while true; do
backup_file=$(whiptail \
--clear \
--backtitle "$BACKTITLE" \
--title "Restore Backup" \
--inputbox "Enter backup file path or URL" \
$HEIGHT $WIDTH \
3>&1 1>&2 2>&3)
if [[ $? -ne 0 ]]; then
break
fi
if [[ ! $backup_file =~ ${regex[file_path]} ]] && [[ ! $backup_file =~ ${regex[url]} ]]; then
message_box "Invalid Backup path of URL" "Backup file path or URL is not valid."
continue
fi
if result=$(restore ${backup_file} 2>&1); then
parse_config_file
parse_users_file
build_config
update_config_file
update_users_file
message_box "Backup Restore Successful" "Backup has been restored successfully."
break
else
message_box "Backup Restore Failed" "${result}"
fi
done
}

function restart_docker_compose {
${docker_cmd} --project-directory ${config_path} -p ${compose_project} down --remove-orphans --timeout 2 || true
${docker_cmd} --project-directory ${config_path} -p ${compose_project} up --build -d --remove-orphans --build
Expand Down Expand Up @@ -2294,6 +2418,19 @@ if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root."
exit 1
fi
if [[ ${args[backup]} == true ]]; then
if backup_url=$(backup); then
echo "Backup created successfully. You can download the backup file from this address:"
echo "${backup_url}"
echo "The URL is valid for 24h."
fi
fi
if [[ -n ${args[restore]} ]]; then
if restore ${args[restore]}; then
args[restart]=true
echo "Backup has been restored successfully."
fi
fi
generate_file_list
install_packages
install_docker
Expand Down

0 comments on commit 381a009

Please sign in to comment.