diff --git a/README.md b/README.md index e26d4cf..eda7151 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Automatically clean up old WordPress backup files to free up disk space. - Scans all WordPress installations in Plesk vhosts - Removes backups older than a specified number of days (default: 365 days) - Always keeps a minimum number of most recent backups per domain (default: 3) -- Optional email report of backups found/removed per domain +- Optional HTML email report of backups found/removed per domain - Dry-run mode to preview deletions without removing files - Configurable retention period via environment variables - Safe deletion with proper error handling @@ -345,10 +345,10 @@ pci-dss-scan\pci-dss-scan.bat https://example.com - `DRY_RUN` - Set to `true` to enable dry-run mode (default: `false`) - Example: `DRY_RUN=true` previews deletions without removing files - `EMAIL_TO` - Email address to receive the per-domain report (default: unset, no email sent) - - If `SMTP_SERVER` is set, sends directly via `curl` (bypassing the local `mail` command entirely); otherwise falls back to the local `mail` command - - Report includes, per domain: backups found, backups removed (or would-remove in dry-run), and filenames removed + - Sent as an HTML email. If `SMTP_SERVER` is set, sends directly via `curl` (bypassing the local `mail` command entirely); otherwise falls back to the local `mail` command + - Report includes a summary, backups removed (or would-remove in dry-run) per domain with filenames, and a backups-found table per domain - `EMAIL_SUBJECT` - Subject line for the email report (default: `WordPress Backup Cleanup Report - `) -- `SMTP_SERVER` - SMTP relay hostname, used only if the local `mail` command is unavailable (default: unset) +- `SMTP_SERVER` - SMTP relay hostname; when set, takes priority over the local `mail` command (default: unset) - `SMTP_PORT` - SMTP relay port (default: `25`) - `SMTP_AUTH_USER` / `SMTP_AUTH_PASS` - SMTP credentials, leave unset for an unauthenticated relay (default: unset) - `SMTP_SECURE` - SMTP security: blank for plain, `ssl` for implicit TLS (typically port 465), `starttls` for explicit STARTTLS (typically port 587) (default: blank) diff --git a/remove-old-wordpress-backups/remove-wordpress-backups.sh b/remove-old-wordpress-backups/remove-wordpress-backups.sh index c456329..453bc46 100644 --- a/remove-old-wordpress-backups/remove-wordpress-backups.sh +++ b/remove-old-wordpress-backups/remove-wordpress-backups.sh @@ -6,7 +6,7 @@ # - Removes backups older than specified retention period # - Configurable retention via DAYS environment variable (default: 365 days) # - Keeps a minimum number of most recent backups per domain regardless of age -# - Optional email report summarizing backups found/removed per domain +# - Optional HTML email report summarizing backups found/removed per domain # - Dry-run mode to preview deletions without removing files # - Safe deletion with proper error handling and validation # - Detailed logging with timestamps @@ -326,6 +326,77 @@ build_report() { printf '%s\n' "${counts_section[@]}" } +# Escapes text for safe inclusion in HTML +html_escape() { + sed 's/&/\&/g; s//\>/g' +} + +# Function to build an HTML version of the report for a nicer-looking email. +# Console/dry-run output still uses the plain-text build_report() above. +build_html_report() { + local action="removed" action_title="Removed" + if [ "${DRY_RUN}" = "true" ]; then + action="would be removed" + action_title="Would remove (dry-run)" + fi + + local i domain found removed_files removed_count row_bg + local total_found=0 total_removed=0 + local removed_html="" counts_html="" + + for i in "${!DOMAIN_NAMES[@]}"; do + domain=$(printf '%s' "${DOMAIN_NAMES[$i]}" | html_escape) + found="${DOMAIN_FOUND[$i]}" + removed_files="${DOMAIN_REMOVED_FILES[$i]}" + removed_count=0 + [ -n "${removed_files}" ] && removed_count=$(printf '%s' "${removed_files}" | grep -c .) + + total_found=$((total_found + found)) + total_removed=$((total_removed + removed_count)) + + row_bg="#ffffff" + [ $(( i % 2 )) -eq 1 ] && row_bg="#f7f7f7" + counts_html+="${domain}${found}" + + if [ "${removed_count}" -gt 0 ]; then + local files_html="" + while IFS= read -r f; do + [ -n "${f}" ] && files_html+="
  • $(printf '%s' "${f}" | html_escape)
  • " + done <<<"${removed_files}" + removed_html+="
  • ${domain} - ${action_title}: ${removed_count}
  • " + fi + done + + [ -n "${removed_html}" ] || removed_html="
  • (none)
  • " + + cat < + +

    WordPress Backup Cleanup Report

    +

    $(hostname -f 2>/dev/null || hostname) — $(date '+%Y-%m-%d %H:%M:%S')

    + + + + + +
    Domains scanned${#DOMAIN_NAMES[@]}
    Backups found${total_found}
    Backups ${action}${total_removed}
    + +

    Backups ${action} by domain

    + + +

    Backups found by domain

    + + + + + + ${counts_html} +
    DomainBackups Found
    + + +EOF +} + # Function to send the report via an SMTP relay using curl, for servers with no local MTA. # SMTP_SECURE: blank for plain, "ssl" for implicit TLS (typically port 465), "starttls" for # explicit STARTTLS (typically port 587) — same convention as monitor-aspnet.bat. @@ -341,6 +412,8 @@ send_via_smtp() { echo "From: ${from}" echo "To: ${EMAIL_TO}" echo "Subject: ${subject}" + echo "MIME-Version: 1.0" + echo "Content-Type: text/html; charset=UTF-8" echo "Date: $(date -R)" echo "" echo "${body}" @@ -359,10 +432,12 @@ send_via_smtp() { return "${rc}" } -# Function to email the report when EMAIL_TO is configured. If SMTP_SERVER is -# explicitly set, it takes priority (sends directly via curl) so an explicit -# relay config always wins over a possibly-misconfigured local MTA. Falls -# back to the local 'mail' command otherwise. +# Function to email the report when EMAIL_TO is configured. Sends the HTML +# report built by build_html_report(). If SMTP_SERVER is explicitly set, it +# takes priority (sends directly via curl) so an explicit relay config always +# wins over a possibly-misconfigured local MTA. Falls back to the local +# 'mail' command otherwise (using -a for the HTML content-type header, as +# supported by mailx/s-nail/bsd-mailx). send_email_report() { local body="$1" @@ -380,7 +455,7 @@ send_email_report() { fi if command -v mail >/dev/null 2>&1; then - if echo "${body}" | mail -s "${subject}" "${EMAIL_TO}"; then + if echo "${body}" | mail -a "Content-Type: text/html; charset=UTF-8" -s "${subject}" "${EMAIL_TO}"; then log_message "Report emailed to ${EMAIL_TO}" return 0 fi @@ -418,14 +493,15 @@ remove_wordpress_backups() { scan_domains local file_count=${#TO_DELETE[@]} - local report + local report html_report report=$(build_report) + html_report=$(build_html_report) if [ "${file_count}" -eq 0 ]; then log_message "No eligible backup files found. Nothing to delete." echo "" echo "${report}" - send_email_report "${report}" + send_email_report "${html_report}" return 0 fi @@ -437,19 +513,17 @@ remove_wordpress_backups() { if [ "${DRY_RUN}" = "true" ]; then log_message "Dry-run complete. ${file_count} file(s) would be deleted." log_message "Run without --dry-run flag to actually delete these files." - send_email_report "${report}" + send_email_report "${html_report}" return 0 fi # Remove old backup files if printf '%s\0' "${TO_DELETE[@]}" | xargs -0 "${RM_CMD}" -f; then log_message "Successfully removed ${file_count} old backup file(s)." - send_email_report "${report}" + send_email_report "${html_report}" else log_message "ERROR: Failed to remove some backup files. Check permissions." - send_email_report "${report} - -WARNING: one or more files could not be removed - check server permissions/logs." + send_email_report "${html_report}

    WARNING: one or more files could not be removed - check server permissions/logs.

    " return 1 fi