/** * Database & Server Settings - Unified Design with TypeScript * Enhanced with Autoload Management features */ import { useState, useEffect } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { Spinner, SelectControl, } from '@wordpress/components'; import apiFetch from '@wordpress/api-fetch'; import ProrankToggleSlider from '../../../components/ProrankToggleSlider'; import ProrankButton from '../../../components/ProrankButton'; import { useNotification } from '../../../contexts/NotificationContext'; import BloatControlPanel from './BloatControlPanel'; import * as React from 'react'; // TypeScript Interfaces interface CleanupItems { revisions: boolean; drafts: boolean; trash: boolean; spam_comments: boolean; unapproved_comments: boolean; transients: boolean; expired_transients: boolean; orphaned_meta: boolean; action_scheduler: boolean; wpmailsmtp_logs: boolean; } interface DatabaseSettings { auto_cleanup_enabled: boolean; cleanup_schedule: 'daily' | 'weekly' | 'monthly'; cleanup_items: CleanupItems; revision_limit: number; trash_days: number; action_scheduler_retention_days: number; wpmailsmtp_log_retention_days: number; heartbeat_control: boolean; heartbeat_frequency: number; heartbeat_frequency_front: number; heartbeat_frequency_admin: number; heartbeat_frequency_editor: number; } interface DatabaseStats { table_count?: number; total_size?: string; revisions_count?: number; trash_count?: number; trash_posts_count?: number; trash_comments_count?: number; pending_comments_count?: number; spam_comments_count?: number; transients_count?: number; orphaned_meta?: number; } interface ServerInfo { php_version: string; memory_limit: string; max_execution_time: string; upload_max_filesize: string; post_max_size: string; opcache_enabled: boolean; opcache_info?: { enabled: boolean; memory_usage: string; hit_rate: string; }; mysql_version?: string; mysql_type?: string; server_software?: string; object_cache_enabled?: boolean; object_cache_type?: string; object_cache_info?: { version?: string; memory?: string; }; object_cache_dropin_exists?: boolean; object_cache_available_backends?: string[]; } // Autoload Analysis Interfaces interface AutoloadOption { name: string; size: number; reason?: string; } interface AutoloadGuardReport { last_run: string | null; threshold_kb: number; found: number; disabled_count: number; disabled: { name: string; size_kb: number }[]; } interface AutoloadAnalysis { total_size: number; total_count: number; large_options: AutoloadOption[]; suspicious_options: AutoloadOption[]; recommendations: string[]; guard?: AutoloadGuardReport; } // Table Management Interface interface TableInfo { name: string; rows: number; data_size: number; index_size: number; total_size: number; overhead: number; engine: string; collation: string; created: string | null; updated: string | null; is_core: boolean; can_optimize: boolean; can_repair: boolean; can_convert_to_innodb?: boolean; conversion_note?: string; } // Cron Job Interface interface CronJob { hook: string; timestamp: number; next_run: string; schedule: string; interval: number; args: Record | any[]; is_orphaned: boolean; can_delete?: boolean; } // Backup Interface interface DatabaseBackup { filename: string; size: number; size_formatted?: string; created?: string; date?: string; tables?: number | string[]; storage?: string; hash?: string; } interface DatabaseServerSettingsProps { onClose?: () => void; embedded?: boolean; } interface ApiSettingsResponse { settings: Partial; } interface ApiStatsResponse { stats: DatabaseStats; } interface ApiServerInfoResponse { info: ServerInfo; } interface CleanupResult { success: boolean; partial_success?: boolean; deleted: number; table: string | null; tables?: Array<{ table: string; deleted: number; optimized: boolean; error?: string | null; }>; optimized: boolean; optimized_tables?: string[]; error?: string | null; } interface ApiOptimizeResponse { success?: boolean; message?: string; cleaned?: number; tasks_run?: number; tables_optimized?: number; cleanup_results?: Record; } interface ApiAutoloadResponse { total_size: number; total_count: number; large_options: AutoloadOption[]; suspicious_options: AutoloadOption[]; recommendations: string[]; guard?: AutoloadGuardReport; } const normalizeBooleanSetting = (value: unknown): boolean => { if (typeof value === 'boolean') { return value; } if (typeof value === 'number') { return value !== 0; } if (typeof value === 'string') { const normalized = value.trim().toLowerCase(); if (['1', 'true', 'yes', 'on', 'enabled'].includes(normalized)) { return true; } if (['0', 'false', 'no', 'off', 'disabled', ''].includes(normalized)) { return false; } } return Boolean(value); }; const normalizeNumberSetting = (value: unknown, fallback: number): number => { const numeric = Number(value); return Number.isFinite(numeric) ? numeric : fallback; }; const normalizeDatabaseSettings = (value: Partial): Partial => ({ ...value, auto_cleanup_enabled: normalizeBooleanSetting(value.auto_cleanup_enabled), heartbeat_control: normalizeBooleanSetting(value.heartbeat_control), heartbeat_frequency: normalizeNumberSetting(value.heartbeat_frequency, 60), heartbeat_frequency_front: normalizeNumberSetting(value.heartbeat_frequency_front, 60), heartbeat_frequency_admin: normalizeNumberSetting(value.heartbeat_frequency_admin, 60), heartbeat_frequency_editor: normalizeNumberSetting(value.heartbeat_frequency_editor, 60), cleanup_items: { revisions: normalizeBooleanSetting(value.cleanup_items?.revisions), drafts: normalizeBooleanSetting(value.cleanup_items?.drafts), trash: normalizeBooleanSetting(value.cleanup_items?.trash), spam_comments: normalizeBooleanSetting(value.cleanup_items?.spam_comments), unapproved_comments: normalizeBooleanSetting(value.cleanup_items?.unapproved_comments), transients: normalizeBooleanSetting(value.cleanup_items?.transients), expired_transients: normalizeBooleanSetting(value.cleanup_items?.expired_transients), orphaned_meta: normalizeBooleanSetting(value.cleanup_items?.orphaned_meta), action_scheduler: normalizeBooleanSetting(value.cleanup_items?.action_scheduler), wpmailsmtp_logs: normalizeBooleanSetting(value.cleanup_items?.wpmailsmtp_logs), }, }); const formatObjectCacheBackendName = (backend: string): string => { switch (backend) { case 'redis': return 'Redis'; case 'memcached': return 'Memcached'; case 'memcache': return 'Memcache'; case 'apcu': return 'APCu'; case 'external': return __('External', 'prorank-seo'); default: return backend ? backend.charAt(0).toUpperCase() + backend.slice(1) : __('None', 'prorank-seo'); } }; const DatabaseServerSettings: React.FC = ({ onClose, embedded = false }) => { const [settings, setSettings] = useState({ // Database settings auto_cleanup_enabled: false, cleanup_schedule: 'weekly', cleanup_items: { revisions: true, drafts: false, trash: true, spam_comments: true, unapproved_comments: false, transients: true, expired_transients: true, orphaned_meta: false, action_scheduler: false, wpmailsmtp_logs: false, }, revision_limit: 5, trash_days: 30, action_scheduler_retention_days: 7, wpmailsmtp_log_retention_days: 30, heartbeat_control: false, heartbeat_frequency: 60, heartbeat_frequency_front: 60, heartbeat_frequency_admin: 60, heartbeat_frequency_editor: 60, }); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [optimizing, setOptimizing] = useState(false); const [lastCleanupResult, setLastCleanupResult] = useState(null); const [databaseStats, setDatabaseStats] = useState(null); const [serverInfo, setServerInfo] = useState(null); // Autoload state const [autoloadAnalysis, setAutoloadAnalysis] = useState(null); const [tablesInfo, setTablesInfo] = useState([]); const [tablesLoading, setTablesLoading] = useState(false); const [tableActionLoading, setTableActionLoading] = useState(null); const [cronJobs, setCronJobs] = useState([]); const [cronLoading, setCronLoading] = useState(false); const [cronDeleteLoading, setCronDeleteLoading] = useState(null); const [backups, setBackups] = useState([]); const [backupsLoading, setBackupsLoading] = useState(false); const [backupCreating, setBackupCreating] = useState(false); const [backupActionLoading, setBackupActionLoading] = useState(null); const [loadingAutoload, setLoadingAutoload] = useState(false); const [togglingAutoload, setTogglingAutoload] = useState(null); const selectedCleanupCount = Object.values(settings.cleanup_items || {}).filter(Boolean).length; const objectCacheActive = Boolean( serverInfo?.object_cache_enabled && serverInfo?.object_cache_type && serverInfo.object_cache_type !== 'none' ); const objectCacheAvailableBackends = serverInfo?.object_cache_available_backends || []; const objectCacheAvailable = objectCacheAvailableBackends.length > 0; const objectCacheBadgeBackground = objectCacheActive ? '#e8f5e9' : objectCacheAvailable ? '#fff8e1' : '#f3f4f6'; const objectCacheBadgeColor = objectCacheActive ? '#2e7d32' : objectCacheAvailable ? '#b26a00' : '#6b7280'; const objectCacheBadgeLabel = objectCacheActive ? formatObjectCacheBackendName(serverInfo?.object_cache_type || 'external') : objectCacheAvailable ? __('Available', 'prorank-seo') : __('Unavailable', 'prorank-seo'); const objectCacheHelpText = objectCacheActive ? sprintf( __('Active via %1$s. %2$s', 'prorank-seo'), formatObjectCacheBackendName(serverInfo?.object_cache_type || 'external'), serverInfo?.object_cache_dropin_exists ? __('A valid object-cache drop-in is loaded.', 'prorank-seo') : __('The external object cache is active.', 'prorank-seo') ) : objectCacheAvailable ? sprintf( __('%1$s is available on this server, but no active object-cache drop-in is loaded. Enable it through your hosting stack or a compatible cache plugin.', 'prorank-seo'), objectCacheAvailableBackends.map(formatObjectCacheBackendName).join(', ') ) : __('No supported object-cache backend was detected on this server.', 'prorank-seo'); const { showNotification } = useNotification(); useEffect(() => { loadSettings(); loadDatabaseStats(); loadServerInfo(); loadAutoloadAnalysis(); loadTablesInfo(); loadCronJobs(); loadBackups(); }, []); const loadSettings = async () => { try { const response = await apiFetch({ path: '/prorank-seo/v1/settings/database', }); if (response?.settings) { const normalizedSettings = normalizeDatabaseSettings(response.settings); // Ensure cleanup_items exists with defaults const mergedSettings = { ...settings, ...normalizedSettings, cleanup_items: { ...settings.cleanup_items, ...(normalizedSettings.cleanup_items || {}) } }; setSettings(mergedSettings); } } catch (error) { // Silently fail } finally { setLoading(false); } }; const loadDatabaseStats = async () => { try { const response = await apiFetch({ path: '/prorank-seo/v1/database/stats', }); if (response?.stats) { setDatabaseStats(response.stats); } } catch (error) { // Silently fail } }; const loadServerInfo = async () => { try { const response = await apiFetch({ path: '/prorank-seo/v1/server/info', }); if (response?.info) { setServerInfo(response.info); } } catch (error) { // Set default values if API fails setServerInfo({ php_version: 'Unknown', memory_limit: 'Unknown', max_execution_time: 'Unknown', upload_max_filesize: 'Unknown', post_max_size: 'Unknown', opcache_enabled: false, }); } }; // Autoload Analysis const loadAutoloadAnalysis = async () => { setLoadingAutoload(true); try { const response = await apiFetch({ path: '/prorank-seo/v1/database-optimization/analyze-autoload', }); if (response) { setAutoloadAnalysis({ total_size: response.total_size || 0, total_count: response.total_count || 0, large_options: response.large_options || [], suspicious_options: response.suspicious_options || [], recommendations: response.recommendations || [], guard: response.guard, }); } } catch (error) { // Silently fail - feature may not be available } finally { setLoadingAutoload(false); } }; const toggleAutoload = async (optionName: string, currentAutoload: boolean) => { setTogglingAutoload(optionName); try { await apiFetch({ path: '/prorank-seo/v1/database-optimization/option/autoload', method: 'POST', data: { option_name: optionName, autoload: currentAutoload ? 'no' : 'yes' }, }); showNotification( currentAutoload ? __('Autoload disabled for this option', 'prorank-seo') : __('Autoload enabled for this option', 'prorank-seo'), 'success' ); // Reload autoload analysis await loadAutoloadAnalysis(); } catch (error: any) { showNotification(error.message || __('Failed to update autoload', 'prorank-seo'), 'error'); } finally { setTogglingAutoload(null); } }; // Load table information const loadTablesInfo = async () => { setTablesLoading(true); try { const response = await apiFetch({ path: '/prorank-seo/v1/database-optimization/tables', }); setTablesInfo(response || []); } catch (error) { console.error('Failed to load tables info:', error); } finally { setTablesLoading(false); } }; // Handle table operations (optimize, repair, convert) const handleTableOperation = async (tableName: string, operation: 'optimize' | 'repair' | 'convert') => { setTableActionLoading(tableName + '-' + operation); try { await apiFetch({ path: '/prorank-seo/v1/database-optimization/table/' + operation, method: 'POST', data: { table_name: tableName }, }); await loadTablesInfo(); } catch (error) { console.error('Table operation failed:', error); } finally { setTableActionLoading(null); } }; // Load cron jobs const loadCronJobs = async () => { setCronLoading(true); try { const response = await apiFetch({ path: '/prorank-seo/v1/database-optimization/cron', }); setCronJobs(response || []); } catch (error) { console.error('Failed to load cron jobs:', error); } finally { setCronLoading(false); } }; // Delete cron job const handleDeleteCron = async (hook: string, timestamp: number, args: Record | any[] = []) => { setCronDeleteLoading(hook + '-' + timestamp); try { await apiFetch({ path: '/prorank-seo/v1/database-optimization/cron/delete', method: 'DELETE', data: { hook, timestamp, args }, }); await loadCronJobs(); showNotification(__('Orphaned cron job deleted', 'prorank-seo'), 'success'); } catch (error) { console.error('Failed to delete cron job:', error); showNotification(__('Failed to delete cron job', 'prorank-seo'), 'error'); } finally { setCronDeleteLoading(null); } }; // Load backups list const loadBackups = async () => { setBackupsLoading(true); try { const response = await apiFetch({ path: '/prorank-seo/v1/database-optimization/backup/list', }); setBackups(response || []); } catch (error) { console.error('Failed to load backups:', error); } finally { setBackupsLoading(false); } }; // Create backup const handleCreateBackup = async () => { setBackupCreating(true); try { await apiFetch({ path: '/prorank-seo/v1/database-optimization/backup/create', method: 'POST', }); await loadBackups(); } catch (error) { console.error('Failed to create backup:', error); } finally { setBackupCreating(false); } }; // Restore backup const handleRestoreBackup = async (filename: string) => { if (!confirm(__('Are you sure you want to restore this backup? This will overwrite current data.', 'prorank-seo'))) { return; } setBackupActionLoading(filename + '-restore'); try { await apiFetch({ path: '/prorank-seo/v1/database-optimization/backup/restore', method: 'POST', data: { backup_file: filename }, }); alert(__('Backup restored successfully!', 'prorank-seo')); } catch (error) { console.error('Failed to restore backup:', error); } finally { setBackupActionLoading(null); } }; // Delete backup const handleDeleteBackup = async (filename: string) => { if (!confirm(__('Are you sure you want to delete this backup?', 'prorank-seo'))) { return; } setBackupActionLoading(filename + '-delete'); try { await apiFetch({ path: '/prorank-seo/v1/database-optimization/backup/delete', method: 'DELETE', data: { filename }, }); await loadBackups(); } catch (error) { console.error('Failed to delete backup:', error); } finally { setBackupActionLoading(null); } }; const saveSettings = async () => { setSaving(true); try { await apiFetch({ path: '/prorank-seo/v1/settings/database', method: 'POST', data: { settings }, }); showNotification(__('Settings saved successfully', 'prorank-seo'), 'success'); } catch (error: any) { showNotification(error.message || __('Failed to save settings', 'prorank-seo'), 'error'); } finally { setSaving(false); } }; const runOptimization = async () => { if (selectedCleanupCount === 0) { showNotification( __('Select at least one cleanup item first.', 'prorank-seo'), 'warning' ); return; } setOptimizing(true); setLastCleanupResult(null); try { const response = await apiFetch({ path: '/prorank-seo/v1/database/optimize', method: 'POST', data: { items: settings.cleanup_items || {} }, }); setLastCleanupResult(response); showNotification( response.message || __('Database cleanup completed successfully', 'prorank-seo'), (response.cleaned ?? 0) > 0 ? 'success' : 'info' ); await loadDatabaseStats(); await loadAutoloadAnalysis(); await loadTablesInfo(); } catch (error: any) { showNotification(error.message || __('Failed to run selected cleanup', 'prorank-seo'), 'error'); } finally { setOptimizing(false); } }; // Helper function to update settings const updateSetting = (key: K, value: DatabaseSettings[K]) => { setSettings(prev => ({ ...prev, [key]: value })); }; const updateCleanupItem = (item: keyof CleanupItems, value: boolean) => { setSettings(prev => ({ ...prev, cleanup_items: { ...prev.cleanup_items, [item]: value, } })); }; // Helper to format bytes const formatBytes = (bytes: number): string => { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; // Get autoload health status const getAutoloadHealth = (): { status: 'healthy' | 'warning' | 'critical'; label: string; color: string } => { if (!autoloadAnalysis) return { status: 'healthy', label: __('Unknown', 'prorank-seo'), color: '#6b7280' }; const sizeKB = autoloadAnalysis.total_size; if (sizeKB < 500) { return { status: 'healthy', label: __('Healthy', 'prorank-seo'), color: '#16a34a' }; } else if (sizeKB < 800) { return { status: 'warning', label: __('Needs Attention', 'prorank-seo'), color: '#d97706' }; } else { return { status: 'critical', label: __('Critical - May cause 502 errors', 'prorank-seo'), color: '#dc2626' }; } }; if (loading) { return (

