#!/bin/bash # Note: We use $HOME here, because it matches the duply/duplicity profile creation folder. # The profile folder will be stored under '~/.duply/' (where ~ is the current users home directory). # Hint: If the folder '/etc/duply' exists, the profiles for the super user root will be searched & created there. bucket_validation() { local bucketname=$(echo $1 | cut -d "/" -f 1) local bucketfolder=$(echo $1 | cut -d "/" -f 2- -s) # Only numerals 0-9, basic Latin letters (only lowercase) and underscore. # https://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html if [[ -z $1 || -z $bucketname ]]; then echo "${red}[ERROR] Please, enter a valid bucket name! ${end}" exit 1 elif ! [[ $bucketname =~ ^[0-9a-z\/-]+$ ]] || [[ -z $bucketname || $(echo $bucketname | cut -c-1) =~ [-|\/] || ${#bucketname} -gt 63 || ${#bucketname} -lt 3 ]]; then echo "${red}[ERROR] Bucket names can only contain lowercase letters, numbers or hyphens; must start with a letter or number and must be at least 3 and no more than 63 characters long.${end}" exit 1 elif [[ -n $bucketfolder ]] && ! [[ $bucketfolder =~ ^[0-9a-zA-Z\/-_\!\.\*\'\)\(]+$ ]]; then if [[ $(is_url_path /$bucketfolder) == "true" ]]; then echo "${red}[WARNING] Your bucket folder name contain some characters that might require special handling!" echo "More info: https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html#object-key-guidelines ${end}" else echo "${red}[ERROR] Invalid bucket folder name.${end}" exit 1 fi fi } bkp_local_db() { # Check for Multiple sites or DB's if [[ -z $child_multi_bkp_db && (( $(echo "${wp}" | cut -c-1) == "[" && $(echo "${wp}" | rev | cut -c-1) == "]" ) || ( $(echo "${dbname}" | cut -c-1) == "[" && $(echo "${dbname}" | rev | cut -c-1) == "]" )) ]]; then if [[ -n $subfolder ]]; then echo "${red}[ERROR] Subfolder option not allowed for multiple sites!${end}" exit 1 fi [[ -n $dbname ]] && local multi=${dbname:1:-1} [[ -n $wp ]] && local multi=${wp:1:-1} local master_multi_bkp_db="true" local c=1 echo "${blu}Processing multiple databases for backup...${end}" # Prevent error when enteres just one value in multi-mode: [one] [[ -z $(echo $multi | cut -d',' -f 1 -s) ]] && local multi="${multi}," while [[ -n $(echo $multi | cut -d',' -f $c -s) ]] do if [[ -f /etc/nginx/sites-available/$(echo $multi | cut -d',' -f $c -s) ]]; then webinoly -backup=local -wp=$(echo $multi | cut -d',' -f $c -s) -child-multi-bkp-db else webinoly -backup=local -dbname=$(echo $multi | cut -d',' -f $c -s) -child-multi-bkp-db fi local c=$(($c+1)) done elif [[ -n $child_multi_bkp_db && $dbname == "all" ]]; then echo "${red}[ERROR] ALL option cannot be used when backing up multiple databases! ${dim}(skip)${end}" return fi # Not run if this is the master process in multiple DB backup # Not run if ALL if [[ -z $master_multi_bkp_db && $dbname != "all" ]]; then if [[ ( -z $wp || $wp == "true") && ( -z $dbname || $dbname == "true" ) ]]; then read -p "${gre}WordPress site (domain) or Database name: ${end}" dbq if [[ -n $dbq && -f /etc/nginx/sites-available/$dbq ]]; then wp=$dbq elif [[ -n $dbq ]]; then dbname=$dbq wp="" else echo "${red}[ERROR] Invalid value!${end}" exit 1 fi fi if [[ -n $wp ]]; then if [[ ! -f /etc/nginx/sites-available/$wp ]]; then echo "${red}[ERROR] Site not found! ${dim}($wp)${end}" exit 1 elif [[ $(is_wp $wp $subfolder) != "true" ]]; then echo "${red}[ERROR] Please, enter a valid WP site! ${dim}($wp)${end}" exit 1 else wp_conf_retrieve $wp true true $subfolder [[ $wp_dbhost_host == "localhost" ]] && check_for_mysql [[ -n $subfolder ]] && local subname=$(echo $subfolder | sed "s/\//_/g") fi if [[ $wp_dbhost_host != "localhost" && ( -z $wp_dbhost_host || -z $wp_dbhost_port ) ]]; then echo "${red}[ERROR] Invalid data for External Database!${end}" exit 1 # Duplicate check: we need this only to prevent error in the next is_wp_installed elif [[ $wp_dbhost_host != "localhost" && $(check_mysql_connection $extdb_url $extdb_port any -login-file -master-admin) != "true" ]]; then echo "${red}[ERROR] External DB Connection failed!${end}" exit 1 elif [[ $wp_dbhost_host == "localhost" && $(check_mysql_connection localhost $mysql_param) != "true" ]]; then echo "${red}[ERROR] Localhost DB Connection failed!${end}" exit 1 elif [[ $(is_wp_installed $wp $subfolder) != "true" ]]; then echo "${red}[ERROR] Your WP site database is still empty!${end}" exit 1 else local dbname=$wp_dbname local extdb_url=$extdb_url local extdb_port=$extdb_port local extdb_user=$extdb_user local extdb_pass=$extdb_pass fi else check_external_db_saved if [[ -z $external_db ]]; then check_for_mysql if [[ $(check_mysql_connection localhost) != "true" ]]; then echo "${red}[ERROR] Cannot connect with your database (localhost)!${end}" exit 1 fi local checkdbname=$(sudo mysqlshow --user=admin | grep -ow $dbname) else external_db_parse if [[ $(check_mysql_connection $extdb_url $extdb_port any -login-file -master-admin) != "true" ]]; then echo "${red}[ERROR] Cannot connect with your External Database!${end}" exit 1 fi local checkdbname=$(sudo mysqlshow --defaults-group-suffix=_${extdb_url}:${extdb_port}_default -h "$extdb_url" -P "$extdb_port" | grep -ow $dbname) fi if [[ $checkdbname != $dbname ]]; then echo "${red}[ERROR] Database not found! ${dim}($dbname)${end}" exit 1 fi fi fi # Multiple DB's if [[ -n $master_multi_bkp_db ]]; then local fn="multiple" local db_name_list=$(conf_read multi-bkp-db) conf_delete multi-bkp-db elif [[ -n $child_multi_bkp_db ]]; then [[ -n $(conf_read multi-bkp-db) ]] && conf_write multi-bkp-db "$(conf_read multi-bkp-db) $dbname" || conf_write multi-bkp-db $dbname echo "${blu}${dim}Added: $dbname ${end}" # When is a child process we need to exit here, backup is made by master process, not child. return else local db_name_list=$dbname [[ -n $wp ]] && local fn="${wp}${subname}" || local fn=$dbname echo "${blu}${dim}Backup is in process, wait...${end}" fi # ALL Databases [[ $dbname == "all" ]] && local db_name_list="--all-databases" || local db_name_list="--databases $db_name_list" # Set destination folder and filename if [[ -z $destination || $destination =~ ^(default|true)$ ]]; then destination="$HOME/webinoly-backups/$fn" sudo mkdir -p $destination # Must start with / and can not end with / elif [[ ! -d $destination && $(echo "${destination}" | cut -c-1) == "/" && $(echo "${destination}" | rev | cut -c-1) != "/" ]]; then sudo mkdir -p $destination fi if [[ ! -d $destination || $(echo "${destination}" | rev | cut -c-1) == "/" ]]; then echo "${red}[ERROR] Please, enter a valid destination path!${end}" exit 1 fi [[ -z $filename ]] && local filename="webinoly-backup-db_${fn}_$(date +%F)-$(date +%T).sql" if [[ ( $wp_dbhost_host == "localhost" || ( -z $extdb_url && -z $extdb_port )) && $(check_mysql_connection localhost $mysql_param) == "true" ]]; then sudo mysqldump --user=admin --single-transaction --lock-tables --quick $db_name_list > $destination/$filename elif [[ $(check_mysql_connection $extdb_url $extdb_port any -login-file -master-admin) == "true" ]]; then sudo mysqldump --defaults-group-suffix=_${extdb_url}:${extdb_port}_default -h "$extdb_url" -P "$extdb_port" --single-transaction --lock-tables --quick $db_name_list > $destination/$filename else echo "${red}[ERROR] DB Connection failed!${end}" exit 1 fi if [[ -s $destination/$filename ]]; then echo "${gre}Database local backup successfully done!${end}${dim} ($destination/$filename)${end}" [[ -n $s3_compatible_endpoint ]] && local param="-s3-compatible-endpoint=$s3_compatible_endpoint " [[ -n $bucket ]] && sudo webinoly -backup=s3 -send-to-s3=$destination/$filename -bucket=$bucket $param [[ -n $max && $max =~ ^[0-9]+$ ]] && sudo ls -1t $destination | tail -n +$((max+1)) | xargs -d '\n' -I '%' sudo rm -f $destination/% else echo "${red}[ERROR] Database backup failed!${end}" exit 1 fi } check_duply_profile() { if [[ ! -d $HOME/.duply/$profile ]]; then echo "${red}[ERROR] Backup profile not found!${end}" exit 1 fi } bkp_s3_profile() { if [[ ! -s $HOME/.aws/credentials && $(conf_read awsiamrole) != true ]]; then echo "${red}[ERROR] AWS S3 Credentials not found!${end}" exit 1 fi if [[ $profile == "true" || -z $profile ]]; then echo "" read -p "${gre}Profile name: ${end}" profile if [[ -z $profile ]]; then echo "${red}[ERROR] Profile name is empty!${end}" exit 1 fi fi if [[ -n $run ]]; then check_duply_profile sudo duply $profile backup_verify_purge --force --allow-source-mismatch elif [[ -n $info ]]; then check_duply_profile local tar=$(grep -E "^TARGET[ ]?=" $HOME/.duply/$profile/conf | cut -f 2 -d "'" ) local sou=$(grep -E "^SOURCE[ ]?=" $HOME/.duply/$profile/conf | cut -f 2 -d "'" ) local age=$(grep -E "^MAX_AGE[ ]?=" $HOME/.duply/$profile/conf | cut -f 2 -d "=" ) local par=$(grep "s3-use-new-style" $HOME/.duply/$profile/conf | cut -f 2 -d '"' ) # Validations [[ -z $par ]] && local par="${dim}${end}" [[ ! -d $sou ]] && local sou="$sou ${red}${dim}(Error: Path not found!)${end}" # Display echo "" echo "${blu}S3 Bucket:${end} $tar" echo "${blu}Source:${end} $sou" echo "${blu}Max_Age:${end} $age" echo "${blu}Parameters:${end} $par" echo "" elif [[ -n $delete ]]; then check_duply_profile sudo rm -rf $HOME/.duply/$profile echo "${gre}Backup profile ${blu}'$profile'${gre} was successfully deleted!${end}" elif [[ -n $restore ]]; then check_duply_profile # Temporary check!!!! Should be removed soon!!! if [[ $restore != "true" && -z $destination ]]; then echo "${red}[ERROR] Backup Restore syntax has changed, destination paramater is needed!${end}" exit 1 fi if [[ -z $destination || $destination == "true" ]]; then echo "" # We don't do a destination path validation because duply can handle it better. read -p "${gre}Restore destination folder: ${end}" destination if [[ -z $destination ]]; then echo "${red}[ERROR] Invalid destination path!${end}" exit 1 fi fi [[ $restore != "true" ]] && sudo duply $profile fetch $restore $destination $date || sudo duply $profile restore $destination $date elif [[ -n $add_db_pre && -n $list ]]; then check_duply_profile if [[ -s $HOME/.duply/$profile/pre ]]; then if [[ -z $raw ]]; then echo "" echo "${gre}The following lines will be executed every time just before (PRE)${blu} '$profile' ${gre}backup is run:${blu}" fi cat $HOME/.duply/$profile/pre [[ -z $raw ]] && echo "${end}" else [[ -z $raw ]] && echo "${red}[ERROR] PRE Database backups is empty!${end}" exit 1 fi elif [[ -n $add_db_pre && -n $purge ]]; then check_duply_profile sudo rm -rf $HOME/.duply/$profile/pre echo "${gre}PRE Database backups for${blu} '$profile' ${gre}has been successfully removed!${end}" elif [[ -n $add_db_pre ]]; then check_duply_profile [[ $add_db_pre == "true" ]] && read -p "${gre}WordPress site: ${end}" add_db_pre # we dont check is_wp_installed because at this point we are still not doing a backup, just setting it. if [[ -z $add_db_pre || $(is_wp $add_db_pre $subfolder) != "true" ]]; then echo "${red}[ERROR] Please, enter a valid WP site!${end}" exit 1 fi if [[ $(wp_config_read $add_db_pre DB_HOST $subfolder) != "localhost" && -z $(conf_read external-dbu) && -z $(conf_read external-dbp) ]]; then echo "${red}[ERROR] Database host is not localhost!${dim} (External databases are supported only when credentials are saved in Webinoly Configuration file)${end}" exit 1 fi [[ -z $destination || $destination == "true" ]] && destination="default" [[ -z $max && $destination == "default" ]] && max="5" [[ -n $max && $max =~ ^[0-9]+$ ]] && local param="-max=$max " [[ -n $bucket && $bucket != "true" ]] && local param="${param}-bucket=$bucket " [[ -n $s3_compatible_endpoint ]] && local param="${param}-s3-compatible-endpoint=$s3_compatible_endpoint " [[ -n $subfolder ]] && local param="${param}-subfolder=$subfolder" [[ ! -f $HOME/.duply/$profile/pre ]] && sudo touch $HOME/.duply/$profile/pre echo "sudo webinoly -backup=local -wp=$add_db_pre -destination=$destination $param" >> $HOME/.duply/$profile/pre echo "${gre}Database backup will run each time you run your S3 backup!${end}" else if [[ -d $HOME/.duply/$profile ]]; then echo "${red}[ERROR] Can not create profile${blu} '$profile' ${red}because already exists!${end}" exit 1 fi # S3 Compatible services if [[ -n $s3_compatible_endpoint ]] && ! [[ $(is_url $s3_compatible_endpoint) =~ ^(http|https)$ ]]; then echo "${red}[ERROR] Invalid endpoint URL!${end}" exit 1 fi [[ -z $bucket || $bucket == "true" ]] && read -p "${gre}S3 Bucket name: ${end}" bucket bucket_validation $bucket [[ -z $source || $source == "true" ]] && read -p "${gre}Source path: ${end}" source if [[ -z $source || ! -d $source ]]; then echo "${red}[ERROR] Please, enter a valid source folder and bucket name!${end}" exit 1 fi sudo duply $profile create [[ -z $max_age ]] && max_age="1M" sudo sed -i -E "/^[#]?GPG_KEY=/c GPG_KEY='disabled'" $HOME/.duply/$profile/conf sudo sed -i -E "/^[#]?GPG_PW=/c #GPG_PW='_GPG_PASSWORD_'" $HOME/.duply/$profile/conf sudo sed -i -E "/^[#]?SOURCE=/c SOURCE='$source'" $HOME/.duply/$profile/conf sudo sed -i -E "/^[#]?MAX_AGE=/c MAX_AGE=$max_age" $HOME/.duply/$profile/conf sudo sed -i '/^#MAX_FULLBKP_AGE=/s/#//' $HOME/.duply/$profile/conf sudo sed -i '/^#DUPL_PARAMS="\$DUPL_PARAMS --full-if-older-than \$MAX_FULLBKP_AGE "/s/#//' $HOME/.duply/$profile/conf sudo sed -i -E "/^[#]?TARGET=/c TARGET='s3://${bucket}'" $HOME/.duply/$profile/conf if [[ -n $s3_compatible_endpoint ]]; then sudo echo "DUPL_PARAMS=\"\$DUPL_PARAMS --s3-endpoint-url $s3_compatible_endpoint \"" >> $HOME/.duply/$profile/conf echo "${blu}${dim}S3 Compatible service mode enabled (Not AWS)${end}" fi echo "${gre}Backup profile ${blu}'$profile'${gre} was successfully created!${end}" fi } bkp_s3_list() { echo "" if [[ -d $HOME/.duply ]]; then for f in $HOME/.duply/* do [[ -d $f ]] && pro=$(echo $f | rev | cut -f 1 -d "/" -s | rev) [[ -f $f/conf ]] && fail="" || fail="${red}(fail)${end}" [[ -n $raw || $list == "raw" ]] && outlist="$pro" || outlist=" ${gre}+ $pro ${end}${fail}" if [[ -n $pro ]]; then echo "$outlist" nonemptylist=true fi done fi [[ -z $nonemptylist && -z $raw && $list != "raw" ]] && echo "${blu}[Empty] No profiles were found!${end}" echo "" } s3_send() { if [[ ! -s $HOME/.aws/credentials && $(conf_read awsiamrole) != true ]]; then echo "${red}[ERROR] AWS S3 Credentials not found!${end}" exit 1 fi # S3 Compatible services if [[ -n $s3_compatible_endpoint ]] && ! [[ $(is_url $s3_compatible_endpoint) =~ ^(http|https)$ ]]; then echo "${red}[ERROR] Invalid endpoint URL!${end}" exit 1 elif [[ -n $s3_compatible_endpoint ]]; then echo "${blu}${dim}S3 Compatible service mode enabled (Not AWS)${end}" else local s3_compatible_endpoint="" fi [[ -z $send_to_s3 || $send_to_s3 == "true" ]] && read -p "${blu}File to send: ${end}" send_to_s3 if [[ ! -f $send_to_s3 ]]; then echo "${red}[ERROR] File not found!${end}" exit 1 fi [[ -z $bucket || $bucket == "true" ]] && read -p "${blu}S3 Bucket name: ${end}" bucket bucket_validation $bucket local keyfol=$(echo $bucket | cut -f 2- -d "/" -s) [[ -n $keyfol ]] && keyfol="${keyfol}/" export keyfol export bucket=$(echo $bucket | cut -f 1 -d "/") export send_to_s3 export s3_compatible_endpoint python3 - &>/dev/null < /dev/null fi smtp_backup # Create TAR file [[ -d /var/www/html ]] && local exclude="--exclude=/var/www/html" || local exclude="" [[ -d /var/www/$ADMIN_TOOLS_SITE ]] && local exclude="$exclude --exclude=/var/www/$ADMIN_TOOLS_SITE" [[ -f /etc/nginx/sites-available/$ADMIN_TOOLS_SITE ]] && local exclude="$exclude --exclude=/etc/nginx/sites-available/$ADMIN_TOOLS_SITE" [[ -L /etc/nginx/sites-enabled/$ADMIN_TOOLS_SITE ]] && local exclude="$exclude --exclude=/etc/nginx/sites-enabled/$ADMIN_TOOLS_SITE" [[ -f /etc/nginx/sites-available/default ]] && local exclude="$exclude --exclude=/etc/nginx/sites-available/default" [[ -L /etc/nginx/sites-enabled/default ]] && local exclude="$exclude --exclude=/etc/nginx/sites-enabled/default" [[ -f /opt/webinoly/webinoly.conf.exported ]] && local include="/opt/webinoly/webinoly.conf.exported" || local include="" [[ -d /etc/nginx/apps.d ]] && local include="$include /etc/nginx/apps.d" [[ -f /etc/nginx/conf.d/blockips.conf ]] && local include="$include /etc/nginx/conf.d/blockips.conf" [[ -f /etc/nginx/conf.d/webinoly.conf ]] && local include="$include /etc/nginx/conf.d/webinoly.conf" [[ -d /etc/nginx/sites-available ]] && local include="$include /etc/nginx/sites-available" [[ -d /etc/nginx/sites-enabled ]] && local include="$include /etc/nginx/sites-enabled" [[ -d /var/www ]] && local include="$include /var/www" [[ -d /etc/letsencrypt ]] && local include="$include /etc/letsencrypt" [[ -f /etc/nginx/.htpasswd ]] && local include="$include /etc/nginx/.htpasswd" [[ -f $HOME/.aws/credentials ]] && local include="$include $HOME/.aws/credentials" [[ -f /opt/webinoly/templates/source/csp_webinoly.data ]] && local include="$include /opt/webinoly/templates/source/csp_webinoly.data" [[ -f /opt/webinoly/templates/source/pph_webinoly.data ]] && local include="$include /opt/webinoly/templates/source/pph_webinoly.data" [[ -f /opt/webinoly/templates/source/cch_webinoly.data ]] && local include="$include /opt/webinoly/templates/source/cch_webinoly.data" [[ -f /opt/webinoly/templates/source/rob_webinoly.data ]] && local include="$include /opt/webinoly/templates/source/rob_webinoly.data" [[ -f /opt/webinoly/templates/source/custom_header_http_webinoly.data ]] && local include="$include /opt/webinoly/templates/source/custom_header_http_webinoly.data" [[ -f /opt/webinoly/templates/source/custom_header_https_webinoly.data ]] && local include="$include /opt/webinoly/templates/source/custom_header_https_webinoly.data" [[ -f /opt/webinoly/templates/source/custom_header_html_webinoly.data ]] && local include="$include /opt/webinoly/templates/source/custom_header_html_webinoly.data" [[ -f $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly-login.cnf ]] && local include="$include $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly.cnf" [[ -f $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly.cnf ]] && local include="$include $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly.cnf" [[ -d /etc/nginx/certs ]] && local include="$include /etc/nginx/certs" sudo tar $exclude -Pcf $destination/$filename $include # Remove Temporary Files sudo rm -rf /opt/webinoly/webinoly.conf.exported [[ $(conf_read mysql) == "true" && -z $skip_db ]] && sudo rm -rf /var/www/webinoly_backup_dball [[ $(conf_read smtp) == "true" ]] && sudo rm -rf /var/www/webinoly_backup_smtp if [[ -s $destination/$filename ]]; then echo "${gre}Webinoly Complete Server Backup exported successfully!${end}${dim} ($destination/$filename) ${end}" else [[ -f $destination/$filename ]] && sudo rm -rf $destination/$filename echo "${red}[ERROR] Unexpected error occurred while trying to export your server files!${end}" exit 1 fi } export_site() { # Check for Multiple sites or DB's if [[ $(echo "${export}" | cut -c-1) == "[" && $(echo "${export}" | rev | cut -c-1) == "]" ]]; then if [[ -n $subfolder ]]; then echo "${red}[ERROR] Subfolder option not allowed for multiple sites!${end}" exit 1 else local multi_export="true" echo "${blu}Processing multiple sites...${end}" fi elif [[ ! -f /etc/nginx/sites-available/$export ]]; then echo "${red}[ERROR] Please, enter a valid value for export site/server parameter!${end}" exit 1 elif [[ -f /etc/nginx/sites-available/$export && -n $subfolder && ! -d /var/www/$export/htdocs$subfolder ]]; then echo "${red}[ERROR] Subfolder not found!${end}" exit 1 fi [[ -n $subfolder ]] && local subn=$(echo $subfolder | sed "s/\//_/g") [[ -z $filename && -z $multi_export ]] && local filename="webinoly_${export}${subn}_$(date +%F)-$(date +%T)" [[ -z $filename && -n $multi_export ]] && local filename="webinoly_multiple_$(date +%F)-$(date +%T)" if [[ -z $destination || $destination =~ ^(default|true)$ ]]; then destination="$HOME" sudo mkdir -p $destination # Must start with / and can not end with / elif [[ ! -d $destination && $(echo "${destination}" | cut -c-1) == "/" && $(echo "${destination}" | rev | cut -c-1) != "/" ]]; then sudo mkdir -p $destination fi if [[ ! -d $destination || $(echo "${destination}" | rev | cut -c-1) == "/" ]]; then echo "${red}[ERROR] Please, enter a valid destination path!${end}" exit 1 fi # Processing sites to export! local c=1 while [[ -n $(echo ${export:1:-1} | cut -d',' -f $c -s) || -z $multi_export ]] do [[ -z $multi_export ]] && local siteto=$export || local siteto=$(echo ${export:1:-1} | cut -d',' -f $c -s) local c=$(($c+1)) if [[ -n $multi_export && ! -f /etc/nginx/sites-available/$siteto ]]; then echo "${red}[ERROR] Site not found! $siteto ${end}" continue elif [[ -n $multi_export ]]; then echo "${blu}${dim}Added: $siteto ${end}" fi [[ -s /etc/nginx/sites-available/$siteto ]] && local include="$include /etc/nginx/sites-available/$siteto" [[ -L /etc/nginx/sites-enabled/$siteto ]] && local include="$include /etc/nginx/sites-enabled/$siteto" [[ -s /etc/nginx/apps.d/.htpasswd-$siteto ]] && local include="$include /etc/nginx/apps.d/.htpasswd-$siteto" [[ -z $subfolder && -d /var/www/$siteto ]] && local include="$include /var/www/$siteto" [[ -n $subfolder && -d /var/www/$siteto/htdocs$subfolder ]] && local include="$include /var/www/$siteto/htdocs$subfolder" for app in /etc/nginx/apps.d/${siteto}${subn}*.conf do [[ -s $app ]] && local include="$include $app" done # Check if Custom Cache if [[ -f /etc/nginx/conf.d/webinoly.conf ]]; then if [[ -f /etc/nginx/apps.d/$siteto$subn-wpcache.conf || -f /etc/nginx/apps.d/$siteto$subn-phpcache.conf ]]; then local custom_cache="$(grep -E "^fastcgi_cache_path \/run\/nginx-cache\/${siteto}[ _].*;$" /etc/nginx/conf.d/webinoly.conf)" elif [[ -f /etc/nginx/apps.d/$siteto$subn-proxy.conf && -n $(grep -F "# WebinolyProxyCacheStart" /etc/nginx/apps.d/$siteto$subn-proxy.conf) ]]; then local custom_cache="$(grep -E "^proxy_cache_path \/run\/nginx-cache\/${siteto}[ _].*;$" /etc/nginx/conf.d/webinoly.conf)" fi if [[ -n $custom_cache ]]; then [[ ! -f /var/www/webinoly.conf_temp ]] && sudo touch /var/www/webinoly.conf_temp echo "$custom_cache" >> /var/www/webinoly.conf_temp fi fi # Include database backup for this site! if [[ -z $skip_db ]]; then if [[ $(is_wp $siteto $subfolder) == "true" ]]; then wp_conf_retrieve $siteto false false $subfolder if [[ $(conf_read mysql) == "true" && $wp_dbhost_host == "localhost" && $(is_wp_installed $siteto $subfolder) == "true" && -n $wp_dbname ]]; then # We use dbnames instead of domains because is easier to include subfolders if [[ -z $export_db_list ]]; then local export_db_list="$wp_dbname" else local export_db_list="$export_db_list,$wp_dbname" fi echo "${blu}${dim}Database included: ${siteto}${subfolder} ${end}" elif [[ $wp_dbhost_host == "localhost" ]]; then echo "${red}${dim}Database not found or empty: ${siteto}${subfolder} ${end}" elif [[ -n $wp_dbhost_host && $wp_dbhost_host != "localhost" ]]; then echo "${red}${dim}External database not included: ${siteto}${subfolder} ${end}" fi fi # Check if site contains WP in subfolders for sit in "/etc/nginx/apps.d/${siteto}_"*-wpcommon.conf do local suby="/$(echo $sit | cut -f 2- -d "_" -s | cut -f -1 -d "-" -s | sed "s/_/\//g")" if [[ $(is_wp $siteto $suby) == "true" ]]; then wp_conf_retrieve $siteto false false $suby if [[ -n $suby && $(conf_read mysql) == "true" && $wp_dbhost_host == "localhost" && $(is_wp_installed $siteto $suby) == "true" && -n $wp_dbname ]]; then if [[ -z $export_db_list ]]; then local export_db_list="$wp_dbname" else local export_db_list="$export_db_list,$wp_dbname" fi echo "${blu}${dim}Database included: ${siteto}${suby} ${end}" elif [[ -n $suby && $wp_dbhost_host == "localhost" ]]; then echo "${red}${dim}Database not found or empty: ${siteto}${suby} ${end}" elif [[ -n $suby && -n $wp_dbhost_host && $wp_dbhost_host != "localhost" ]]; then echo "${red}${dim}External database not included: ${siteto}${suby} ${end}" fi fi local suby="" done fi [[ -z $multi_export ]] && break done # Create backup file! if [[ $(conf_read mysql) == "true" && -z $skip_db && -n $include && -n $export_db_list ]]; then local export_db_list="[${export_db_list}]" sudo webinoly -backup=local -dbname=$export_db_list -destination=/var/www -filename=webinoly_backup_db > /dev/null [[ -f /var/www/webinoly_backup_db ]] && local include="$include /var/www/webinoly_backup_db" fi [[ -f /var/www/webinoly.conf_temp ]] && local include="$include /var/www/webinoly.conf_temp" [[ -n $include ]] && sudo tar -Pcf $destination/$filename $include # Remove temporary files sudo rm -rf /var/www/webinoly_backup_db sudo rm -rf /var/www/webinoly.conf_temp if [[ -s $destination/$filename ]]; then [[ -z $multi_export ]] && echo "${gre}Webinoly Site Backup${blu} $siteto ${gre}exported successfully!${end}${dim} ($destination/$filename) ${end}" [[ -n $multi_export ]] && echo "${gre}Webinoly Site Backup for multiple sites exported successfully!${end}${dim} ($destination/$filename) ${end}" else [[ -f $destination/$filename ]] && sudo rm -rf $destination/$filename echo "${red}[ERROR] Unexpected error occurred while trying to export your site!${end}" exit 1 fi } import_site() { if [[ $import =~ ^(stack|full)$ ]]; then echo "${red}[ERROR] Site backup file found, stack or full options are not supported!${end}" exit 1 elif ! [[ $import =~ ^(true|sites)$ ]]; then echo "${red}[ERROR] Invalid value for import parameter!${end}" exit 1 fi # List sites found! local list_sites=$(tar -tvf $file --absolute-names | grep -Go "/etc/nginx/sites-available/.*" | sort -u) if [[ -n $list_sites ]]; then echo "${blu}Sites found:${dim}" echo "$list_sites" | cut -d'/' -f 5 -s echo "${end}" else echo "${red}[ERROR] No sites were found!${end}" exit 1 fi local c=1 while [[ -n $(echo "$list_sites" | sed -n "${c}p" | cut -d'/' -f 5 -s) ]] do local domain=$(echo "$list_sites" | sed -n "${c}p" | cut -d'/' -f 5 -s) local c=$(($c+1)) if [[ -f /etc/nginx/sites-available/$domain && $overwrite != "on" ]]; then echo "${red}[ERROR] Site you are trying to import already exists! ${dim}(${domain})${end}" exit 1 elif [[ -f /etc/nginx/sites-available/$domain && $overwrite == "on" ]]; then echo "${blu}Site${end} $domain ${blu}already exists... ${dim} Overwriting...${end}" elif [[ ! -f /etc/nginx/sites-available/$domain && $overwrite == "on" ]]; then # If site don't exist, it's not really overwriting, so it can break things that should be there in an existing site. overwrite="" fi done sudo tar -Pxf $file # Database if [[ -s /var/www/webinoly_backup_db && $(conf_read mysql) == "true" && $(check_mysql_connection localhost) == "true" && -z $skip_db ]]; then sudo webinoly -db-import -file=/var/www/webinoly_backup_db sudo rm -rf /var/www/webinoly_backup_db local db_create_users="true" elif [[ -f /var/www/webinoly_backup_db && ( $(conf_read mysql) != "true" || $(check_mysql_connection localhost) != "true" ) && -z $skip_db ]]; then echo "${red}[ERROR] Database backup found but can not be restored because MySQL/MariaDB is not installed!${end}" # REMOVE: Just for legacy support, folder with domain is not used anymore! elif [[ -f /var/www/$domain/webinoly_backup_db && -z $skip_db ]]; then echo "${red}[ERROR] Database backup found but can not be restored because is using an old and incompatible format, you need to create a new exported file!${end}" fi # Custom Cache settings if [[ -f /var/www/webinoly.conf_temp ]]; then if [[ ! -f /etc/nginx/conf.d/webinoly.conf ]]; then sudo touch /etc/nginx/conf.d/webinoly.conf sudo chmod 644 /etc/nginx/conf.d/webinoly.conf sudo chown -R root:root /etc/nginx/conf.d/webinoly.conf fi sudo cat /var/www/webinoly.conf_temp >> /etc/nginx/conf.d/webinoly.conf sudo rm -rf /var/www/webinoly.conf_temp fi local c=1 while [[ -f /etc/nginx/sites-available/$(echo "$list_sites" | sed -n "${c}p" | cut -d'/' -f 5 -s) ]] do local domain=$(echo "$list_sites" | sed -n "${c}p" | cut -d'/' -f 5 -s) local c=$(($c+1)) # Remove old configurations remove_nginx_default_server $domain [[ $(is_ssl $domain) == "true" ]] && sudo site $domain -ssl=off -revoke=off > /dev/null 2>&1 if [[ $(conf_read php) != "true" && ( $(is_wp $domain) == "true" || $(is_php $domain) == "true" ) ]]; then echo "${red}[WARNING] Your site${bol} $domain ${end}${red}may not work because PHP is not installed!${end}" fi # Database recovery - create users because they are not imported if [[ -n $db_create_users && $overwrite != "on" && $(is_wp $domain) == "true" ]]; then wp_conf_retrieve $domain false false if [[ $wp_dbhost_host == "localhost" && -n $wp_dbname && -n $wp_dbuser && -n $wp_dbpass && -n $(sudo mysqlshow --user=admin "${mysql_params[@]}" | grep -ow $wp_dbname) ]]; then echo "${blu}${dim}Restoring database for${end}${dim} $domain ${blu}${dim}site!${end}" # Check if user exists if [[ -z $(sudo mysql --connect-timeout=10 --user=admin -e "SELECT User FROM mysql.user;" "${mysql_params[@]}" | grep -ow $wp_dbuser) ]]; then sudo mysql --connect-timeout=10 --user=admin "${mysql_params[@]}" <<_EOF_ CREATE USER '${wp_dbuser}'@'localhost' IDENTIFIED BY '${wp_dbpass}'; GRANT $(db_user_role) on ${wp_dbname}.* to '${wp_dbuser}'@'localhost'; FLUSH PRIVILEGES; _EOF_ elif [[ $(check_mysql_connection localhost $wp_dbuser $wp_dbpass $wp_dbname $mysql_param) != "true" ]]; then echo "${red}${dim}[ERROR] Database cannot be restored, seems like a user with the same name already exists.${end}" else echo "${blu}${dim}DB user already exists and a successful connection can be established, so we will use it.${end}" fi fi fi # Check if site contains WP in subfolders and include these db's. if [[ -n $db_create_users && $overwrite != "on" ]]; then for sit in "/etc/nginx/apps.d/${domain}_"*-wpcommon.conf do local suby="/$(echo $sit | cut -f 2- -d "_" -s | cut -f -1 -d "-" -s | sed "s/_/\//g")" if [[ -n $suby && -f /var/www/$domain/htdocs$suby/wp-config.php ]]; then wp_conf_retrieve $domain false false $suby if [[ $wp_dbhost_host == "localhost" && -n $wp_dbname && -n $wp_dbuser && -n $wp_dbpass && -n $(sudo mysqlshow --user=admin "${mysql_params[@]}" | grep -ow $wp_dbname) ]]; then echo "${blu}${dim}Restoring database for${end}${dim} ${domain}${suby} ${blu}${dim}site!${end}" # Check if user exists if [[ -z $(sudo mysql --connect-timeout=10 --user=admin -e "SELECT User FROM mysql.user;" "${mysql_params[@]}" | grep -ow $wp_dbuser) ]]; then sudo mysql --connect-timeout=10 --user=admin "${mysql_params[@]}" <<_EOF_ CREATE USER '${wp_dbuser}'@'localhost' IDENTIFIED BY '${wp_dbpass}'; GRANT $(db_user_role) on ${wp_dbname}.* to '${wp_dbuser}'@'localhost'; FLUSH PRIVILEGES; _EOF_ elif [[ $(check_mysql_connection localhost $wp_dbuser $wp_dbpass $wp_dbname $mysql_param) != "true" ]]; then echo "${red}${dim}[ERROR] Database cannot be restored, seems like a user with the same name already exists.${end}" else echo "${blu}${dim}DB user already exists and a successful connection can be established, so we will use it.${end}" fi fi fi local suby="" done fi echo "${gre}Webinoly Site Backup${blu} $domain ${gre}imported successfully!${end}" done } import_server() { [[ -z $file || $file == "true" ]] && read -p "${blu}Path file to import: ${end}" file if [[ ! -s $file ]]; then echo "${red}[ERROR] File not found or empty!${end}" exit 1 elif ! [[ $import =~ ^(true|sites|stack|full)$ ]]; then echo "${red}[ERROR] Invalid value for import parameter!${end}" exit 1 elif [[ -n $(sudo file $file | grep -Fo " tar archive ") ]]; then is_tar="true" fi if [[ $is_tar != "true" ]]; then if [[ -z $(grep -w "^app-version:.*" $file | cut -f 2 -d ':') ]]; then echo "${red}[ERROR] Invalid File, Webinoly configuration not found!${end}" exit 1 elif ! [[ $import =~ ^(true|stack)$ ]]; then echo "${red}[ERROR] Invalid value for import parameter!${end}" exit 1 fi [[ $import == "true" ]] && import="stack" echo "${gre}${dim}Webinoly single file Stack Configuration found...${end}" elif ! tar -tvf $file --absolute-names | grep -o "/opt/webinoly/webinoly.conf.exported"; then if tar -tvf $file --absolute-names | grep -Goq "/etc/nginx/sites-available/.*"; then import_site return else echo "${red}[ERROR] Invalid File, Webinoly configuration not found!${end}" exit 1 fi fi if [[ $import =~ ^(true|stack|full)$ ]]; then [[ $is_tar == "true" ]] && tar -C / -xf $file /opt/webinoly/webinoly.conf.exported --absolute-names || sudo cp $file /opt/webinoly/webinoly.conf.exported [[ $is_tar == "true" && $import == "true" ]] && import="full" check_exported_conf echo "${gre}Webinoly Stack Server was built using your imported configuration!${end}" fi if [[ $import =~ ^(true|sites|full)$ ]]; then sudo tar -Pxf $file # Check imported configuration file to prevent issues [[ ! -f /opt/webinoly/webinoly.conf.exported ]] && echo "${red}[ERROR] Exported Configuration file not found!${end}" if [[ $(conf_read_exported php) == "true" && $(conf_read php) != "true" ]]; then echo "${red}[ERROR] PHP is not installed, some exported files may not work properly!${end}" fi if [[ $(conf_read_exported server-version) != $(conf_read server-version) ]]; then echo "${red}${dim}[WARNING] Imported file was created using an older configuration, the new stack built can differ!${end}" fi if [[ -s /var/www/webinoly_backup_dball && $(conf_read mysql) == "true" && -z $skip_db ]]; then sudo webinoly -db-import -file=/var/www/webinoly_backup_dball [[ -n $(conf_read_exported mysql-root) ]] && conf_write mysql-root $(conf_read_exported mysql-root) [[ -n $(conf_read_exported mysql-admin) ]] && conf_write mysql-admin $(conf_read_exported mysql-admin) sudo rm -rf /var/www/webinoly_backup_dball sudo webinoly -server-reset=mysql elif [[ -s /var/www/webinoly_backup_dball && $(conf_read mysql) != "true" && -z $skip_db ]]; then echo "${red}[ERROR] Database backup found but can not be restored because MySQL/MariaDB is not installed!${end}" fi [[ $import != "sites" ]] && echo "${gre}Webinoly Full Server Backup imported successfully!${end}" || echo "${gre}Webinoly Backup imported successfully!${end}" fi # SMTP Configuration - should be here after sites are extracted and only if sites are imported. [[ $import == "full" && $(conf_read_exported smtp) == "true" && -s /var/www/webinoly_backup_smtp ]] && smtp_backup_recovery # Default-Site and Tools Site Settings! - should be here after sites are extracted and only if sites are imported. if [[ $import =~ ^(sites|stack|full)$ ]]; then if [[ -f /etc/nginx/sites-available/$(conf_read_exported default-site) ]]; then remove_nginx_default_server $(conf_read_exported default-site) sudo webinoly -default-site=$(conf_read_exported default-site) fi [[ -f /etc/nginx/sites-available/$(conf_read_exported tools-site) ]] && sudo webinoly -tools-site=$(conf_read_exported tools-site) fi sudo mv /opt/webinoly/webinoly.conf.exported /opt/webinoly/webinoly.conf.imported_$(date +%F)-$(date +%T) } bkp_wizard() { echo "${gre}" echo " ***********************************" echo " ************ Backups ************" echo " ***********************************" echo "${blu}" echo " 1 - Add AWS S3 Credentials" echo " 2 - AWS S3 directory backup" echo " 3 - WordPress Database local backup" echo " 4 - Restore backup from S3" echo " 5 - Run S3 backup" echo " 6 - Delete profile" echo " 7 - Profile info" echo " 8 - List profiles" echo " 9 - Export site" echo " 10 - Export server" echo " 11 - Import server/site" echo "${gre}" read -p "What do you want to do? ${end}" wzd echo "" if [[ $wzd == 1 ]]; then webinoly -aws-s3-credentials elif [[ $wzd == 2 ]]; then bkp_s3_profile elif [[ $wzd == 3 ]]; then bkp_local_db elif [[ $wzd == 4 ]]; then restore="true" bkp_s3_profile elif [[ $wzd == 5 ]]; then run="true" bkp_s3_profile elif [[ $wzd == 6 ]]; then delete="true" bkp_s3_profile elif [[ $wzd == 7 ]]; then info="true" bkp_s3_profile elif [[ $wzd == 8 ]]; then bkp_s3_list elif [[ $wzd == 9 ]]; then read -p "${blu}Site to export: ${end}" site sudo webinoly -backup=local -export=$site elif [[ $wzd == 10 ]]; then export_server elif [[ $wzd == 11 ]]; then import_server else echo "${red}[ERROR] Please, enter a valid option!${end}" exit 1 fi }