From a500e90483887ecc4db4526e3c833d4938d41b84 Mon Sep 17 00:00:00 2001 From: cschantz Date: Fri, 7 Nov 2025 17:05:08 -0500 Subject: [PATCH] Add WordPress Cron Manager with intelligent load distribution Features: - Scan for all WordPress installations on server - Disable wp-cron for specific domain, user, or server-wide - Check wp-cron status for any domain or user - Automatic wp-config.php backups before changes - Intelligent cron job staggering to prevent load spikes Load Distribution: - Staggers cron times across 15-minute windows - Example with 300 sites: distributes across minutes 0-14 - Site 1: runs at 0,15,30,45 - Site 2: runs at 1,16,31,46 - Site 3: runs at 2,17,32,47 - ...continues up to minute 14, then wraps - Prevents all sites from running simultaneously - Uses user crontabs (not system cron) for proper permissions Technical Details: - Adds DISABLE_WP_CRON to wp-config.php - Creates user-specific crontab entries - Prevents duplicate cron jobs - Shows cron timing when adding jobs - Handles multiple WP installations per user --- modules/website/wordpress-cron-manager.sh | 463 ++++++++++++++++++++++ 1 file changed, 463 insertions(+) create mode 100755 modules/website/wordpress-cron-manager.sh diff --git a/modules/website/wordpress-cron-manager.sh b/modules/website/wordpress-cron-manager.sh new file mode 100755 index 0000000..39500d8 --- /dev/null +++ b/modules/website/wordpress-cron-manager.sh @@ -0,0 +1,463 @@ +#!/bin/bash + +################################################################################ +# WordPress Cron Manager +################################################################################ +# Purpose: Disable wp-cron and convert to real system cron jobs +# Features: +# - Detect all WordPress installations +# - Disable DISABLE_WP_CRON in wp-config.php +# - Add proper cron jobs for scheduled tasks +# - Server-wide, per-user, or per-domain operations +################################################################################ + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +source "$SCRIPT_DIR/lib/common-functions.sh" +source "$SCRIPT_DIR/lib/system-detect.sh" + +if [ "$EUID" -ne 0 ]; then + print_error "This script must be run as root" + exit 1 +fi + +# Global counter for staggering cron times +CRON_OFFSET=0 + +# Function to generate staggered cron time +# Distributes jobs across 15 minutes to avoid load spikes +generate_staggered_cron() { + local minute=$((CRON_OFFSET % 15)) + + # Create pattern: 0,15,30,45 but offset by the calculated minute + local minutes="" + for base in 0 15 30 45; do + local actual_minute=$(( (base + minute) % 60 )) + if [ -z "$minutes" ]; then + minutes="$actual_minute" + else + minutes="$minutes,$actual_minute" + fi + done + + # Increment offset for next site (wraps at 15) + CRON_OFFSET=$((CRON_OFFSET + 1)) + + echo "$minutes * * * *" +} + +clear +print_banner "WordPress Cron Manager" + +echo "" +echo -e "${BOLD}What would you like to do?${NC}" +echo "" +echo " 1) Scan for WordPress installations" +echo " 2) Disable wp-cron for specific domain" +echo " 3) Disable wp-cron for specific user (all their WP sites)" +echo " 4) Disable wp-cron server-wide (all WordPress sites)" +echo " 5) Check wp-cron status for domain/user" +echo " 0) Return to menu" +echo "" +echo -n "Select option [0]: " +read -r choice +choice="${choice:-0}" + +case "$choice" in + 1) + # Scan for WordPress installations + echo "" + print_banner "WordPress Installation Scanner" + echo "" + + echo "Scanning for WordPress installations..." + echo "" + + # Find all wp-config.php files in home directories + wp_sites=$(find /home/*/public_html -name "wp-config.php" -type f 2>/dev/null) + + if [ -z "$wp_sites" ]; then + echo -e "${YELLOW}No WordPress installations found${NC}" + else + count=0 + echo -e "${BOLD}Found WordPress Installations:${NC}" + echo "" + + while IFS= read -r config_file; do + count=$((count + 1)) + + # Extract info + site_path=$(dirname "$config_file") + user=$(echo "$site_path" | cut -d'/' -f3) + + # Try to find domain + domain="(unknown domain)" + if [ -f "/var/cpanel/userdata/$user/main" ]; then + domain=$(grep -m1 "^servername:" "/var/cpanel/userdata/$user/main" 2>/dev/null | awk '{print $2}') + fi + + # Check if wp-cron is disabled + if grep -q "define.*DISABLE_WP_CRON.*true" "$config_file" 2>/dev/null; then + status="${GREEN}✓ Disabled (using system cron)${NC}" + else + status="${YELLOW}⚠ Enabled (default wp-cron)${NC}" + fi + + echo -e "${count}. ${BOLD}$domain${NC}" + echo " Path: $site_path" + echo " User: $user" + echo " Status: $status" + echo "" + done <<< "$wp_sites" + + echo -e "${CYAN}Total WordPress installations: $count${NC}" + fi + ;; + + 2) + # Disable wp-cron for specific domain + echo "" + echo -n "Enter domain name: " + read -r domain + + if [ -z "$domain" ]; then + print_error "Domain cannot be empty" + press_enter + exit 1 + fi + + # Find WordPress installation for this domain + echo "" + echo "Searching for WordPress installation for $domain..." + + # Try to find via cPanel user data + wp_config="" + for userdata_file in /var/cpanel/userdata/*/main; do + if grep -q "^servername: $domain" "$userdata_file" 2>/dev/null; then + user=$(basename "$(dirname "$userdata_file")") + potential_config="/home/$user/public_html/wp-config.php" + if [ -f "$potential_config" ]; then + wp_config="$potential_config" + break + fi + fi + done + + if [ -z "$wp_config" ]; then + print_error "WordPress installation not found for $domain" + press_enter + exit 1 + fi + + echo -e "${GREEN}Found WordPress:${NC} $wp_config" + echo "" + + # Check if already disabled + if grep -q "define.*DISABLE_WP_CRON.*true" "$wp_config" 2>/dev/null; then + echo -e "${YELLOW}wp-cron is already disabled for this site${NC}" + echo "" + echo -n "Re-configure anyway? (y/n) [n]: " + read -r confirm + if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then + press_enter + exit 0 + fi + fi + + # Backup wp-config.php + cp "$wp_config" "${wp_config}.backup-$(date +%Y%m%d-%H%M%S)" + echo -e "${GREEN}✓${NC} Backed up wp-config.php" + + # Add DISABLE_WP_CRON to wp-config.php + if grep -q "DISABLE_WP_CRON" "$wp_config" 2>/dev/null; then + # Replace existing + sed -i "s/define.*DISABLE_WP_CRON.*/define('DISABLE_WP_CRON', true);/" "$wp_config" + else + # Add after opening PHP tag + sed -i "//dev/null 2>&1" + + # Add to user's crontab + user=$(echo "$site_path" | cut -d'/' -f3) + + # Check if cron job already exists + if crontab -u "$user" -l 2>/dev/null | grep -q "$site_path.*wp-cron.php"; then + echo -e "${YELLOW}⚠${NC} Cron job already exists for this site" + else + # Generate staggered cron time + cron_time=$(generate_staggered_cron) + (crontab -u "$user" -l 2>/dev/null; echo "$cron_time $cron_cmd") | crontab -u "$user" - + echo -e "${GREEN}✓${NC} Added cron job ($cron_time)" + fi + + echo "" + print_success "WordPress cron converted to system cron for $domain" + echo "" + echo "Changes made:" + echo " • DISABLE_WP_CRON set to true in wp-config.php" + echo " • System cron job added (every 15 minutes)" + echo " • Backup saved: ${wp_config}.backup-*" + ;; + + 3) + # Disable wp-cron for specific user + echo "" + echo -n "Enter cPanel username: " + read -r target_user + + if [ -z "$target_user" ]; then + print_error "Username cannot be empty" + press_enter + exit 1 + fi + + if [ ! -d "/home/$target_user" ]; then + print_error "User $target_user does not exist" + press_enter + exit 1 + fi + + echo "" + echo "Searching for WordPress installations for user: $target_user" + echo "" + + wp_configs=$(find "/home/$target_user" -name "wp-config.php" -type f 2>/dev/null) + + if [ -z "$wp_configs" ]; then + print_error "No WordPress installations found for $target_user" + press_enter + exit 1 + fi + + count=0 + echo "$wp_configs" | while IFS= read -r wp_config; do + count=$((count + 1)) + site_path=$(dirname "$wp_config") + + echo -e "${BOLD}Site $count:${NC} $site_path" + + # Backup + cp "$wp_config" "${wp_config}.backup-$(date +%Y%m%d-%H%M%S)" + echo " • Backed up wp-config.php" + + # Disable wp-cron + if grep -q "DISABLE_WP_CRON" "$wp_config" 2>/dev/null; then + sed -i "s/define.*DISABLE_WP_CRON.*/define('DISABLE_WP_CRON', true);/" "$wp_config" + else + sed -i "//dev/null 2>&1" + + if ! crontab -u "$target_user" -l 2>/dev/null | grep -q "$site_path.*wp-cron.php"; then + cron_time=$(generate_staggered_cron) + (crontab -u "$target_user" -l 2>/dev/null; echo "$cron_time $cron_cmd") | crontab -u "$target_user" - + echo " • Added cron job ($cron_time)" + else + echo " • Cron job already exists" + fi + + echo "" + done + + print_success "All WordPress sites for $target_user converted to system cron" + ;; + + 4) + # Server-wide conversion + echo "" + echo -e "${RED}${BOLD}WARNING: Server-Wide wp-cron Conversion${NC}" + echo "" + echo "This will:" + echo " • Find ALL WordPress installations on the server" + echo " • Disable wp-cron in each wp-config.php" + echo " • Add system cron jobs for each user" + echo "" + echo -n "Are you sure? Type 'yes' to confirm: " + read -r confirm + + if [ "$confirm" != "yes" ]; then + echo "Cancelled" + press_enter + exit 0 + fi + + echo "" + echo "Scanning entire server for WordPress installations..." + echo "" + + total=0 + converted=0 + + wp_configs=$(find /home/*/public_html -name "wp-config.php" -type f 2>/dev/null) + + if [ -z "$wp_configs" ]; then + echo -e "${YELLOW}No WordPress installations found${NC}" + press_enter + exit 0 + fi + + while IFS= read -r wp_config; do + total=$((total + 1)) + site_path=$(dirname "$wp_config") + user=$(echo "$site_path" | cut -d'/' -f3) + + echo -e "${BOLD}Processing:${NC} $site_path (user: $user)" + + # Backup + cp "$wp_config" "${wp_config}.backup-$(date +%Y%m%d-%H%M%S)" 2>/dev/null + + # Disable wp-cron + if grep -q "DISABLE_WP_CRON" "$wp_config" 2>/dev/null; then + sed -i "s/define.*DISABLE_WP_CRON.*/define('DISABLE_WP_CRON', true);/" "$wp_config" 2>/dev/null + else + sed -i "//dev/null + fi + + # Add cron job with staggered timing + cron_cmd="cd $site_path && /usr/bin/php -q wp-cron.php >/dev/null 2>&1" + + if ! crontab -u "$user" -l 2>/dev/null | grep -q "$site_path.*wp-cron.php"; then + cron_time=$(generate_staggered_cron) + (crontab -u "$user" -l 2>/dev/null; echo "$cron_time $cron_cmd") | crontab -u "$user" - 2>/dev/null + echo " Cron: $cron_time" + fi + + converted=$((converted + 1)) + echo -e "${GREEN}✓${NC} Converted" + echo "" + done <<< "$wp_configs" + + echo "" + print_success "Server-wide conversion complete" + echo "" + echo "Summary:" + echo " • Total WordPress sites found: $total" + echo " • Successfully converted: $converted" + ;; + + 5) + # Check status + echo "" + echo "Check wp-cron status for:" + echo " 1) Specific domain" + echo " 2) Specific user" + echo "" + echo -n "Select [1]: " + read -r check_choice + check_choice="${check_choice:-1}" + + if [ "$check_choice" = "1" ]; then + echo "" + echo -n "Enter domain name: " + read -r domain + + # Find WordPress for domain + wp_config="" + for userdata_file in /var/cpanel/userdata/*/main; do + if grep -q "^servername: $domain" "$userdata_file" 2>/dev/null; then + user=$(basename "$(dirname "$userdata_file")") + potential_config="/home/$user/public_html/wp-config.php" + if [ -f "$potential_config" ]; then + wp_config="$potential_config" + break + fi + fi + done + + if [ -z "$wp_config" ]; then + print_error "WordPress not found for $domain" + press_enter + exit 1 + fi + + echo "" + echo -e "${BOLD}WordPress Cron Status for $domain${NC}" + echo "" + echo "Config file: $wp_config" + echo "" + + if grep -q "define.*DISABLE_WP_CRON.*true" "$wp_config" 2>/dev/null; then + echo -e "wp-cron: ${GREEN}DISABLED${NC} (using system cron)" + + # Check for cron job + site_path=$(dirname "$wp_config") + user=$(echo "$site_path" | cut -d'/' -f3) + + if crontab -u "$user" -l 2>/dev/null | grep -q "wp-cron.php"; then + echo -e "System cron: ${GREEN}CONFIGURED${NC}" + echo "" + echo "Cron jobs:" + crontab -u "$user" -l 2>/dev/null | grep "wp-cron.php" + else + echo -e "System cron: ${RED}NOT CONFIGURED${NC}" + fi + else + echo -e "wp-cron: ${YELLOW}ENABLED${NC} (default WordPress cron)" + echo "" + echo "Recommendation: Disable wp-cron and use system cron for better performance" + fi + + else + echo "" + echo -n "Enter cPanel username: " + read -r check_user + + if [ ! -d "/home/$check_user" ]; then + print_error "User $check_user does not exist" + press_enter + exit 1 + fi + + echo "" + echo -e "${BOLD}WordPress Cron Status for user: $check_user${NC}" + echo "" + + wp_configs=$(find "/home/$check_user" -name "wp-config.php" -type f 2>/dev/null) + + if [ -z "$wp_configs" ]; then + echo "No WordPress installations found" + else + count=0 + while IFS= read -r wp_config; do + count=$((count + 1)) + site_path=$(dirname "$wp_config") + + echo -e "${count}. ${BOLD}$site_path${NC}" + + if grep -q "define.*DISABLE_WP_CRON.*true" "$wp_config" 2>/dev/null; then + echo " wp-cron: ${GREEN}DISABLED${NC}" + else + echo " wp-cron: ${YELLOW}ENABLED${NC}" + fi + echo "" + done <<< "$wp_configs" + + # Show cron jobs + echo -e "${BOLD}Cron Jobs:${NC}" + if crontab -u "$check_user" -l 2>/dev/null | grep -q "wp-cron.php"; then + crontab -u "$check_user" -l 2>/dev/null | grep "wp-cron.php" + else + echo " No wp-cron jobs found" + fi + fi + fi + ;; + + 0) + exit 0 + ;; + + *) + print_error "Invalid option" + ;; +esac + +echo "" +press_enter