{__('Loading database settings...', 'prorank-seo')}

); } const autoloadHealth = getAutoloadHealth(); const autoloadGuard = autoloadAnalysis?.guard; const getCleanupTaskLabel = (type: string): string => { const labels: Record = { revisions: __('Post Revisions', 'prorank-seo'), auto_drafts: __('Auto-Drafts', 'prorank-seo'), trash: __('Trash', 'prorank-seo'), spam: __('Comments Marked as Spam', 'prorank-seo'), pending: __('Pending / Unapproved Comments', 'prorank-seo'), transients: __('All Transients', 'prorank-seo'), expired_transients: __('Expired Transients', 'prorank-seo'), orphaned: __('Orphaned Meta', 'prorank-seo'), action_scheduler: __('Action Scheduler History', 'prorank-seo'), wpmailsmtp_logs: __('WP Mail SMTP Email Logs', 'prorank-seo'), }; return labels[type] || type.replace(/_/g, ' '); }; const getCleanupTaskStatus = (result: CleanupResult): { color: string; icon: string } => { if (result.success) { return { color: '#16a34a', icon: '\u2713' }; } if (result.partial_success) { return { color: '#d97706', icon: '\u26a0' }; } return { color: '#dc2626', icon: '\u2717' }; }; return (
{/* Unified WordPress Bloat Control (reuses existing settings/runtime). Replaces the former "Bloat Cleanup Preset" card, which called the destructive /performance/presets/bloat-cleanup endpoint (scheduled DB cleanup + heartbeat changes) while mislabelled "Safe preset". */} {/* Database Stats Card */}

{__('Database Statistics', 'prorank-seo')}

{__('Total Tables', 'prorank-seo')} {databaseStats?.table_count || 0}
{__('Database Size', 'prorank-seo')} {databaseStats?.total_size || '0 MB'}
{__('Post Revisions', 'prorank-seo')} {databaseStats?.revisions_count || 0}
{__('Trashed Items', 'prorank-seo')} {databaseStats?.trash_count || 0} {(databaseStats?.trash_posts_count || 0)} {__('posts', 'prorank-seo')} • {(databaseStats?.trash_comments_count || 0)} {__('comments', 'prorank-seo')}
{__('Pending Comments', 'prorank-seo')} {databaseStats?.pending_comments_count || 0}
{__('Comments Marked as Spam', 'prorank-seo')} {databaseStats?.spam_comments_count || 0}
{__('Transients', 'prorank-seo')} {databaseStats?.transients_count || 0}
{__('Orphaned Meta', 'prorank-seo')} {databaseStats?.orphaned_meta || 0}
{/* Autoload Management Card */}

{__('Autoload Management', 'prorank-seo')}

{autoloadHealth.label}
{loadingAutoload ? (
{__('Analyzing autoload data...', 'prorank-seo')}
) : autoloadAnalysis ? ( <> {/* Autoload Size Indicator */}
{__('Total Autoload Size', 'prorank-seo')} {formatBytes(autoloadAnalysis.total_size * 1024)} / 800 KB
{/* Warning threshold marker at 500KB */}
{/* Critical threshold marker at 800KB */}
0 KB 500 KB 800 KB
{/* Stats Row */}
{autoloadAnalysis.total_count}
{__('Options', 'prorank-seo')}
0 ? '#fef3c7' : '#f0fdf4', borderRadius: '8px' }}>
0 ? '#d97706' : '#16a34a' }}> {autoloadAnalysis.large_options.length}
{__('Large (>100KB)', 'prorank-seo')}
0 ? '#fee2e2' : '#f0fdf4', borderRadius: '8px' }}>
0 ? '#dc2626' : '#16a34a' }}> {autoloadAnalysis.suspicious_options.length}
{__('Suspicious', 'prorank-seo')}
{/* Autoload Guard status */} {autoloadGuard && (
{__('Autoload Guard', 'prorank-seo')}
{autoloadGuard.last_run ? `${__('Last run', 'prorank-seo')}: ${autoloadGuard.last_run}` : __('Runs daily using WordPress core autoload controls to disable oversized entries safely.', 'prorank-seo')}
{__('Threshold', 'prorank-seo')}: {autoloadGuard.threshold_kb} KB • {__('Auto-disabled', 'prorank-seo')}: {autoloadGuard.disabled_count}
{autoloadGuard.disabled && autoloadGuard.disabled.length > 0 && (
{__('Recent fixes:', 'prorank-seo')} {autoloadGuard.disabled.slice(0, 3).map(item => item.name).join(', ')}
)}
)} {/* Recommendations */} {autoloadAnalysis.recommendations.length > 0 && (
{__('Recommendations:', 'prorank-seo')}
    {autoloadAnalysis.recommendations.map((rec, idx) => (
  • {rec}
  • ))}
)} {/* Large Options List */} {autoloadAnalysis.large_options.length > 0 && (

{__('Large Autoloaded Options', 'prorank-seo')}

{autoloadAnalysis.large_options.slice(0, 5).map((option, idx) => (
{option.name}
{formatBytes(option.size * 1024)}
toggleAutoload(option.name, true)} disabled={togglingAutoload === option.name} style={{ marginLeft: '12px', fontSize: '12px', padding: '4px 8px' }} > {togglingAutoload === option.name ? ( ) : ( __('Disable Autoload', 'prorank-seo') )}
))}
{autoloadAnalysis.large_options.length > 5 && (

{__('And', 'prorank-seo')} {autoloadAnalysis.large_options.length - 5} {__('more...', 'prorank-seo')}

)}
)} {/* Suspicious Options List */} {autoloadAnalysis.suspicious_options.length > 0 && (

{__('Suspicious Autoloaded Options', 'prorank-seo')}

{autoloadAnalysis.suspicious_options.slice(0, 5).map((option, idx) => (
{option.name}
{option.reason || __('Should not be autoloaded', 'prorank-seo')}
toggleAutoload(option.name, true)} disabled={togglingAutoload === option.name} style={{ marginLeft: '12px', fontSize: '12px', padding: '4px 8px' }} > {togglingAutoload === option.name ? ( ) : ( __('Disable', 'prorank-seo') )}
))}
)} {/* Refresh Button */}
{__('Refresh Analysis', 'prorank-seo')}
) : (
{__('Autoload analysis is not available. Please check that the database optimization module is enabled.', 'prorank-seo')}
)}
{/* Database Cleanup Card */}

