diff --git a/modules/website/wordpress/wordpress-cron-manager.sh b/modules/website/wordpress/wordpress-cron-manager.sh index e2cae61..816d1cc 100755 --- a/modules/website/wordpress/wordpress-cron-manager.sh +++ b/modules/website/wordpress/wordpress-cron-manager.sh @@ -59,6 +59,220 @@ safe_remove_cron_jobs() { return $? } +# Function to validate wp-config.php syntax before and after modification +# Returns 0 if valid, 1 if syntax error detected +validate_wp_config_syntax() { + local wp_config="$1" + + # Check if file exists and is readable + [ ! -f "$wp_config" ] && return 1 + [ ! -r "$wp_config" ] && return 1 + + # Validate PHP syntax using php -l if available + if command -v php >/dev/null 2>&1; then + if ! php -l "$wp_config" >/dev/null 2>&1; then + return 1 + fi + fi + + # Additional checks: ensure file has opening /dev/null; then + return 1 + fi + + return 0 +} + +# Function to check if cron job already exists for a specific site +# Returns 0 if exists, 1 if not found +cron_job_exists() { + local user="$1" + local site_path="$2" + + crontab -u "$user" -l 2>/dev/null | grep -qF "$site_path" && return 0 || return 1 +} + +# Function to check if DISABLE_WP_CRON already exists in wp-config +# Returns 0 if exists, 1 if not found +disable_wp_cron_exists() { + local wp_config="$1" + + grep -q "DISABLE_WP_CRON.*true" "$wp_config" 2>/dev/null && return 0 || return 1 +} + +# Function to verify user owns the WordPress installation +# Returns 0 if user matches, 1 if mismatch +verify_user_ownership() { + local user="$1" + local wp_config="$2" + + # Get actual owner of file + local actual_owner=$(stat -c '%U' "$wp_config" 2>/dev/null || echo "") + + if [ -z "$actual_owner" ]; then + # stat failed, try alternative method + actual_owner=$(ls -l "$wp_config" 2>/dev/null | awk '{print $3}') + fi + + # Verify user matches + if [ "$user" = "$actual_owner" ]; then + return 0 + else + return 1 + fi +} + +# Function to check if user exists and can receive crontab entries +# Returns 0 if valid, 1 if not +user_is_valid() { + local user="$1" + + # Check if user exists in system + if ! id "$user" >/dev/null 2>&1; then + return 1 + fi + + # Check if user can be used with crontab (not system user like root) + # Should have a real home directory + local home_dir=$(getent passwd "$user" | cut -d: -f6) + if [ -z "$home_dir" ] || [ "$home_dir" = "/dev/null" ]; then + return 1 + fi + + return 0 +} + +# Function to pre-check all WordPress installations before any modifications +# Returns count of valid installations +preflight_check() { + local panel="$1" + local found_count=0 + local issues_count=0 + + echo "" + echo "Running pre-flight checks..." + echo "" + + # Find all wp-config.php files based on panel type + local wp_configs="" + case "$panel" in + cpanel) + wp_configs=$(find /home/*/public_html -name "wp-config.php" -type f 2>/dev/null) + ;; + interworx) + wp_configs=$(find /home/*/*/html -name "wp-config.php" -type f 2>/dev/null) + ;; + plesk) + wp_configs=$(find /var/www/vhosts/*/httpdocs -name "wp-config.php" -type f 2>/dev/null) + ;; + *) + wp_configs=$(find /var/www/html -name "wp-config.php" -type f 2>/dev/null) + ;; + esac + + if [ -z "$wp_configs" ]; then + echo -e "${YELLOW}No WordPress installations found${NC}" + return 0 + fi + + while IFS= read -r wp_config; do + found_count=$((found_count + 1)) + site_path=$(dirname "$wp_config") + user=$(extract_user_from_path "$site_path") + + # Verify user is valid + if ! user_is_valid "$user"; then + echo -e "${RED}✗${NC} Site $found_count: Invalid user '$user' ($site_path)" + issues_count=$((issues_count + 1)) + continue + fi + + # Verify user owns the WordPress install + if ! verify_user_ownership "$user" "$wp_config"; then + echo -e "${YELLOW}⚠${NC} Site $found_count: User mismatch - extracted='$user', owner=$(stat -c '%U' "$wp_config" 2>/dev/null) ($site_path)" + issues_count=$((issues_count + 1)) + continue + fi + + # Validate wp-config.php syntax + if ! validate_wp_config_syntax "$wp_config"; then + echo -e "${RED}✗${NC} Site $found_count: Invalid wp-config.php syntax ($site_path)" + issues_count=$((issues_count + 1)) + continue + fi + + echo -e "${GREEN}✓${NC} Site $found_count: $site_path (user: $user)" + done <<< "$wp_configs" + + echo "" + echo "Pre-flight check complete:" + echo " Total found: $found_count" + echo " Issues: $issues_count" + echo " Ready: $((found_count - issues_count))" + + return $((found_count - issues_count)) +} + +# Function to display detailed status of all WordPress installations +# Shows current state before any changes +show_installation_status() { + local panel="$1" + + echo "" + echo "Current WordPress Installation Status:" + echo "" + + local wp_configs="" + case "$panel" in + cpanel) + wp_configs=$(find /home/*/public_html -name "wp-config.php" -type f 2>/dev/null) + ;; + interworx) + wp_configs=$(find /home/*/*/html -name "wp-config.php" -type f 2>/dev/null) + ;; + plesk) + wp_configs=$(find /var/www/vhosts/*/httpdocs -name "wp-config.php" -type f 2>/dev/null) + ;; + *) + wp_configs=$(find /var/www/html -name "wp-config.php" -type f 2>/dev/null) + ;; + esac + + if [ -z "$wp_configs" ]; then + echo "No WordPress installations found" + return 1 + fi + + local count=0 + while IFS= read -r wp_config; do + count=$((count + 1)) + site_path=$(dirname "$wp_config") + user=$(extract_user_from_path "$site_path") + + # Check wp-cron status + if disable_wp_cron_exists "$wp_config"; then + cron_status="${GREEN}Disabled${NC} (system cron)" + else + cron_status="${YELLOW}Enabled${NC} (default)" + fi + + # Check if cron job exists + if cron_job_exists "$user" "$site_path"; then + cron_job="${GREEN}Yes${NC}" + else + cron_job="${YELLOW}No${NC}" + fi + + echo "$count. ${BOLD}$site_path${NC}" + echo " User: $user" + echo " WP-Cron: $cron_status" + echo " System Cron Job: $cron_job" + echo "" + done <<< "$wp_configs" + + echo "Total installations: $count" +} + # Function to generate staggered cron time # Distributes jobs across 60 minutes to avoid load spikes generate_staggered_cron() { @@ -204,6 +418,8 @@ echo -e " ${CYAN}8)${NC} Re-enable wp-cron server-wide (all WordPress sites)" echo "" echo -e "${CYAN}Status & Information:${NC}" echo -e " ${CYAN}5)${NC} Check wp-cron status for domain/user" +echo -e " ${CYAN}9)${NC} Run pre-flight checks (validate all installations)" +echo -e " ${CYAN}10)${NC} Show detailed status of all WordPress sites" echo "" echo -e " ${RED}0)${NC} Return to menu" echo "" @@ -215,9 +431,9 @@ while true; do read -r choice choice="${choice:-0}" - if ! [[ "$choice" =~ ^[0-8]$ ]]; then + if ! [[ "$choice" =~ ^([0-9]|10)$ ]]; then echo "" - print_error "Invalid choice. Please enter 0-8" + print_error "Invalid choice. Please enter 0-10" echo "" continue fi @@ -394,8 +610,50 @@ case "$choice" in 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 + # Extract site path and user + site_path=$(dirname "$wp_config") + user=$(extract_user_from_path "$site_path") + + # PRE-FLIGHT VALIDATION CHECKS + echo "Running pre-flight validation checks..." + echo "" + + # Check 1: Validate user + if ! user_is_valid "$user"; then + print_error "User '$user' is not valid or not a cPanel user" + press_enter + exit 1 + fi + echo -e "${GREEN}✓${NC} User valid: $user" + + # Check 2: Verify user ownership + if ! verify_user_ownership "$user" "$wp_config"; then + actual_owner=$(stat -c '%U' "$wp_config" 2>/dev/null || ls -l "$wp_config" | awk '{print $3}') + echo -e "${YELLOW}⚠${NC} User mismatch detected:" + echo " Extracted user: $user" + echo " Actual owner: $actual_owner" + echo "" + echo -n "Continue anyway? (y/n) [n]: " + read -r confirm + if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then + press_enter + exit 0 + fi + else + echo -e "${GREEN}✓${NC} User ownership verified" + fi + + # Check 3: Validate wp-config.php syntax BEFORE any changes + if ! validate_wp_config_syntax "$wp_config"; then + print_error "wp-config.php has syntax errors - cannot modify" + echo " Please fix syntax issues first" + press_enter + exit 1 + fi + echo -e "${GREEN}✓${NC} wp-config.php syntax is valid" + + # Check 4: Check for existing DISABLE_WP_CRON + if disable_wp_cron_exists "$wp_config"; then echo -e "${YELLOW}wp-cron is already disabled for this site${NC}" echo "" echo -n "Re-configure anyway? (y/n) [n]: " @@ -404,8 +662,28 @@ case "$choice" in press_enter exit 0 fi + else + echo -e "${GREEN}✓${NC} wp-cron currently enabled (will be disabled)" fi + # Check 5: Check for existing cron job + if cron_job_exists "$user" "$site_path"; then + echo -e "${YELLOW}⚠${NC} System cron job already exists for this site" + echo "" + echo -n "Update existing cron job? (y/n) [n]: " + read -r confirm + if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then + press_enter + exit 0 + fi + else + echo -e "${GREEN}✓${NC} No existing system cron job" + fi + + echo "" + echo "All validation checks passed. Proceeding with configuration..." + echo "" + # 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" @@ -413,6 +691,20 @@ case "$choice" in # Safely disable wp-cron in wp-config.php if disable_wpcron_in_config "$wp_config"; then echo -e "${GREEN}✓${NC} Set DISABLE_WP_CRON to true in wp-config.php" + + # CRITICAL: Verify syntax after modification + if ! validate_wp_config_syntax "$wp_config"; then + print_error "CRITICAL: wp-config.php syntax became invalid after modification!" + echo " Attempting to restore backup..." + backup_file=$(find "${wp_config}.backup-"* 2>/dev/null | sort -r | head -1) + if [ -n "$backup_file" ]; then + cp "$backup_file" "$wp_config" + echo -e "${GREEN}✓${NC} Restored from backup" + fi + press_enter + exit 1 + fi + echo -e "${GREEN}✓${NC} wp-config.php syntax verified after modification" else print_error "Failed to modify wp-config.php" echo " Please check file permissions and syntax" @@ -421,27 +713,25 @@ case "$choice" in fi # Add cron job with staggered timing - site_path=$(dirname "$wp_config") if [ -z "$site_path" ]; then echo -e "${RED}✗${NC} Could not determine site path" continue fi cron_cmd="cd \"$site_path\" && /usr/bin/php -q wp-cron.php >/dev/null 2>&1" - # Add to user's crontab - Multi-panel support - user=$(extract_user_from_path "$site_path") + # Check if cron job already exists (for duplicate prevention) + if cron_job_exists "$user" "$site_path"; then + # Remove old one first to avoid duplicates + safe_remove_cron_jobs "$user" "$site_path.*wp-cron.php" + echo -e "${YELLOW}⚠${NC} Removed existing cron job (updating)" + fi - # 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" + # Generate staggered cron time and add to crontab + cron_time=$(generate_staggered_cron) + if safe_add_cron_job "$user" "$cron_time" "$cron_cmd"; then + echo -e "${GREEN}✓${NC} Added cron job ($cron_time)" else - # Generate staggered cron time - cron_time=$(generate_staggered_cron) - if safe_add_cron_job "$user" "$cron_time" "$cron_cmd"; then - echo -e "${GREEN}✓${NC} Added cron job ($cron_time)" - else - echo -e "${YELLOW}⚠${NC} Failed to add cron job" - fi + echo -e "${YELLOW}⚠${NC} Failed to add cron job" fi echo "" @@ -1014,6 +1304,20 @@ case "$choice" in echo " • Successfully reverted: $reverted" ;; + 9) + # Run pre-flight checks + echo "" + print_banner "Pre-Flight Checks" + preflight_check "$SYS_CONTROL_PANEL" + ;; + + 10) + # Show detailed status + echo "" + print_banner "WordPress Installation Status" + show_installation_status "$SYS_CONTROL_PANEL" + ;; + 0) exit 0 ;;