#!/bin/bash BASE_URL=$2 BACKUP_TYPE=$3 BACKUP_LOG_FILE=$4 ENV_NAME=$5 BACKUP_COUNT=$6 APP_PATH=$7 USER_SESSION=$8 USER_EMAIL=$9 BACKUP_PATH=${10} # Ensure restic is installed if ! which restic &>/dev/null; then if which dnf &>/dev/null; then dnf install -y epel-release dnf install -y restic elif which yum &>/dev/null; then yum-config-manager --add-repo https://copr.fedorainfracloud.org/coprs/copart/restic/repo/epel-7/copart-restic-epel-7.repo yum-config-manager --enable copart-restic yum -y install restic yum-config-manager --disable copart-restic fi fi # Function definitions remain the same, but adjustments are made to use $BACKUP_PATH function update_restic(){ restic self-update 2>&1; } function check_backup_repo() { local backup_repo_path="/mnt/backups/${ENV_NAME}" local password_file="/etc/restic-password" # Create the backup repository path if it does not exist [ -d "${backup_repo_path}" ] || mkdir -p "${backup_repo_path}" # Check if the password file exists, otherwise create a new random password if [ -f "$password_file" ]; then stored_password=$(cat "$password_file") if [ -z "$stored_password" ] || [[ ${#stored_password} -lt 10 ]]; then # Generate a new password if the existing one is less than 10 characters echo "$(date) Password is invalid or too short. Generating a new password." | tee -a "${BACKUP_LOG_FILE}" stored_password=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 10) echo "$stored_password" > "$password_file" fi else # Generate and save a random 10-character password stored_password=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 10) echo "$stored_password" > "$password_file" echo "$(date) Password file created with password: ${stored_password}" | tee -a "${BACKUP_LOG_FILE}" fi # Export the repository password export RESTIC_PASSWORD="$stored_password" export RESTIC_REPOSITORY="$backup_repo_path" export FILES_COUNT=$(find "${backup_repo_path}" -mindepth 1 | wc -l) # Check repository integrity if it has files, otherwise initialize it if [ "${FILES_COUNT}" -gt 0 ]; then echo "$(date) ${ENV_NAME} Checking the backup repository integrity and consistency" | tee -a "${BACKUP_LOG_FILE}" if [[ $(ls -A "${backup_repo_path}/locks") ]]; then echo "$(date) ${ENV_NAME} Backup repository has a stale lock, removing" | tee -a "${BACKUP_LOG_FILE}" GOGC=20 restic -r "${backup_repo_path}" unlock sendEmailNotification fi if ! GOGC=20 restic -q -r "${backup_repo_path}" check --read-data-subset=5%; then echo "Backup repository integrity check failed." | tee -a "${BACKUP_LOG_FILE}" exit 1 fi else echo "$(date) ${ENV_NAME} Initializing new backup repository" | tee -a "${BACKUP_LOG_FILE}" GOGC=20 restic init -r "${backup_repo_path}" fi } function sendEmailNotification() { if [ -e "/usr/lib/jelastic/modules/api.module" ]; then [ -e "/var/run/jem.pid" ] && return 0; CURRENT_PLATFORM_MAJOR_VERSION=$(jem api apicall -s --connect-timeout 3 --max-time 15 [API_DOMAIN]/1.0/statistic/system/rest/getversion 2>/dev/null | jq .version | grep -o [0-9.]* | awk -F . '{print $1}') if [ "${CURRENT_PLATFORM_MAJOR_VERSION}" -ge "7" ]; then echo "$(date) ${ENV_NAME} Sending e-mail notification about removing the stale lock" | tee -a "$BACKUP_LOG_FILE" SUBJECT="Stale lock is removed on ${BACKUP_PATH}/${ENV_NAME} backup repo" BODY="Please pay attention to ${BACKUP_PATH}/${ENV_NAME} backup repo because the stale lock left from previous operation is removed during the integrity check and backup rotation. Manual check of backup repo integrity and consistency is highly desired." jem api apicall -s --connect-timeout 3 --max-time 15 [API_DOMAIN]/1.0/message/email/rest/send --data-urlencode "session=$USER_SESSION" --data-urlencode "to=$USER_EMAIL" --data-urlencode "subject=$SUBJECT" --data-urlencode "body=$BODY" if [[ $? != 0 ]]; then echo "$(date) ${ENV_NAME} Sending of e-mail notification failed" | tee -a "$BACKUP_LOG_FILE" else echo "$(date) ${ENV_NAME} E-mail notification is sent successfully" | tee -a "$BACKUP_LOG_FILE" fi elif [ -z "${CURRENT_PLATFORM_MAJOR_VERSION}" ]; then echo "$(date) ${ENV_NAME} Error when checking the platform version" | tee -a "$BACKUP_LOG_FILE" else echo "$(date) ${ENV_NAME} Email notification is not sent because this functionality is unavailable for current platform version." | tee -a "$BACKUP_LOG_FILE" fi else echo "$(date) ${ENV_NAME} Email notification is not sent because this functionality is unavailable for current platform version." | tee -a "$BACKUP_LOG_FILE" fi } function rotate_snapshots(){ local backup_repo_path="/mnt/backups/${ENV_NAME}" echo "$(date) ${ENV_NAME} Rotating snapshots by keeping the last ${BACKUP_COUNT}" | tee -a "${BACKUP_LOG_FILE}" if [[ $(ls -A "${backup_repo_path}/locks") ]]; then echo "$(date) ${ENV_NAME} Backup repository has a stale lock, removing" | tee -a "${BACKUP_LOG_FILE}" GOGC=20 RESTIC_PASSWORD="${ENV_NAME}" restic -r "${backup_repo_path}" unlock sendEmailNotification fi if ! GOGC=20 RESTIC_COMPRESSION=off RESTIC_PACK_SIZE=8 RESTIC_PASSWORD="${ENV_NAME}" restic forget -q -r "${backup_repo_path}" --keep-last "${BACKUP_COUNT}" --prune | tee -a "${BACKUP_LOG_FILE}"; then echo "Backup rotation failed." | tee -a "${BACKUP_LOG_FILE}" exit 1 fi } function create_snapshot(){ local backup_repo_path="/mnt/backups/${ENV_NAME}" DUMP_NAME=$(date "+%F_%H%M%S_%Z") echo "$(date) ${ENV_NAME} Begin uploading the ${DUMP_NAME} snapshot to backup storage" | tee -a "${BACKUP_LOG_FILE}" if ! GOGC=20 RESTIC_COMPRESSION=off RESTIC_PACK_SIZE=8 RESTIC_READ_CONCURRENCY=8 RESTIC_PASSWORD="${ENV_NAME}" restic backup -q -r "${backup_repo_path}" --tag "${DUMP_NAME} ${BACKUP_ADDON_COMMIT_ID} ${BACKUP_TYPE}" "${APP_PATH}" ~/wp_db_backup.sql | tee -a "${BACKUP_LOG_FILE}"; then echo "Backup snapshot creation failed." | tee -a "${BACKUP_LOG_FILE}" exit 1 fi echo "$(date) ${ENV_NAME} End uploading the ${DUMP_NAME} snapshot to backup storage" | tee -a "${BACKUP_LOG_FILE}" } function backup_database() { local backup_repo_path="/mnt/backups/${ENV_NAME}/db" local log_file="/home/litespeed/mbmanager/logs/${ENV_NAME}_db_backup.log" # Ensure the backup and log directories exist mkdir -p "${backup_repo_path}" mkdir -p "$(dirname "${log_file}")" echo "$(date) ${ENV_NAME} Starting database backup..." | tee -a "${log_file}" # Extract database credentials from wp-config.php DB_NAME=$(grep DB_NAME /var/www/webroot/ROOT/wp-config.php | cut -d "'" -f 4) DB_USER=$(grep DB_USER /var/www/webroot/ROOT/wp-config.php | cut -d "'" -f 4) DB_PASSWORD=$(grep DB_PASSWORD /var/www/webroot/ROOT/wp-config.php | cut -d "'" -f 4) DB_HOST=$(grep DB_HOST /var/www/webroot/ROOT/wp-config.php | cut -d "'" -f 4 | awk -F':' '{print $1}') DB_PORT=$(grep DB_HOST /var/www/webroot/ROOT/wp-config.php | cut -d "'" -f 4 | awk -F':' '{print $2}' | sed 's/)//') DB_PORT=${DB_PORT:-3306} # Determine the correct --column-statistics option based on MySQL or MariaDB version local column_statistics_option="" SERVER_VERSION=$(mysql -h "${DB_HOST}" -u "${DB_USER}" -p"${DB_PASSWORD}" -e 'SELECT VERSION();' -s -N) if [[ "${SERVER_VERSION}" =~ ^5\.7|^8\.0|MariaDB ]]; then column_statistics_option="--column-statistics=0" fi # Create a database dump local dump_file="${backup_repo_path}/${ENV_NAME}_$(date "+%Y-%m-%d_%H-%M-%S").sql" mysqldump -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASSWORD}" ${column_statistics_option} --force --single-transaction --quote-names --opt "${DB_NAME}" > "${dump_file}" if [ $? -ne 0 ]; then echo "$(date) ${ENV_NAME} Database backup process failed." | tee -a "${log_file}" exit 1 fi # Backup the dump file using Restic restic -r "${backup_repo_path}" backup "${dump_file}" --tag "db" --host "${ENV_NAME}" if [ $? -ne 0 ]; then echo "$(date) ${ENV_NAME} Restic backup process failed." | tee -a "${log_file}" exit 1 fi echo "$(date) ${ENV_NAME} Database backup completed successfully." | tee -a "${log_file}" # Optionally, remove the dump file after successful backup rm -f "${dump_file}" } function backup_wp_core() { echo "Starting backup of WordPress core files excluding uploads directory..." # Define the path to the WordPress installation and the backup tag local wp_path="$APP_PATH" local backup_tag="wp-core-$(date "+%F_%H%M%S_%Z")" # Exclude the uploads directory using the --exclude option GOGC=20 RESTIC_PASSWORD="$RESTIC_PASSWORD" restic -r "$backupPath" backup \ --tag "$backup_tag" \ --exclude="$wp_path/wp-content/uploads" \ "$wp_path" echo "WordPress core files backup completed." } function backup_uploads() { echo "Starting backup of WordPress uploads directory..." # Define the path to the uploads directory and the backup tag local uploads_path="$APP_PATH/wp-content/uploads" local backup_tag="wp-uploads-$(date "+%F_%H%M%S_%Z")" # Perform the backup GOGC=20 RESTIC_PASSWORD="$RESTIC_PASSWORD" restic -r "$backupPath" backup \ --tag "$backup_tag" \ "$uploads_path" echo "WordPress uploads directory backup completed." } function backup_database() { echo "Starting backup of WordPress database..." # Define the backup tag and the path for the temporary SQL dump local backup_tag="wp-db-$(date "+%F_%H%M%S_%Z")" local sql_dump_path="/tmp/wp_db_backup.sql" # Create a database dump mysqldump -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" > "$sql_dump_path" # Perform the backup GOGC=20 RESTIC_PASSWORD="$RESTIC_PASSWORD" restic -r "$backupPath" backup \ --tag "$backup_tag" \ "$sql_dump_path" # Remove the temporary SQL dump file rm -f "$sql_dump_path" echo "WordPress database backup completed." } case "$1" in backup) backup_wp_core backup_uploads backup_database ;; backup_wp_core) backup_wp_core ;; backup_uploads) backup_uploads ;; backup_database) backup_database ;; check_backup_repo) check_backup_repo ;; rotate_snapshots) rotate_snapshots ;; create_snapshot) create_snapshot ;; update_restic) update_restic ;; *) echo "Usage: $0 {backup|backup_wp_core|backup_uploads|backup_database|check_backup_repo|rotate_snapshots|create_snapshot|update_restic}" exit 2 esac exit $?