{__('Database Cleanup', 'prorank-seo')}

{__('Automatic Cleanup', 'prorank-seo')} {__('Schedule regular cleanup of the selected WordPress data types', 'prorank-seo')}
updateSetting('auto_cleanup_enabled', value)} label="" />
{settings.auto_cleanup_enabled && (
updateSetting('cleanup_schedule', value)} />
)}

{__('Cleanup Items', 'prorank-seo')}

{__('Run only the selected cleanup tasks. Any WordPress tables that change are optimized automatically after deletion.', 'prorank-seo')}

{__('Comment cleanup follows native WordPress moderation states. Spam comments and pending / unapproved comments are separate cleanup tasks.', 'prorank-seo')}
{__('Post Revisions', 'prorank-seo')} {__('Remove old post revisions', 'prorank-seo')}
updateCleanupItem('revisions', value)} label="" />
{__('Auto-Drafts', 'prorank-seo')} {__('Remove auto-saved drafts', 'prorank-seo')}
updateCleanupItem('drafts', value)} label="" />
{__('Trash', 'prorank-seo')} {__('Empty trash for posts, pages, and comments', 'prorank-seo')}
updateCleanupItem('trash', value)} label="" />
{__('Comments Marked as Spam', 'prorank-seo')} {__('Delete comments currently marked as spam by WordPress', 'prorank-seo')}
updateCleanupItem('spam_comments', value)} label="" />
{__('Pending / Unapproved Comments', 'prorank-seo')} {__('Delete comments waiting for moderation approval', 'prorank-seo')}
updateCleanupItem('unapproved_comments', value)} label="" />
{__('Expired Transients', 'prorank-seo')} {__('Remove expired temporary data', 'prorank-seo')}
updateCleanupItem('expired_transients', value)} label="" />
{__('All Transients', 'prorank-seo')} {__('Remove all temporary data', 'prorank-seo')}
updateCleanupItem('transients', value)} label="" />
{__('Orphaned Meta', 'prorank-seo')} {__('Clean up unused metadata and broken term relationships', 'prorank-seo')}
updateCleanupItem('orphaned_meta', value)} label="" />
{__('Action Scheduler History', 'prorank-seo')} {__('Delete completed, failed, and canceled Action Scheduler jobs plus their logs after the retention window.', 'prorank-seo')}
updateCleanupItem('action_scheduler', value)} label="" />
{settings.cleanup_items.action_scheduler && (
updateSetting('action_scheduler_retention_days', Math.max(1, Math.min(365, parseInt(event.target.value || '7', 10) || 7)))} style={{ width: '120px' }} />
)}
{__('WP Mail SMTP Email Logs', 'prorank-seo')} {__('Delete WP Mail SMTP email log rows after the retention window.', 'prorank-seo')}
updateCleanupItem('wpmailsmtp_logs', value)} label="" />
{settings.cleanup_items.wpmailsmtp_logs && (
updateSetting('wpmailsmtp_log_retention_days', Math.max(1, Math.min(365, parseInt(event.target.value || '30', 10) || 30)))} style={{ width: '120px' }} />
)}
{optimizing ? __('Running Cleanup...', 'prorank-seo') : __('Run Selected Cleanup', 'prorank-seo')} {selectedCleanupCount === 0 && ( {__('Select at least one cleanup item first.', 'prorank-seo')} )}
{lastCleanupResult && (
0 ? '#f0fdf4' : '#f8fafc', borderRadius: '8px', border: `1px solid ${(lastCleanupResult.cleaned ?? 0) > 0 ? '#bbf7d0' : '#e2e8f0'}` }}> {lastCleanupResult.message && (
{lastCleanupResult.message}
)}
{lastCleanupResult.cleaned ?? 0}
{__('Rows deleted', 'prorank-seo')}
{lastCleanupResult.tasks_run ?? 0}
{__('Tasks run', 'prorank-seo')}
{lastCleanupResult.tables_optimized ?? 0}
{__('Tables optimized', 'prorank-seo')}
{lastCleanupResult.cleanup_results && (
{Object.entries(lastCleanupResult.cleanup_results).map(([type, result]) => (
{getCleanupTaskStatus(result).icon} {getCleanupTaskLabel(type)} {result.deleted > 0 ? `${result.deleted} ${__('rows deleted', 'prorank-seo')}${(result.tables?.length || 0) > 1 ? ` ${__('across', 'prorank-seo')} ${result.tables?.length} ${__('tables', 'prorank-seo')}` : ''}` : result.partial_success ? __('Some tables failed to clean', 'prorank-seo') : __('No matching records', 'prorank-seo')}
{result.tables && result.tables.length > 0 && (
{result.tables.map((tableResult) => (
{tableResult.table} {': '} {tableResult.error ? tableResult.error : tableResult.deleted > 0 ? `${tableResult.deleted} ${__('rows', 'prorank-seo')}${tableResult.optimized ? ` • ${__('optimized', 'prorank-seo')}` : ''}` : __('No matching records', 'prorank-seo')}
))}
)} {result.error && !result.tables?.some((tableResult) => tableResult.error === result.error) && (
{result.error}
)}
))}
)}
)}
{/* Table Management Card */}

