true, 'backup_frequency' => 'daily', 'retention_days' => 30, 'backup_location' => 'local', // local, s3, ftp 'compression_enabled' => true, 'encryption_enabled' => true, 'max_backup_size_mb' => 500, 'backup_critical_only' => false ]; /** * Critical data tables and options */ private static $critical_data = [ 'tables' => [ 'posts', 'postmeta', 'users', 'usermeta', 'options' ], 'options' => [ 'hvac_*', 'tribe_events_*', 'astra_*' ], 'files' => [ 'uploads/hvac-*', 'plugins/hvac-community-events' ] ]; /** * Initialize backup manager */ public static function init() { // Load settings self::$settings = array_merge( self::$settings, get_option('hvac_backup_settings', []) ); // Schedule automatic backups if (self::$settings['auto_backup_enabled']) { self::schedule_automatic_backups(); } // Hook backup actions add_action('hvac_run_backup', [__CLASS__, 'run_scheduled_backup']); add_action('hvac_cleanup_old_backups', [__CLASS__, 'cleanup_old_backups']); // Emergency backup triggers add_action('hvac_before_critical_operation', [__CLASS__, 'create_emergency_backup']); add_action('wp_upgrade', [__CLASS__, 'create_pre_update_backup']); add_action('upgrader_process_complete', [__CLASS__, 'create_post_update_backup'], 10, 2); // Admin interface if (is_admin()) { add_action('admin_menu', [__CLASS__, 'add_admin_menu']); add_action('wp_ajax_hvac_backup_action', [__CLASS__, 'handle_backup_action']); add_action('admin_notices', [__CLASS__, 'show_backup_notices']); } // REST API endpoints add_action('rest_api_init', [__CLASS__, 'register_rest_endpoints']); // WP-CLI integration if (defined('WP_CLI') && WP_CLI) { WP_CLI::add_command('hvac backup', [__CLASS__, 'wp_cli_backup']); } // Disaster recovery detection add_action('init', [__CLASS__, 'check_disaster_recovery_mode']); } /** * Schedule automatic backups */ private static function schedule_automatic_backups() { $frequency = self::$settings['backup_frequency']; if (!wp_next_scheduled('hvac_run_backup')) { wp_schedule_event(time(), $frequency, 'hvac_run_backup'); } // Schedule cleanup if (!wp_next_scheduled('hvac_cleanup_old_backups')) { wp_schedule_event(time(), 'daily', 'hvac_cleanup_old_backups'); } } /** * Create backup * * @param string $type Backup type * @param array $options Backup options * @return array Backup result */ public static function create_backup($type = self::BACKUP_FULL, $options = []) { $backup_id = uniqid('hvac_backup_'); $timestamp = time(); // Default options $options = array_merge([ 'compress' => self::$settings['compression_enabled'], 'encrypt' => self::$settings['encryption_enabled'], 'description' => '', 'triggered_by' => 'manual' ], $options); // Create backup record $backup_record = [ 'id' => $backup_id, 'type' => $type, 'status' => self::STATUS_RUNNING, 'created_at' => $timestamp, 'size' => 0, 'file_path' => '', 'options' => $options, 'progress' => 0 ]; self::update_backup_record($backup_id, $backup_record); try { // Create backup directory $backup_dir = self::get_backup_directory(); if (!wp_mkdir_p($backup_dir)) { throw new Exception('Failed to create backup directory'); } $backup_file = $backup_dir . "/{$backup_id}.sql"; // Perform backup based on type switch ($type) { case self::BACKUP_FULL: $result = self::create_full_backup($backup_file, $backup_id); break; case self::BACKUP_CRITICAL_DATA: $result = self::create_critical_data_backup($backup_file, $backup_id); break; case self::BACKUP_INCREMENTAL: $result = self::create_incremental_backup($backup_file, $backup_id); break; default: throw new Exception('Invalid backup type'); } // Post-processing if ($result['success']) { $final_file = $result['file_path']; // Compress if enabled if ($options['compress']) { $final_file = self::compress_backup($final_file); } // Encrypt if enabled if ($options['encrypt']) { $final_file = self::encrypt_backup($final_file); } $file_size = file_exists($final_file) ? filesize($final_file) : 0; // Update backup record $backup_record['status'] = self::STATUS_COMPLETED; $backup_record['file_path'] = $final_file; $backup_record['size'] = $file_size; $backup_record['progress'] = 100; $backup_record['completed_at'] = time(); self::update_backup_record($backup_id, $backup_record); // Log success HVAC_Logger::info( "Backup completed successfully: $backup_id ($type)", 'Backup Manager' ); return [ 'success' => true, 'backup_id' => $backup_id, 'file_path' => $final_file, 'size' => $file_size ]; } else { throw new Exception($result['error']); } } catch (Exception $e) { // Update backup record with failure $backup_record['status'] = self::STATUS_FAILED; $backup_record['error'] = $e->getMessage(); self::update_backup_record($backup_id, $backup_record); HVAC_Logger::error( "Backup failed: $backup_id - " . $e->getMessage(), 'Backup Manager' ); return [ 'success' => false, 'error' => $e->getMessage(), 'backup_id' => $backup_id ]; } } /** * Create full backup */ private static function create_full_backup($backup_file, $backup_id) { global $wpdb; try { // Get all tables $tables = $wpdb->get_results('SHOW TABLES', ARRAY_N); $total_tables = count($tables); $current_table = 0; $sql_content = "-- HVAC Plugin Full Backup\n"; $sql_content .= "-- Created: " . date('Y-m-d H:i:s') . "\n"; $sql_content .= "-- Backup ID: $backup_id\n\n"; foreach ($tables as $table) { $table_name = $table[0]; $current_table++; // Update progress $progress = ($current_table / $total_tables) * 90; // 90% for database, 10% for files self::update_backup_progress($backup_id, $progress); // Export table structure $create_table = $wpdb->get_row("SHOW CREATE TABLE `$table_name`", ARRAY_N); $sql_content .= "\n-- Table structure for `$table_name`\n"; $sql_content .= "DROP TABLE IF EXISTS `$table_name`;\n"; $sql_content .= $create_table[1] . ";\n\n"; // Export table data $rows = $wpdb->get_results("SELECT * FROM `$table_name`", ARRAY_A); if (!empty($rows)) { $sql_content .= "-- Data for table `$table_name`\n"; foreach ($rows as $row) { $values = array_map(function($value) { return is_null($value) ? 'NULL' : "'" . esc_sql($value) . "'"; }, array_values($row)); $sql_content .= "INSERT INTO `$table_name` VALUES (" . implode(', ', $values) . ");\n"; } $sql_content .= "\n"; } } // Write SQL file $bytes_written = file_put_contents($backup_file, $sql_content); if ($bytes_written === false) { throw new Exception('Failed to write backup file'); } // Update progress to 100% self::update_backup_progress($backup_id, 100); return [ 'success' => true, 'file_path' => $backup_file, 'tables_exported' => $total_tables, 'size' => $bytes_written ]; } catch (Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Create critical data backup */ private static function create_critical_data_backup($backup_file, $backup_id) { global $wpdb; try { $sql_content = "-- HVAC Plugin Critical Data Backup\n"; $sql_content .= "-- Created: " . date('Y-m-d H:i:s') . "\n"; $sql_content .= "-- Backup ID: $backup_id\n\n"; $critical_tables = self::$critical_data['tables']; $total_tables = count($critical_tables); $current_table = 0; foreach ($critical_tables as $table_suffix) { $table_name = $wpdb->prefix . $table_suffix; $current_table++; // Update progress $progress = ($current_table / $total_tables) * 100; self::update_backup_progress($backup_id, $progress); // Check if table exists $table_exists = $wpdb->get_var($wpdb->prepare( "SHOW TABLES LIKE %s", $table_name )); if (!$table_exists) { continue; } // Export critical data from this table if ($table_suffix === 'options') { // Export critical options only $sql_content .= self::export_critical_options($table_name); } elseif ($table_suffix === 'posts') { // Export HVAC-related posts only $sql_content .= self::export_hvac_posts($table_name); } else { // Export full table for other critical tables $sql_content .= self::export_full_table($table_name); } } // Write SQL file $bytes_written = file_put_contents($backup_file, $sql_content); if ($bytes_written === false) { throw new Exception('Failed to write critical data backup file'); } return [ 'success' => true, 'file_path' => $backup_file, 'tables_exported' => count($critical_tables), 'size' => $bytes_written ]; } catch (Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Export critical options */ private static function export_critical_options($table_name) { global $wpdb; $sql = "\n-- Critical Options from $table_name\n"; foreach (self::$critical_data['options'] as $option_pattern) { $like_pattern = str_replace('*', '%', $option_pattern); $options = $wpdb->get_results($wpdb->prepare( "SELECT * FROM `$table_name` WHERE option_name LIKE %s", $like_pattern ), ARRAY_A); foreach ($options as $option) { $name = esc_sql($option['option_name']); $value = esc_sql($option['option_value']); $autoload = esc_sql($option['autoload']); $sql .= "INSERT INTO `$table_name` (option_name, option_value, autoload) VALUES "; $sql .= "('$name', '$value', '$autoload') ON DUPLICATE KEY UPDATE "; $sql .= "option_value = VALUES(option_value), autoload = VALUES(autoload);\n"; } } return $sql . "\n"; } /** * Export HVAC-related posts */ private static function export_hvac_posts($table_name) { global $wpdb; $sql = "\n-- HVAC-related Posts from $table_name\n"; // Export tribe_events and HVAC pages $post_types = ['tribe_events', 'page']; foreach ($post_types as $post_type) { $posts = $wpdb->get_results($wpdb->prepare( "SELECT * FROM `$table_name` WHERE post_type = %s AND (post_title LIKE %s OR post_name LIKE %s)", $post_type, '%trainer%', '%hvac%' ), ARRAY_A); foreach ($posts as $post) { $values = array_map(function($value) { return is_null($value) ? 'NULL' : "'" . esc_sql($value) . "'"; }, array_values($post)); $sql .= "INSERT INTO `$table_name` VALUES (" . implode(', ', $values) . ");\n"; } } return $sql . "\n"; } /** * Export full table */ private static function export_full_table($table_name) { global $wpdb; $sql = "\n-- Full export of $table_name\n"; // Get table structure $create_table = $wpdb->get_row("SHOW CREATE TABLE `$table_name`", ARRAY_N); $sql .= "DROP TABLE IF EXISTS `$table_name`;\n"; $sql .= $create_table[1] . ";\n\n"; // Get table data $rows = $wpdb->get_results("SELECT * FROM `$table_name`", ARRAY_A); foreach ($rows as $row) { $values = array_map(function($value) { return is_null($value) ? 'NULL' : "'" . esc_sql($value) . "'"; }, array_values($row)); $sql .= "INSERT INTO `$table_name` VALUES (" . implode(', ', $values) . ");\n"; } return $sql . "\n"; } /** * Create incremental backup */ private static function create_incremental_backup($backup_file, $backup_id) { // Get last backup timestamp $last_backup_time = get_option('hvac_last_backup_timestamp', 0); $current_time = time(); // Find changes since last backup global $wpdb; $sql_content = "-- HVAC Plugin Incremental Backup\n"; $sql_content .= "-- Created: " . date('Y-m-d H:i:s') . "\n"; $sql_content .= "-- Since: " . date('Y-m-d H:i:s', $last_backup_time) . "\n"; $sql_content .= "-- Backup ID: $backup_id\n\n"; // Get modified posts $modified_posts = $wpdb->get_results($wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE post_modified_gmt > %s", date('Y-m-d H:i:s', $last_backup_time) ), ARRAY_A); if (!empty($modified_posts)) { $sql_content .= "-- Modified Posts\n"; foreach ($modified_posts as $post) { $values = array_map(function($value) { return is_null($value) ? 'NULL' : "'" . esc_sql($value) . "'"; }, array_values($post)); $sql_content .= "REPLACE INTO {$wpdb->posts} VALUES (" . implode(', ', $values) . ");\n"; } } // Get modified options $modified_options = $wpdb->get_results($wpdb->prepare( "SELECT * FROM {$wpdb->options} WHERE option_name LIKE %s", 'hvac_%' ), ARRAY_A); if (!empty($modified_options)) { $sql_content .= "\n-- HVAC Options\n"; foreach ($modified_options as $option) { $name = esc_sql($option['option_name']); $value = esc_sql($option['option_value']); $autoload = esc_sql($option['autoload']); $sql_content .= "REPLACE INTO {$wpdb->options} (option_name, option_value, autoload) "; $sql_content .= "VALUES ('$name', '$value', '$autoload');\n"; } } // Write incremental backup $bytes_written = file_put_contents($backup_file, $sql_content); if ($bytes_written === false) { throw new Exception('Failed to write incremental backup file'); } // Update last backup timestamp update_option('hvac_last_backup_timestamp', $current_time); return [ 'success' => true, 'file_path' => $backup_file, 'modified_posts' => count($modified_posts), 'modified_options' => count($modified_options), 'size' => $bytes_written ]; } /** * Compress backup file */ private static function compress_backup($file_path) { if (!function_exists('gzopen')) { HVAC_Logger::warning('GZ compression not available', 'Backup Manager'); return $file_path; } $compressed_file = $file_path . '.gz'; $file_handle = fopen($file_path, 'rb'); $gz_handle = gzopen($compressed_file, 'wb9'); if ($file_handle && $gz_handle) { while (!feof($file_handle)) { gzwrite($gz_handle, fread($file_handle, 4096)); } fclose($file_handle); gzclose($gz_handle); // Remove original file unlink($file_path); return $compressed_file; } return $file_path; } /** * Encrypt backup file */ private static function encrypt_backup($file_path) { if (!function_exists('openssl_encrypt')) { HVAC_Logger::warning('OpenSSL encryption not available', 'Backup Manager'); return $file_path; } $encryption_key = self::get_encryption_key(); $encrypted_file = $file_path . '.enc'; $data = file_get_contents($file_path); $iv = openssl_random_pseudo_bytes(16); $encrypted_data = openssl_encrypt( $data, 'AES-256-CBC', $encryption_key, 0, $iv ); if ($encrypted_data !== false) { // Store IV + encrypted data file_put_contents($encrypted_file, base64_encode($iv . $encrypted_data)); // Remove original file unlink($file_path); return $encrypted_file; } return $file_path; } /** * Get or generate encryption key */ private static function get_encryption_key() { $key = get_option('hvac_backup_encryption_key'); if (!$key) { $key = bin2hex(openssl_random_pseudo_bytes(32)); update_option('hvac_backup_encryption_key', $key); } return $key; } /** * Restore from backup * * @param string $backup_id Backup ID to restore * @param array $options Restore options * @return array Restore result */ public static function restore_from_backup($backup_id, $options = []) { $backup_record = self::get_backup_record($backup_id); if (!$backup_record || $backup_record['status'] !== self::STATUS_COMPLETED) { return [ 'success' => false, 'error' => 'Backup not found or incomplete' ]; } $backup_file = $backup_record['file_path']; if (!file_exists($backup_file)) { return [ 'success' => false, 'error' => 'Backup file not found' ]; } try { // Create restoration backup first self::create_backup(self::BACKUP_CRITICAL_DATA, [ 'description' => 'Pre-restoration backup', 'triggered_by' => 'restore_operation' ]); // Decrypt if needed if (str_ends_with($backup_file, '.enc')) { $backup_file = self::decrypt_backup($backup_file); } // Decompress if needed if (str_ends_with($backup_file, '.gz')) { $backup_file = self::decompress_backup($backup_file); } // Execute SQL restoration $sql_content = file_get_contents($backup_file); if ($sql_content === false) { throw new Exception('Failed to read backup file'); } // Split SQL into individual statements $statements = array_filter( explode(";\n", $sql_content), function($statement) { return trim($statement) && !str_starts_with(trim($statement), '--'); } ); global $wpdb; $executed_statements = 0; $failed_statements = 0; foreach ($statements as $statement) { $statement = trim($statement); if (empty($statement)) continue; $result = $wpdb->query($statement); if ($result === false) { $failed_statements++; HVAC_Logger::warning( "Failed to execute SQL: " . $wpdb->last_error, 'Backup Manager' ); } else { $executed_statements++; } } // Clear all caches wp_cache_flush(); // Log successful restoration HVAC_Logger::info( "Backup restored successfully: $backup_id ($executed_statements statements)", 'Backup Manager' ); return [ 'success' => true, 'executed_statements' => $executed_statements, 'failed_statements' => $failed_statements, 'backup_id' => $backup_id ]; } catch (Exception $e) { HVAC_Logger::error( "Backup restoration failed: $backup_id - " . $e->getMessage(), 'Backup Manager' ); return [ 'success' => false, 'error' => $e->getMessage(), 'backup_id' => $backup_id ]; } } /** * Decrypt backup file */ private static function decrypt_backup($encrypted_file) { $encryption_key = self::get_encryption_key(); $decrypted_file = str_replace('.enc', '', $encrypted_file); $encrypted_data = base64_decode(file_get_contents($encrypted_file)); $iv = substr($encrypted_data, 0, 16); $data = substr($encrypted_data, 16); $decrypted_data = openssl_decrypt( $data, 'AES-256-CBC', $encryption_key, 0, $iv ); if ($decrypted_data !== false) { file_put_contents($decrypted_file, $decrypted_data); return $decrypted_file; } throw new Exception('Failed to decrypt backup file'); } /** * Decompress backup file */ private static function decompress_backup($compressed_file) { $decompressed_file = str_replace('.gz', '', $compressed_file); $gz_handle = gzopen($compressed_file, 'rb'); $file_handle = fopen($decompressed_file, 'wb'); if ($gz_handle && $file_handle) { while (!gzeof($gz_handle)) { fwrite($file_handle, gzread($gz_handle, 4096)); } gzclose($gz_handle); fclose($file_handle); return $decompressed_file; } throw new Exception('Failed to decompress backup file'); } /** * Run scheduled backup */ public static function run_scheduled_backup() { $backup_type = self::$settings['backup_critical_only'] ? self::BACKUP_CRITICAL_DATA : self::BACKUP_FULL; $result = self::create_backup($backup_type, [ 'description' => 'Scheduled automatic backup', 'triggered_by' => 'scheduler' ]); if ($result['success']) { HVAC_Logger::info( 'Scheduled backup completed: ' . $result['backup_id'], 'Backup Manager' ); } else { HVAC_Logger::error( 'Scheduled backup failed: ' . $result['error'], 'Backup Manager' ); // Send alert email self::send_backup_alert('Scheduled Backup Failed', [ 'error' => $result['error'], 'timestamp' => time() ]); } } /** * Create emergency backup */ public static function create_emergency_backup() { $result = self::create_backup(self::BACKUP_CRITICAL_DATA, [ 'description' => 'Emergency backup before critical operation', 'triggered_by' => 'emergency' ]); if (!$result['success']) { HVAC_Logger::error( 'Emergency backup failed: ' . $result['error'], 'Backup Manager' ); } return $result; } /** * Create pre-update backup */ public static function create_pre_update_backup() { self::create_backup(self::BACKUP_FULL, [ 'description' => 'Pre-WordPress update backup', 'triggered_by' => 'wp_update' ]); } /** * Create post-update backup */ public static function create_post_update_backup($upgrader, $options) { if ($options['type'] === 'plugin' && isset($options['plugins']) && in_array('hvac-community-events/hvac-community-events.php', $options['plugins'])) { self::create_backup(self::BACKUP_CRITICAL_DATA, [ 'description' => 'Post-plugin update backup', 'triggered_by' => 'plugin_update' ]); } } /** * Cleanup old backups */ public static function cleanup_old_backups() { $retention_days = self::$settings['retention_days']; $cutoff_timestamp = time() - ($retention_days * 86400); $backups = self::get_all_backups(); $cleaned_count = 0; foreach ($backups as $backup_id => $backup) { if ($backup['created_at'] < $cutoff_timestamp) { if (self::delete_backup($backup_id)) { $cleaned_count++; } } } if ($cleaned_count > 0) { HVAC_Logger::info( "Cleaned up $cleaned_count old backups", 'Backup Manager' ); } } /** * Delete backup */ public static function delete_backup($backup_id) { $backup_record = self::get_backup_record($backup_id); if ($backup_record && !empty($backup_record['file_path'])) { if (file_exists($backup_record['file_path'])) { unlink($backup_record['file_path']); } } // Remove from records $backups = get_option('hvac_backups', []); unset($backups[$backup_id]); update_option('hvac_backups', $backups); return true; } /** * Get backup directory */ private static function get_backup_directory() { $upload_dir = wp_upload_dir(); return $upload_dir['basedir'] . '/hvac-backups'; } /** * Update backup record */ private static function update_backup_record($backup_id, $record) { $backups = get_option('hvac_backups', []); $backups[$backup_id] = $record; update_option('hvac_backups', $backups); } /** * Get backup record */ private static function get_backup_record($backup_id) { $backups = get_option('hvac_backups', []); return $backups[$backup_id] ?? null; } /** * Get all backups */ public static function get_all_backups() { return get_option('hvac_backups', []); } /** * Update backup progress */ private static function update_backup_progress($backup_id, $progress) { $backup_record = self::get_backup_record($backup_id); if ($backup_record) { $backup_record['progress'] = $progress; self::update_backup_record($backup_id, $backup_record); } } /** * Check disaster recovery mode */ public static function check_disaster_recovery_mode() { $recovery_mode = get_option('hvac_disaster_recovery_mode', false); if ($recovery_mode) { // Display disaster recovery notice add_action('admin_notices', function() use ($recovery_mode) { echo '
'; echo '

