mirror of
https://gh.wpcy.net/https://github.com/CaptainCore/captaincore.git
synced 2026-04-23 01:50:05 +08:00
202 lines
No EOL
7.5 KiB
Bash
202 lines
No EOL
7.5 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
#
|
|
# Backups one or more sites.
|
|
#
|
|
# `captaincore backup generate <site>`
|
|
#
|
|
# [--skip-remote]
|
|
# Skips saving to Restic backup repo
|
|
#
|
|
# [--skip-db]
|
|
# Skips database backup
|
|
#
|
|
|
|
if [ ${#@} -ne 1 ]; then
|
|
echo -e "${COLOR_RED}Error:${COLOR_NORMAL} Requires a <site>"
|
|
exit
|
|
fi
|
|
|
|
while read config; do
|
|
if [[ "$config" == "Error:"* ]]; then
|
|
continue
|
|
fi
|
|
declare "$config"
|
|
done <<< "$(php ${CAPTAINCORE_PATH}/lib/local-scripts/configs.php fetch)"
|
|
|
|
site=$1
|
|
root_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"; root_path=${root_path%app*}
|
|
|
|
run_command() {
|
|
|
|
runtime_start=$( date +%s )
|
|
|
|
if [[ $SKIP_REMOTE == true ]]; then
|
|
echo "Skipping remote"
|
|
fi
|
|
|
|
# Extract environment
|
|
if [[ "$site" == *"-staging"* ]]; then
|
|
environment=staging
|
|
fi
|
|
|
|
if [[ "$site" == *"-production"* ]]; then
|
|
environment=production
|
|
fi
|
|
|
|
if [[ "$site" != *"-"* ]]; then
|
|
environment=production
|
|
fi
|
|
|
|
# Load site configs
|
|
IFS=$'\n'$'\r'; for line in $(captaincore site get $site --bash --captain-id=$CAPTAIN_ID); do declare "$line"; done
|
|
|
|
# Site found, start the backup
|
|
if [[ $domain == "" ]]; then
|
|
echo "Error: $site missing domain. Skipping backup."
|
|
exit
|
|
fi
|
|
|
|
# Append trailing slash if home_directory exist
|
|
if [ "$home_directory" != "" ]; then
|
|
home_directory="${home_directory}/"
|
|
fi
|
|
|
|
# Define config file
|
|
rclone_config_file="$path/${site}_${site_id}/rclone.conf"
|
|
vault_file="$path/${site}_${site_id}/$environment/vault.txt"
|
|
if [[ ! -f "$rclone_config_file" || ! -f "$vault_file" ]]; then
|
|
captaincore site key-generate $site --captain-id=$CAPTAIN_ID
|
|
fi
|
|
|
|
# Lookup rclone
|
|
remote_check=$( rclone config show $environment --config="$rclone_config_file" )
|
|
remote_backup_check=$( rclone config show backup --config="$rclone_config_file" )
|
|
|
|
if [[ $remote_check == *"Couldn't find type of fs"* || $remote_backup_check == *"Couldn't find type of fs"* ]]; then
|
|
echo "$(date +'%Y-%m-%d %H:%M') Generating rclone configs for $site"
|
|
captaincore site key-generate $site --captain-id=$CAPTAIN_ID
|
|
fi
|
|
|
|
# Captures FTP errors in $ftp_output and file listing to log file
|
|
ftp_output=$( { rclone lsd ${environment}:$home_directory --config="$rclone_config_file" ; } 2>&1 )
|
|
ftp_search_for_wordpress=$( echo "$ftp_output" | perl -wnE'say for /wp-admin/g' )
|
|
|
|
# Handle FTP errors
|
|
if [[ $ftp_search_for_wordpress != "wp-admin"* ]]; then
|
|
wordpress_not_found=true
|
|
fi
|
|
|
|
if [[ "$backup_mode" == "direct" ]]; then
|
|
|
|
# --- 1. Generate a unique, random name for the temporary payload file ---
|
|
do_script_path="${root_path}lib/remote-scripts/_do"
|
|
random_token=$(head /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | head -c 8)
|
|
payload_file="$path_tmp/do_vault_payload-${CAPTAIN_ID}-${site}-${environment}_${random_token}.sh"
|
|
echo "📝 Creating temporary payload file at: $payload_file"
|
|
|
|
# --- 2. Read secrets and write 'export' commands to the payload file ---
|
|
# This block reads each line from your secrets file and builds the export commands.
|
|
{
|
|
echo "export B2_BUCKET='$(sed -n 1p "$vault_file")'"
|
|
echo "export B2_PATH='$(sed -n 2p "$vault_file")'"
|
|
echo "export B2_ACCOUNT_ID='$(sed -n 3p "$vault_file")'"
|
|
echo "export B2_ACCOUNT_KEY='$(sed -n 4p "$vault_file")'"
|
|
echo "export RESTIC_PASSWORD='$(sed -n 5p "$vault_file")'"
|
|
echo "export EMAIL_NOTIFY=$captaincore_admin_email"
|
|
} > "$payload_file"
|
|
|
|
# --- 3. Append the main _do script to the payload file ---
|
|
tail -n +2 "$do_script_path" >> "$payload_file"
|
|
|
|
# Error handling in case the secrets file is not found
|
|
if [ $? -ne 0 ]; then
|
|
echo "❌ Error: Failed to create export commands. Check vault_file path: '$vault_file'" >&2
|
|
rm -f "$payload_file"
|
|
exit 1
|
|
fi
|
|
|
|
ssh_string=$( captaincore ssh ${site}-${environment} --debug --command="bash -s -- vault create" --captain-id=$CAPTAIN_ID )
|
|
|
|
# --- Execute the command for real ---
|
|
echo "🚀 Executing remote vault command..."
|
|
cat $payload_file | eval $ssh_string
|
|
|
|
# --- 5. Clean up the local temporary file ---
|
|
echo "🔥 Cleaning up temporary payload file..."
|
|
rm "$payload_file"
|
|
|
|
captaincore backup list-generate ${site}-${environment} --captain-id=$CAPTAIN_ID
|
|
captaincore backup get-generate ${site}-${environment} --captain-id=$CAPTAIN_ID
|
|
|
|
captaincore usage-update ${site}-${environment} --captain-id=$CAPTAIN_ID
|
|
|
|
cd $path/${site}_${site_id}/${environment}/backups/
|
|
runtime_end=$( date +%s )
|
|
echo "$runtime_start $runtime_end" >> runtime
|
|
exit
|
|
|
|
fi
|
|
|
|
# Incremental backup locally with rclone
|
|
echo "$(date +'%Y-%m-%d %H:%M') Begin incremental backup ${site}-${environment} to local"
|
|
|
|
if [[ $SKIP_DB != true ]] && [[ $wordpress_not_found != true ]]; then
|
|
|
|
# Database backup
|
|
captaincore ssh ${site}-${environment} --script="db-backup" --captain-id=$CAPTAIN_ID
|
|
|
|
if [[ "$provider" == "wpengine" ]]; then
|
|
rclone sync ${environment}:_wpeprivate/database-backup.sql $path/${site}_${site_id}/${environment}/backup/ --config="$rclone_config_file"
|
|
fi
|
|
|
|
if [[ "$provider" == "kinsta" ]]; then
|
|
rclone sync ${environment}:private/database-backup.sql $path/${site}_${site_id}/${environment}/backup/ --config="$rclone_config_file"
|
|
fi
|
|
|
|
if [[ "$provider" == "rocketdotnet" ]]; then
|
|
rclone sync ${environment}:tmp/database-backup.sql $path/${site}_${site_id}/${environment}/backup/ --config="$rclone_config_file"
|
|
fi
|
|
|
|
fi
|
|
|
|
mkdir -p $path/${site}_${site_id}/${environment}/backup/
|
|
|
|
# Backup site locally
|
|
if [[ "$wp_content" != "wp-content" ]]; then
|
|
echo "$(date +'%Y-%m-%d %H:%M') Backing up ${site}-${environment} alternative wp-content location ($wp_content)"
|
|
rclone sync ${environment}:$home_directory $path/${site}_${site_id}/${environment}/backup/ --exclude-from="${CAPTAINCORE_PATH}/lib/excludes" --filter="+ $wp_content/**" --filter="- wp-content/**" --filter="- content/**" --config="$rclone_config_file"
|
|
else
|
|
rclone sync ${environment}:$home_directory $path/${site}_${site_id}/${environment}/backup/ --exclude-from="${CAPTAINCORE_PATH}/lib/excludes" --filter="- content/**" --config="$rclone_config_file"
|
|
fi
|
|
|
|
# Incremental backup upload to Restic
|
|
if [[ $SKIP_REMOTE != true ]]; then
|
|
echo "$(date +'%Y-%m-%d %H:%M') Storing $site to backup archive"
|
|
if [[ $( restic snapshots --repo rclone:$rclone_backup/${site}_${site_id}/${environment}/restic-repo --password-file="${CAPTAINCORE_PATH}/data/restic.key" ) == "" ]]; then
|
|
echo "Generating restic repo for $site"
|
|
restic init --quiet --repo rclone:$rclone_backup/${site}_${site_id}/${environment}/restic-repo --password-file="${CAPTAINCORE_PATH}/data/restic.key"
|
|
fi
|
|
cd $path/${site}_${site_id}/${environment}/backup/
|
|
restic backup . --quiet --repo rclone:$rclone_backup/${site}_${site_id}/${environment}/restic-repo --exclude-file="${CAPTAINCORE_PATH}/lib/restic-excludes" --password-file="${CAPTAINCORE_PATH}/data/restic.key"
|
|
captaincore backup list-generate ${site}-${environment} --captain-id=$CAPTAIN_ID
|
|
captaincore backup get-generate ${site}-${environment} --captain-id=$CAPTAIN_ID
|
|
fi
|
|
|
|
captaincore usage-update ${site}-${environment} --captain-id=$CAPTAIN_ID
|
|
|
|
if [ -f "${path}/process-${process_id}-progress.log" ]; then
|
|
echo -n "." >> ${path}/process-${process_id}-progress.log
|
|
fi
|
|
|
|
if [ ! -d "$path/${site}_${site_id}/${environment}/backups/" ]; then
|
|
mkdir -p "$path/${site}_${site_id}/${environment}/backups"
|
|
fi
|
|
|
|
cd $path/${site}_${site_id}/${environment}/backups/
|
|
runtime_end=$( date +%s )
|
|
echo "$runtime_start $runtime_end" >> runtime
|
|
|
|
}
|
|
|
|
run_command |