{__('Table Management', 'prorank-seo')}

{tablesLoading ? (
{__('Loading tables...', 'prorank-seo')}
) : tablesInfo.length === 0 ? (

{__('No tables found.', 'prorank-seo')}

) : ( <>

{__('Use Optimize to reclaim table overhead. InnoDB conversion is only offered for legacy disk-based engines such as MyISAM or Aria.', 'prorank-seo')}

{__('Total tables:', 'prorank-seo')} {tablesInfo.length} | {__('Total size:', 'prorank-seo')} {tablesInfo.reduce((sum, t) => sum + t.total_size, 0).toFixed(2)} MB

{tablesInfo.map((table) => ( ))}
{__('Table', 'prorank-seo')} {__('Rows', 'prorank-seo')} {__('Size', 'prorank-seo')} {__('Engine', 'prorank-seo')} {__('Overhead', 'prorank-seo')} {__('Actions', 'prorank-seo')}
{table.name} {table.rows.toLocaleString()} {table.total_size.toFixed(2)} MB {table.engine} {table.overhead > 0 ? ( {table.overhead.toFixed(2)} MB ) : ( - )}
{table.can_optimize && ( )} {table.can_repair && ( )} {table.can_convert_to_innodb && ( )} {!table.can_optimize && !table.can_repair && !table.can_convert_to_innodb && ( {table.engine === 'InnoDB' ? __('Healthy', 'prorank-seo') : __('Keep Engine', 'prorank-seo')} )}
)}
{/* Cron Job Manager Card */}