Disaster Recovery Mode Active

'; echo '

Last backup: ' . date('Y-m-d H:i:s', $recovery_mode['timestamp']) . '

'; echo '

Issue: ' . esc_html($recovery_mode['issue']) . '

'; echo '
'; }); } } /** * Send backup alert */ private static function send_backup_alert($subject, $data) { $admin_email = get_option('admin_email'); $site_name = get_bloginfo('name'); $message = "Backup Alert from $site_name\n\n"; $message .= "Subject: $subject\n\n"; foreach ($data as $key => $value) { $message .= ucfirst(str_replace('_', ' ', $key)) . ": $value\n"; } $message .= "\nTime: " . date('Y-m-d H:i:s') . "\n"; wp_mail($admin_email, "[$site_name] $subject", $message); } /** * Get backup statistics */ public static function get_backup_stats() { $backups = self::get_all_backups(); $stats = [ 'total_backups' => count($backups), 'completed_backups' => 0, 'failed_backups' => 0, 'total_size' => 0, 'last_backup' => null, 'next_scheduled' => wp_next_scheduled('hvac_run_backup') ]; foreach ($backups as $backup) { if ($backup['status'] === self::STATUS_COMPLETED) { $stats['completed_backups']++; $stats['total_size'] += $backup['size']; if (!$stats['last_backup'] || $backup['created_at'] > $stats['last_backup']['created_at']) { $stats['last_backup'] = $backup; } } elseif ($backup['status'] === self::STATUS_FAILED) { $stats['failed_backups']++; } } return $stats; } /** * Add admin menu */ public static function add_admin_menu() { if (current_user_can('manage_options')) { add_management_page( 'HVAC Backup Manager', 'HVAC Backups', 'manage_options', 'hvac-backup-manager', [__CLASS__, 'admin_page'] ); } } /** * Admin page */ public static function admin_page() { $stats = self::get_backup_stats(); $backups = array_slice(self::get_all_backups(), -10, 10, true); ?>

