security = tigerstyle_life9()->get_security(); } /** * Register all REST API routes */ public function register_routes() { // Dashboard endpoints register_rest_route($this->namespace, '/dashboard/stats', [ 'methods' => 'GET', 'callback' => [$this, 'get_dashboard_stats'], 'permission_callback' => [$this, 'check_permissions'] ]); // Backup endpoints register_rest_route($this->namespace, '/backups', [ [ 'methods' => 'GET', 'callback' => [$this, 'get_backups'], 'permission_callback' => [$this, 'check_permissions'], 'args' => $this->get_backups_args() ], [ 'methods' => 'POST', 'callback' => [$this, 'create_backup'], 'permission_callback' => [$this, 'check_permissions'], 'args' => $this->create_backup_args() ] ]); register_rest_route($this->namespace, '/backups/(?P\d+)', [ [ 'methods' => 'GET', 'callback' => [$this, 'get_backup'], 'permission_callback' => [$this, 'check_permissions'] ], [ 'methods' => 'DELETE', 'callback' => [$this, 'delete_backup'], 'permission_callback' => [$this, 'check_permissions'] ] ]); register_rest_route($this->namespace, '/backups/(?P\d+)/status', [ 'methods' => 'GET', 'callback' => [$this, 'get_backup_status'], 'permission_callback' => [$this, 'check_permissions'] ]); register_rest_route($this->namespace, '/backups/(?P\d+)/cancel', [ 'methods' => 'POST', 'callback' => [$this, 'cancel_backup'], 'permission_callback' => [$this, 'check_permissions'] ]); register_rest_route($this->namespace, '/backups/(?P\d+)/download', [ 'methods' => 'POST', 'callback' => [$this, 'download_backup'], 'permission_callback' => [$this, 'check_permissions'] ]); // Restore endpoints register_rest_route($this->namespace, '/restore', [ 'methods' => 'POST', 'callback' => [$this, 'start_restore'], 'permission_callback' => [$this, 'check_permissions'], 'args' => $this->restore_args() ]); register_rest_route($this->namespace, '/restore/(?P\d+)/status', [ 'methods' => 'GET', 'callback' => [$this, 'get_restore_status'], 'permission_callback' => [$this, 'check_permissions'] ]); // File management endpoints register_rest_route($this->namespace, '/files/browse', [ 'methods' => 'POST', 'callback' => [$this, 'browse_files'], 'permission_callback' => [$this, 'check_permissions'], 'args' => [ 'path' => [ 'required' => false, 'type' => 'string', 'default' => ABSPATH, 'sanitize_callback' => [$this, 'sanitize_path'] ], 'show_hidden' => [ 'required' => false, 'type' => 'boolean', 'default' => false ] ] ]); register_rest_route($this->namespace, '/files/preview', [ 'methods' => 'POST', 'callback' => [$this, 'preview_file'], 'permission_callback' => [$this, 'check_permissions'], 'args' => [ 'path' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => [$this, 'sanitize_path'], 'validate_callback' => [$this, 'validate_file_path'] ] ] ]); // Settings endpoints register_rest_route($this->namespace, '/settings', [ [ 'methods' => 'GET', 'callback' => [$this, 'get_settings'], 'permission_callback' => [$this, 'check_permissions'] ], [ 'methods' => 'POST', 'callback' => [$this, 'update_settings'], 'permission_callback' => [$this, 'check_permissions'] ] ]); // System endpoints register_rest_route($this->namespace, '/system/status', [ 'methods' => 'GET', 'callback' => [$this, 'get_system_status'], 'permission_callback' => [$this, 'check_permissions'] ]); // Progress streaming endpoint register_rest_route($this->namespace, '/progress-stream', [ 'methods' => 'GET', 'callback' => [$this, 'progress_stream'], 'permission_callback' => [$this, 'check_permissions'] ]); } /** * Check user permissions * * @param WP_REST_Request $request Request object * @return bool */ public function check_permissions($request) { if (!current_user_can('manage_options')) { return false; } // Verify nonce for state-changing operations $method = $request->get_method(); if (in_array($method, ['POST', 'PUT', 'DELETE', 'PATCH'])) { $nonce = $request->get_header('X-WP-Nonce'); if (!wp_verify_nonce($nonce, 'wp_rest')) { return false; } } return true; } /** * Get dashboard statistics * * @param WP_REST_Request $request Request object * @return WP_REST_Response */ public function get_dashboard_stats($request) { try { global $wpdb; $stats = $wpdb->get_row( "SELECT COUNT(*) as total_backups, SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as successful_backups, SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed_backups, SUM(CASE WHEN status = 'completed' THEN COALESCE(file_size, 0) ELSE 0 END) as total_size, MAX(CASE WHEN status = 'completed' THEN created_at ELSE NULL END) as last_backup FROM {$wpdb->prefix}tigerstyle_life9_backups" ); $response_data = [ 'totalBackups' => intval($stats->total_backups ?? 0), 'successfulBackups' => intval($stats->successful_backups ?? 0), 'failedBackups' => intval($stats->failed_backups ?? 0), 'totalSize' => intval($stats->total_size ?? 0), 'lastBackup' => $stats->last_backup ]; return rest_ensure_response(['success' => true, 'data' => $response_data]); } catch (Exception $e) { error_log('TigerStyle Life9: Dashboard stats error - ' . $e->getMessage()); return new WP_Error('stats_error', __('Failed to get dashboard statistics', 'tigerstyle-life9'), ['status' => 500]); } } /** * Get backups list * * @param WP_REST_Request $request Request object * @return WP_REST_Response */ public function get_backups($request) { try { global $wpdb; $limit = intval($request->get_param('limit') ?: 20); $offset = intval($request->get_param('offset') ?: 0); $status = $request->get_param('status'); $where_clause = ''; $where_params = []; if ($status) { $where_clause = 'WHERE status = %s'; $where_params[] = $status; } $backups = $wpdb->get_results($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}tigerstyle_life9_backups {$where_clause} ORDER BY created_at DESC LIMIT %d OFFSET %d", array_merge($where_params, [$limit, $offset]) )); $total = $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}tigerstyle_life9_backups {$where_clause}", $where_params )); return rest_ensure_response([ 'success' => true, 'data' => $backups, 'total' => intval($total), 'limit' => $limit, 'offset' => $offset ]); } catch (Exception $e) { error_log('TigerStyle Life9: Get backups error - ' . $e->getMessage()); return new WP_Error('get_backups_error', __('Failed to get backups', 'tigerstyle-life9'), ['status' => 500]); } } /** * Create new backup * * @param WP_REST_Request $request Request object * @return WP_REST_Response */ public function create_backup($request) { try { $config = $request->get_json_params(); $sanitizer = new TigerStyle_Life9_Sanitizer(); $validator = new TigerStyle_Life9_Validator(); $clean_config = $sanitizer->sanitize_backup_config($config); if (!$validator->validate_backup_config($clean_config)) { return new WP_Error( 'invalid_config', __('Invalid backup configuration', 'tigerstyle-life9'), ['status' => 400, 'errors' => $validator->get_errors()] ); } $backup_engine = new TigerStyle_Life9_Backup_Engine(); $backup_id = $backup_engine->start_backup($clean_config); if ($backup_id) { return rest_ensure_response([ 'success' => true, 'data' => [ 'backup_id' => $backup_id, 'message' => __('Backup started successfully', 'tigerstyle-life9'), 'status_url' => rest_url($this->namespace . '/backups/' . $backup_id . '/status') ] ]); } else { return new WP_Error('backup_start_failed', __('Failed to start backup', 'tigerstyle-life9'), ['status' => 500]); } } catch (Exception $e) { error_log('TigerStyle Life9: Create backup error - ' . $e->getMessage()); return new WP_Error('backup_error', __('An error occurred while starting backup', 'tigerstyle-life9'), ['status' => 500]); } } /** * Get single backup * * @param WP_REST_Request $request Request object * @return WP_REST_Response */ public function get_backup($request) { $backup_id = intval($request->get_param('id')); try { global $wpdb; $backup = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}tigerstyle_life9_backups WHERE id = %d", $backup_id )); if (!$backup) { return new WP_Error('backup_not_found', __('Backup not found', 'tigerstyle-life9'), ['status' => 404]); } return rest_ensure_response(['success' => true, 'data' => $backup]); } catch (Exception $e) { error_log('TigerStyle Life9: Get backup error - ' . $e->getMessage()); return new WP_Error('get_backup_error', __('Failed to get backup', 'tigerstyle-life9'), ['status' => 500]); } } /** * Delete backup * * @param WP_REST_Request $request Request object * @return WP_REST_Response */ public function delete_backup($request) { $backup_id = intval($request->get_param('id')); try { global $wpdb; $backup = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}tigerstyle_life9_backups WHERE id = %d", $backup_id )); if (!$backup) { return new WP_Error('backup_not_found', __('Backup not found', 'tigerstyle-life9'), ['status' => 404]); } // Delete file if exists if ($backup->file_path && file_exists($backup->file_path)) { $encryption = new TigerStyle_Life9_Encryption(); $encryption->secure_delete($backup->file_path); } // Delete from database $wpdb->delete( $wpdb->prefix . 'tigerstyle_life9_backups', ['id' => $backup_id], ['%d'] ); // Log the deletion $this->security->log_security_event('backup_deleted', [ 'backup_id' => $backup_id, 'backup_name' => $backup->name ]); return rest_ensure_response([ 'success' => true, 'data' => ['message' => __('Backup deleted successfully', 'tigerstyle-life9')] ]); } catch (Exception $e) { error_log('TigerStyle Life9: Delete backup error - ' . $e->getMessage()); return new WP_Error('delete_backup_error', __('Failed to delete backup', 'tigerstyle-life9'), ['status' => 500]); } } /** * Browse files * * @param WP_REST_Request $request Request object * @return WP_REST_Response */ public function browse_files($request) { $path = $request->get_param('path'); $show_hidden = $request->get_param('show_hidden'); try { $scanner = new TigerStyle_Life9_File_Scanner(); $files = $scanner->scan_directory($path, [ 'show_hidden' => $show_hidden, 'max_depth' => 1 ]); return rest_ensure_response([ 'success' => true, 'data' => [ 'files' => $files, 'current_path' => $path ] ]); } catch (Exception $e) { error_log('TigerStyle Life9: Browse files error - ' . $e->getMessage()); return new WP_Error('browse_error', __('Failed to browse files', 'tigerstyle-life9'), ['status' => 500]); } } /** * Get system status * * @param WP_REST_Request $request Request object * @return WP_REST_Response */ public function get_system_status($request) { try { $upload_dir = wp_upload_dir(); $backup_dir = $upload_dir['basedir'] . '/tigerstyle-life9'; $status = [ 'php_version' => PHP_VERSION, 'php_version_ok' => version_compare(PHP_VERSION, '8.0', '>='), 'wp_version' => get_bloginfo('version'), 'wp_version_ok' => version_compare(get_bloginfo('version'), '6.0', '>='), 'available_space' => disk_free_space($backup_dir), 'disk_space_ok' => disk_free_space($backup_dir) > (1024 * 1024 * 1024), // 1GB 'permissions_ok' => is_writable($backup_dir), 'extensions' => [ 'openssl' => extension_loaded('openssl'), 'zip' => extension_loaded('zip'), 'curl' => extension_loaded('curl'), 'json' => extension_loaded('json') ] ]; return rest_ensure_response(['success' => true, 'data' => $status]); } catch (Exception $e) { error_log('TigerStyle Life9: System status error - ' . $e->getMessage()); return new WP_Error('system_status_error', __('Failed to get system status', 'tigerstyle-life9'), ['status' => 500]); } } /** * Progress streaming endpoint (Server-Sent Events) * * @param WP_REST_Request $request Request object */ public function progress_stream($request) { // Set headers for Server-Sent Events header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); header('Connection: keep-alive'); // Get backup ID from query $backup_id = intval($request->get_param('backup_id')); if (!$backup_id) { echo "event: error\n"; echo "data: " . json_encode(['error' => 'Invalid backup ID']) . "\n\n"; exit; } // Stream progress updates for ($i = 0; $i < 60; $i++) { // Max 60 seconds global $wpdb; $backup = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}tigerstyle_life9_backups WHERE id = %d", $backup_id )); if ($backup) { $progress_data = [ 'backup_id' => $backup->id, 'status' => $backup->status, 'progress' => $this->calculate_progress($backup), 'message' => $this->get_status_message($backup->status) ]; echo "event: progress\n"; echo "data: " . json_encode($progress_data) . "\n\n"; if (in_array($backup->status, ['completed', 'failed', 'cancelled'])) { echo "event: complete\n"; echo "data: " . json_encode($progress_data) . "\n\n"; break; } } ob_flush(); flush(); sleep(1); } exit; } /** * Sanitize file path * * @param string $path File path * @return string|false */ public function sanitize_path($path) { return $this->security->validate_path($path, ABSPATH) ? $path : false; } /** * Validate file path * * @param string $path File path * @return bool */ public function validate_file_path($path) { return $this->security->validate_path($path, ABSPATH) && file_exists($path); } /** * Get backup creation arguments * * @return array */ private function create_backup_args() { return [ 'backup_name' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ], 'backup_type' => [ 'required' => false, 'type' => 'string', 'default' => 'full', 'enum' => ['full', 'files', 'database'] ], 'include_files' => [ 'required' => false, 'type' => 'boolean', 'default' => true ], 'include_database' => [ 'required' => false, 'type' => 'boolean', 'default' => true ], 'compression_method' => [ 'required' => false, 'type' => 'string', 'default' => 'zip', 'enum' => ['zip', 'tar', 'gzip', 'none'] ] ]; } /** * Get backups list arguments * * @return array */ private function get_backups_args() { return [ 'limit' => [ 'required' => false, 'type' => 'integer', 'default' => 20, 'minimum' => 1, 'maximum' => 100 ], 'offset' => [ 'required' => false, 'type' => 'integer', 'default' => 0, 'minimum' => 0 ], 'status' => [ 'required' => false, 'type' => 'string', 'enum' => ['pending', 'running', 'completed', 'failed', 'cancelled'] ] ]; } /** * Get restore arguments * * @return array */ private function restore_args() { return [ 'backup_id' => [ 'required' => true, 'type' => 'integer', 'minimum' => 1 ], 'restore_files' => [ 'required' => false, 'type' => 'boolean', 'default' => true ], 'restore_database' => [ 'required' => false, 'type' => 'boolean', 'default' => true ], 'replace_urls' => [ 'required' => false, 'type' => 'boolean', 'default' => false ] ]; } /** * Calculate backup progress * * @param object $backup Backup record * @return int Progress percentage */ private function calculate_progress($backup) { switch ($backup->status) { case 'completed': return 100; case 'failed': case 'cancelled': return 0; case 'running': return 50; // This would be more sophisticated in real implementation default: return 0; } } /** * Get status message * * @param string $status Backup status * @return string */ private function get_status_message($status) { $messages = [ 'pending' => __('Backup queued', 'tigerstyle-life9'), 'running' => __('Backup in progress', 'tigerstyle-life9'), 'completed' => __('Backup completed successfully', 'tigerstyle-life9'), 'failed' => __('Backup failed', 'tigerstyle-life9'), 'cancelled' => __('Backup cancelled', 'tigerstyle-life9') ]; return $messages[$status] ?? __('Unknown status', 'tigerstyle-life9'); } }