{__('Cron Job Manager', 'prorank-seo')}

{__('Only orphaned cron events can be deleted here. Active jobs stay read-only so you can inspect schedules without removing live automations.', 'prorank-seo')}

{cronLoading ? (
{__('Loading cron jobs...', 'prorank-seo')}
) : cronJobs.length === 0 ? (

{__('No scheduled cron jobs found.', 'prorank-seo')}

) : ( <>

{__('Scheduled tasks:', 'prorank-seo')} {cronJobs.length} {cronJobs.filter(c => c.is_orphaned).length > 0 && ( | {__('Orphaned:', 'prorank-seo')} {cronJobs.filter(c => c.is_orphaned).length} )}

{cronJobs.map((cron, idx) => ( ))}
{__('Hook', 'prorank-seo')} {__('Schedule', 'prorank-seo')} {__('Next Run', 'prorank-seo')} {__('Status', 'prorank-seo')} {__('Actions', 'prorank-seo')}
{cron.hook} {cron.schedule || __('Single', 'prorank-seo')} {cron.next_run} {cron.is_orphaned ? ( {__('Orphaned', 'prorank-seo')} ) : ( {__('Active', 'prorank-seo')} )} {cron.can_delete && ( )}
)}
{/* Database Backup Card */}

{__('Database Backup', 'prorank-seo')}