HVAC Backup Manager

Backup Overview

Total Backups:

Completed:

Failed:

Total Size:

Last Backup:

Next Scheduled:

Create New Backup

Recent Backups

$backup): ?>
Backup ID Type Status Created Size Actions
No backups found
'manual']); if ($result['success']) { wp_send_json_success('Backup created successfully: ' . $result['backup_id']); } else { wp_send_json_error($result['error']); } break; case 'restore': $backup_id = sanitize_text_field($_POST['backup_id']); $result = self::restore_from_backup($backup_id); if ($result['success']) { wp_send_json_success('Backup restored successfully'); } else { wp_send_json_error($result['error']); } break; case 'delete': $backup_id = sanitize_text_field($_POST['backup_id']); if (self::delete_backup($backup_id)) { wp_send_json_success('Backup deleted successfully'); } else { wp_send_json_error('Failed to delete backup'); } break; default: wp_send_json_error('Unknown action'); } } /** * Show backup notices */ public static function show_backup_notices() { if (!current_user_can('manage_options')) { return; } $stats = self::get_backup_stats(); // Show warning if no recent backups if (!$stats['last_backup'] || $stats['last_backup']['created_at'] < (time() - 7 * 86400)) { echo '
'; echo '

HVAC Backup Warning: No recent backups found. '; echo 'Create a backup now

'; echo '
'; } // Show error if recent backup failures if ($stats['failed_backups'] > 0) { echo '
'; echo '

HVAC Backup Error: Recent backup failures detected. '; echo 'Check backup status

'; echo '
'; } } /** * Register REST endpoints */ public static function register_rest_endpoints() { register_rest_route('hvac/v1', '/backup/stats', [ 'methods' => 'GET', 'callback' => [__CLASS__, 'rest_backup_stats'], 'permission_callback' => function() { return current_user_can('manage_options'); } ]); register_rest_route('hvac/v1', '/backup/create', [ 'methods' => 'POST', 'callback' => [__CLASS__, 'rest_create_backup'], 'permission_callback' => function() { return current_user_can('manage_options'); } ]); } /** * REST API backup stats */ public static function rest_backup_stats() { $stats = self::get_backup_stats(); return new WP_REST_Response([ 'stats' => $stats, 'timestamp' => time() ], 200); } /** * REST API create backup */ public static function rest_create_backup($request) { $type = $request->get_param('type') ?: self::BACKUP_CRITICAL_DATA; $result = self::create_backup($type, [ 'triggered_by' => 'api' ]); if ($result['success']) { return new WP_REST_Response($result, 200); } else { return new WP_REST_Response($result, 500); } } /** * WP-CLI backup command */ public static function wp_cli_backup($args, $assoc_args) { $subcommand = $args[0] ?? 'create'; switch ($subcommand) { case 'create': $type = $assoc_args['type'] ?? self::BACKUP_CRITICAL_DATA; WP_CLI::line("Creating $type backup..."); $result = self::create_backup($type, ['triggered_by' => 'cli']); if ($result['success']) { WP_CLI::success("Backup created: {$result['backup_id']}"); WP_CLI::line("Size: " . size_format($result['size'])); } else { WP_CLI::error($result['error']); } break; case 'list': $backups = self::get_all_backups(); WP_CLI::line('Recent backups:'); foreach (array_slice($backups, -10, 10, true) as $id => $backup) { WP_CLI::line(sprintf( '%s - %s (%s) - %s', $id, $backup['type'], $backup['status'], date('Y-m-d H:i:s', $backup['created_at']) )); } break; case 'restore': $backup_id = $assoc_args['id'] ?? ''; if (empty($backup_id)) { WP_CLI::error('Backup ID required. Use --id='); } WP_CLI::line("Restoring from backup: $backup_id"); $result = self::restore_from_backup($backup_id); if ($result['success']) { WP_CLI::success('Backup restored successfully'); } else { WP_CLI::error($result['error']); } break; case 'stats': $stats = self::get_backup_stats(); WP_CLI::line('Backup Statistics:'); WP_CLI::line('Total Backups: ' . $stats['total_backups']); WP_CLI::line('Completed: ' . $stats['completed_backups']); WP_CLI::line('Failed: ' . $stats['failed_backups']); WP_CLI::line('Total Size: ' . size_format($stats['total_size'])); break; default: WP_CLI::error('Unknown subcommand. Use: create, list, restore, stats'); } } }