{__('Create local SQL snapshots stored in a secure, non-public folder inside wp-content. Restoring a backup overwrites the current database, and ProRank keeps the 10 most recent backups automatically.', 'prorank-seo')}

{backupsLoading ? (
{__('Loading backups...', 'prorank-seo')}
) : backups.length === 0 ? (
{__('No backups found. Click "Create Backup" to create your first backup.', 'prorank-seo')}
) : (
{backups.map((backup) => ( ))}
{__('Filename', 'prorank-seo')} {__('Size', 'prorank-seo')} {__('Created', 'prorank-seo')} {__('Actions', 'prorank-seo')}
{backup.filename} {backup.size_formatted || formatBytes(backup.size || 0)} {backup.created || backup.date || ''}
)}
{/* Server Info Card - Redesigned */}

{__('Server Information', 'prorank-seo')}

{/* PHP Section */}
{__('PHP Environment', 'prorank-seo')}
{__('Version', 'prorank-seo')} {serverInfo?.php_version || 'Unknown'}
{__('Memory Limit', 'prorank-seo')} {serverInfo?.memory_limit || 'Unknown'}
{__('Max Execution', 'prorank-seo')} {serverInfo?.max_execution_time || 'Unknown'}
{__('Upload Limit', 'prorank-seo')} {serverInfo?.upload_max_filesize || 'Unknown'}
{/* Database Section */}
{__('Database', 'prorank-seo')}
{__('Engine', 'prorank-seo')} {serverInfo?.mysql_type || 'MySQL'}
{__('Version', 'prorank-seo')} {serverInfo?.mysql_version ? serverInfo.mysql_version.split('-')[0] : 'Unknown'}
{__('Tables', 'prorank-seo')} {databaseStats?.table_count || '0'}
{__('Total Size', 'prorank-seo')} {databaseStats?.total_size || '0 MB'}
{/* Cache Section */}
{__('Caching', 'prorank-seo')}
{__('OPcache', 'prorank-seo')} {serverInfo?.opcache_enabled ? __('Enabled', 'prorank-seo') : __('Disabled', 'prorank-seo')}
{__('Object Cache', 'prorank-seo')} {objectCacheBadgeLabel}

{objectCacheHelpText}

{/* Server Optimization Card */}

{__('Server Optimization', 'prorank-seo')}

{__('Heartbeat Control is the active server-side optimization on this screen. It reduces admin AJAX churn without relying on host-specific behavior.', 'prorank-seo')}

{__('Heartbeat Control', 'prorank-seo')} {__('Reduce admin-ajax.php requests', 'prorank-seo')}
updateSetting('heartbeat_control', value)} label="" />
{settings.heartbeat_control && (
updateSetting('heartbeat_frequency', parseInt(value, 10) || 60)} /> updateSetting('heartbeat_frequency_front', parseInt(value, 10) || 0)} /> updateSetting('heartbeat_frequency_admin', parseInt(value, 10) || 0)} /> updateSetting('heartbeat_frequency_editor', parseInt(value, 10) || 60)} />
)}
{/* Save Button */}
{saving ? __('Saving...', 'prorank-seo') : __('Save Settings', 'prorank-seo')}
); }; export default DatabaseServerSettings;