<?php
require_once dirname(__DIR__,2).'/core/lib/database_backup.php';
/**
 * 芋头CMS
 * ============================================================================
 * 版权所有 2016-2028 沅陵县乌托邦网络服务有限公司，并保留所有权利。
 * 网站地址: http://www.yutoucms.cn
 * ----------------------------------------------------------------------------
 * 如果商业用途务必到官方购买正版授权, 以免引起不必要的法律纠纷.
 * ============================================================================
 * Author: 芋头 <mxgsf@qq.com>
 * Date: 2026-5-20
 */
$action = isset($_GET['action']) ? $_GET['action'] : 'list';
$message = '';
$error = '';

$backupDir = cmsBackupDirectory(dirname(__DIR__,2)) . '/';
if (!is_dir($backupDir)) {
    @mkdir($backupDir, 0755, true);
}

$licenseInfo = $license->getStatus();
$currentLevel = isset($licenseInfo['level']) ? (int) $licenseInfo['level'] : 1;
$canBackup = $currentLevel >= 2;
$backupWriteError = '备份文件写入失败，请检查 backup 目录权限';
$writeActions=['backup','restore','delete_backup','cleanup_backups','optimize','repair','save_auto_backup'];
if(in_array($action,$writeActions,true)&&$_SERVER['REQUEST_METHOD']!=='POST'){http_response_code(405);$error='数据库变更操作只允许 POST 请求';$action='list';}

if ($action === 'backup') {
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); $error='备份操作只允许 POST 请求'; $action='list'; }
    else { requireAdminCsrfToken(); }
}

if ($action === 'backup' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $config = $db->getConfig();
    $dbName = $config['database'];
    $filename = $dbName . '_' . date('Ymd_His') . '_' . strtoupper(substr(md5(uniqid('', true)), 0, 6)) . '.sql';
    $filepath = $backupDir . $filename;
    $dumpErrorFile = $backupDir . pathinfo($filename, PATHINFO_FILENAME) . '.log';
    $backupSucceeded = false;

    @unlink($filepath);
    @unlink($dumpErrorFile);

    $cmd = sprintf(
        'mysqldump --host=%s --port=%s --user=%s --password=%s %s --single-transaction --hex-blob --skip-triggers > %s 2> %s',
        escapeshellarg($config['host']),
        escapeshellarg($config['port']),
        escapeshellarg($config['username']),
        escapeshellarg($config['password']),
        escapeshellarg($dbName),
        escapeshellarg($filepath),
        escapeshellarg($dumpErrorFile)
    );

    $dumpOutput = [];
    $dumpExitCode = 1;
    @exec($cmd, $dumpOutput, $dumpExitCode);
    if ($dumpExitCode === 0 && is_file($filepath) && filesize($filepath) > 100) {
        $backupSucceeded = true;
    }

    if (!$backupSucceeded) {
        @unlink($filepath);

        $handle = @fopen($filepath, 'wb');
        if ($handle === false) {
            $error = $backupWriteError;
        } else {
            $writeChunk = function ($content) use ($handle) {
                return fwrite($handle, $content) !== false;
            };
            $exportValue = function ($value) {
                if ($value === null) {
                    return 'NULL';
                }
                if (is_bool($value)) {
                    return $value ? "'1'" : "'0'";
                }
                return "'" . str_replace(
                    ["\0", "\r", "\n", "\t", "\x1a"],
                    ["\\0", "\\r", "\\n", "\\t", "\\Z"],
                    addslashes((string) $value)
                ) . "'";
            };

            $manualBackupSuccess = $writeChunk("-- ECMS Database Backup\n")
                && $writeChunk('-- Date: ' . date('Y-m-d H:i:s') . "\n")
                && $writeChunk("-- Database: {$dbName}\n\n")
                && $writeChunk("SET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n");

            try {
                if ($manualBackupSuccess) {
                    $tables = $db->fetchAll("SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'");
                    foreach ($tables as $tableRow) {
                        $tableName = array_values((array) $tableRow)[0];
                        $createRow = $db->fetch("SHOW CREATE TABLE `{$tableName}`");
                        $createSql = array_values((array) $createRow)[1];

                        $manualBackupSuccess = $writeChunk("-- Table: {$tableName}\n")
                            && $writeChunk("DROP TABLE IF EXISTS `{$tableName}`;\n")
                            && $writeChunk($createSql . ";\n\n");
                        if (!$manualBackupSuccess) {
                            break;
                        }

                        $stmt = $db->query("SELECT * FROM `{$tableName}`");
                        while (($row = $stmt->fetch()) !== false) {
                            $fields = array_keys($row);
                            $values = [];
                            foreach (array_values($row) as $value) {
                                $values[] = $exportValue($value);
                            }

                            $insertSql = sprintf(
                                "INSERT INTO `%s` (`%s`) VALUES (%s);\n",
                                $tableName,
                                implode('`,`', $fields),
                                implode(',', $values)
                            );

                            if (!$writeChunk($insertSql)) {
                                $manualBackupSuccess = false;
                                break;
                            }
                        }

                        if (!$manualBackupSuccess || !$writeChunk("\n")) {
                            $manualBackupSuccess = false;
                            break;
                        }
                    }
                }

                if ($manualBackupSuccess) {
                    $manualBackupSuccess = $writeChunk("SET FOREIGN_KEY_CHECKS = 1;\n");
                }
            } catch (Throwable $e) {
                $manualBackupSuccess = false;
            }

            fclose($handle);

            if ($manualBackupSuccess && is_file($filepath) && filesize($filepath) > 0) {
                $backupSucceeded = true;
            } else {
                @unlink($filepath);
                if ($error === '') {
                    $error = $backupWriteError;
                }
            }
        }
    }

    @unlink($dumpErrorFile);

    if ($backupSucceeded) {
        cmsWriteBackupManifest($filepath, 'manual');
        $message = "备份成功：{$filename}";
        addLog('备份', '数据库', "备份数据库：{$dbName}");
    } elseif ($error === '') {
        $error = '数据库备份失败，请检查 mysqldump 环境或 backup 目录权限';
    }

    $action = 'list';
}

if ($action === 'restore') {
    requireAdminCsrfToken();
    if (!isset($_POST['backup_file']) || empty($_POST['backup_file'])) {
        $error = '请选择要恢复的备份文件';
    } else {
        $filename = basename($_POST['backup_file']);
        $filepath = $backupDir . $filename;
        $preflight=cmsBackupPreflight($backupDir,$filename);
        if(!$preflight['success']){$error='恢复预检失败：'.$preflight['message'];}
        elseif(empty($_POST['confirm_restore'])){$error='请勾选恢复确认后再执行';}
        else{$snapshot=cmsCreateDatabaseSnapshot($db,$backupDir,'pre_restore');if(!$snapshot['success']){$error='恢复前自动快照失败，已停止恢复';}else{$restore=cmsRestoreDatabaseBackup($db,$preflight);if($restore['success']){$message="恢复成功：{$filename}；恢复前快照：{$snapshot['filename']}";addLog('恢复','数据库',"恢复数据库：{$filename} | 快照：{$snapshot['filename']}");}else{$error=$restore['message'].'；恢复前快照：'.$snapshot['filename'];}}}
    }
    $action = 'list';
}

if ($action === 'delete_backup') {
    requireAdminCsrfToken();
    $file = cmsBackupSafeFilename($_POST['file'] ?? '');
    $filepath = $backupDir . $file;
    if ($file && is_file($filepath)) {
        @unlink($filepath); @unlink($filepath.'.meta.json');
        $message = "已删除：{$file}";
        addLog('删除', '数据库', "删除备份文件：{$file}");
    }
    $action = 'list';
}

if ($action === 'download_backup') {
    $file=cmsBackupSafeFilename($_GET['file']??'');$preflight=cmsBackupPreflight($backupDir,$file);if(!$preflight['success']){http_response_code(404);echo'备份文件不存在或校验失败';exit;}while(ob_get_level()>0)ob_end_clean();header('Content-Type: application/sql');header('Content-Length: '.$preflight['size']);header('Content-Disposition: attachment; filename="'.$file.'"');addLog('下载','数据库','下载数据库备份：'.$file.' | SHA256:'.$preflight['sha256']);readfile($preflight['path']);exit;
}

if ($action === 'preflight_backup') {
    $file=cmsBackupSafeFilename($_GET['file']??'');$result=cmsBackupPreflight($backupDir,$file);unset($result['statements'],$result['path']);while(ob_get_level()>0)ob_end_clean();header('Content-Type: application/json; charset=utf-8');echo json_encode(['code'=>$result['success']?200:422,'data'=>$result,'msg'=>$result['message']],JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);exit;
}

if ($action === 'cleanup_backups') {
    requireAdminCsrfToken();$keep=max(1,intval($_POST['keep']??10));$deleted=cmsCleanupDatabaseBackups($backupDir,$keep);$message='已清理 '.count($deleted).' 个过期备份';addLog('清理','数据库',$message.' | 保留 '.$keep.' 个');$action='list';
}

if ($action === 'optimize') {
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); $error='优化操作只允许 POST 请求'; }
    else {
        requireAdminCsrfToken();$table = cmsDatabaseResolveMaintenanceTable($db, $_POST['table'] ?? '');
        if ($table === '') { $error = '数据表不存在或名称不合法'; }
        else {
        try {
            $db->query("OPTIMIZE TABLE `{$table}`");
            $message = "优化完成：{$table}";
            addLog('优化', '数据库', "优化数据表：{$table}");
        } catch (Exception $e) {
            $error = '数据表优化失败';
        }
        }
    }
    $action = 'list';
}

if ($action === 'repair') {
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); $error='修复操作只允许 POST 请求'; }
    else {
        requireAdminCsrfToken();$table = cmsDatabaseResolveMaintenanceTable($db, $_POST['table'] ?? '');
        if ($table === '') { $error = '数据表不存在或名称不合法'; }
        else {
        try {
            $db->query("REPAIR TABLE `{$table}`");
            $message = "修复完成：{$table}";
            addLog('修复', '数据库', "修复数据表：{$table}");
        } catch (Exception $e) {
            $error = '数据表修复失败';
        }
        }
    }
    $action = 'list';
}

if ($action === 'save_auto_backup') {
    requireAdminCsrfToken();
    if (!$canBackup) {
        $error = '权限不足，自动备份需要基础版及以上版本';
    } else {
        $enabled = isset($_POST['enabled']) ? 1 : 0;
        $interval = trim(isset($_POST['interval']) ? $_POST['interval'] : 'daily');
        $maxCount = max(1, intval(isset($_POST['max_count']) ? $_POST['max_count'] : 10));
        $saveConfig = function ($key, $value) use ($db) {
            $row = $db->fetch("SELECT id FROM {$db->table('config')} WHERE config_key = ?", [$key]);
            if ($row) {
                $db->update('config', ['config_value' => $value], 'config_key = ?', [$key]);
            } else {
                $db->insert('config', [
                    'config_key' => $key,
                    'config_value' => $value,
                    'config_type' => 'text',
                    'config_group' => 'backup',
                ]);
            }
        };

        $saveConfig('auto_backup_enabled', $enabled);
        $saveConfig('auto_backup_interval', $interval);
        $saveConfig('auto_backup_max_count', $maxCount);

        $message = '自动备份设置已保存';
        addLog('设置', '数据库', '修改自动备份设置：' . ($enabled ? '启用' : '禁用') . " | 周期:{$interval} | 保留:{$maxCount}份");
    }
    $action = 'list';
}

if ($action === 'list') {
    $dbName = $db->getConfig()['database'];
    $tables = $db->fetchAll("SHOW TABLE STATUS FROM `{$dbName}`");
    $dbSizeInfo = $db->fetch(
        'SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS size FROM information_schema.tables WHERE table_schema = ?',
        [$dbName]
    );
    $dbSize = isset($dbSizeInfo['size']) ? $dbSizeInfo['size'] : 0;

    $backups = cmsListDatabaseBackups($backupDir);
    foreach($backups as&$backupItem){$check=cmsBackupPreflight($backupDir,$backupItem['name']);$backupItem['restorable']=$check['success'];$backupItem['statement_count']=intval($check['statement_count']??0);$backupItem['preflight_message']=$check['message']??'';}unset($backupItem);
    $totalBackupSize = array_sum(array_column($backups, 'size'));

    $autoEnabled = $db->fetch("SELECT config_value FROM {$db->table('config')} WHERE config_key = 'auto_backup_enabled'");
    $autoEnabled = isset($autoEnabled['config_value']) ? $autoEnabled['config_value'] : '0';
    $autoInterval = $db->fetch("SELECT config_value FROM {$db->table('config')} WHERE config_key = 'auto_backup_interval'");
    $autoInterval = isset($autoInterval['config_value']) ? $autoInterval['config_value'] : 'daily';
    $autoMaxCount = $db->fetch("SELECT config_value FROM {$db->table('config')} WHERE config_key = 'auto_backup_max_count'");
    $autoMaxCount = isset($autoMaxCount['config_value']) ? $autoMaxCount['config_value'] : '10';
    ?>
    <div class="page-container fade-in">
        <div class="page-header">
            <div class="page-title">
                <div class="page-icon"><svg style="width:22px;height:22px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg></div>
                <div>
                    <h1>数据库管理</h1>
                    <p style="font-size:12px;color:var(--gray-500);margin:0;">数据表管理与备份恢复</p>
                </div>
            </div>
        </div>

        <?php if ($error || $message): ?><script>window.addEventListener('load',function(){CMSAdminUI.toast(<?php echo json_encode($error?:$message,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); ?>,<?php echo json_encode($error?'error':'success'); ?>);});</script><?php endif; ?>

        <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px;margin-bottom:20px;">
            <div class="content-card" style="padding:18px 20px;">
                <div style="font-size:12px;color:var(--gray-500);margin-bottom:8px;">数据库大小</div>
                <div style="font-size:24px;font-weight:700;color:var(--gray-900);"><?php echo $dbSize; ?> <span style="font-size:14px;font-weight:600;color:var(--gray-500);">MB</span></div>
            </div>
            <div class="content-card" style="padding:18px 20px;">
                <div style="font-size:12px;color:var(--gray-500);margin-bottom:8px;">数据表数量</div>
                <div style="font-size:24px;font-weight:700;color:var(--gray-900);"><?php echo count($tables); ?> <span style="font-size:14px;font-weight:600;color:var(--gray-500);">张</span></div>
            </div>
            <div class="content-card" style="padding:18px 20px;">
                <div style="font-size:12px;color:var(--gray-500);margin-bottom:8px;">备份文件</div>
                <div style="font-size:24px;font-weight:700;color:var(--gray-900);"><?php echo count($backups); ?> <span style="font-size:14px;font-weight:600;color:var(--gray-500);">个</span></div>
            </div>
            <div class="content-card" style="padding:18px 20px;">
                <div style="font-size:12px;color:var(--gray-500);margin-bottom:8px;">备份总大小</div>
                <div style="font-size:24px;font-weight:700;color:var(--gray-900);"><?php echo round($totalBackupSize / 1024 / 1024, 2); ?> <span style="font-size:14px;font-weight:600;color:var(--gray-500);">MB</span></div>
            </div>
        </div>

        <div style="background:#fff;border:1px solid var(--border-light);border-radius:12px;padding:20px;margin-bottom:20px;">
            <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;flex-wrap:wrap;gap:12px;">
                <div style="font-size:14px;font-weight:600;color:var(--gray-800);display:flex;align-items:center;gap:8px;">
                    <svg style="width:16px;height:16px;color:#2563eb;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg> 备份管理
                </div>
                <div style="display:flex;gap:10px;">
                    <form method="post" action="?act=database&action=backup" style="margin:0"><input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars(getAdminCsrfToken()); ?>"><button class="btn btn-primary" type="submit" data-cms-confirm="将立即生成完整数据库备份，执行期间请勿关闭页面。" data-cms-title="创建数据库备份" data-cms-confirm-text="开始备份" data-cms-loading="备份中"><svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> 手动备份</button></form>
                    <form method="post" action="?act=database&action=cleanup_backups" style="margin:0;display:flex;gap:6px"><input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars(getAdminCsrfToken()); ?>"><input type="hidden" name="keep" value="<?php echo intval($autoMaxCount ?: 10); ?>"><button class="btn btn-secondary" type="submit" data-cms-confirm="将按保留策略删除旧备份；恢复前快照不会自动删除。" data-cms-title="清理旧备份" data-cms-tone="danger" data-cms-confirm-text="确认清理" data-cms-loading="清理中">清理旧备份</button></form>
                    <?php if ($canBackup): ?>
                    <button class="btn btn-secondary" onclick="toggleAutoBackup()"><svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg> 自动备份</button>
                    <?php else: ?>
                    <button class="btn btn-secondary" disabled title="需要基础版或专业版授权"><svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg> 自动备份（需授权）</button>
                    <?php endif; ?>
                </div>
            </div>

            <div id="autoBackupPanel" style="display:none;border-top:1px solid var(--border-light);padding-top:16px;margin-top:4px;">
                <form method="post" action="?act=database&action=save_auto_backup">
                    <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars(getAdminCsrfToken()); ?>">
                    <div style="display:grid;grid-template-columns:1fr 1fr 1fr auto;gap:14px;align-items:end;flex-wrap:wrap;">
                        <div>
                            <label style="display:block;font-size:12px;color:var(--gray-500);margin-bottom:6px;">自动备份</label>
                            <label style="display:flex;align-items:center;gap:8px;cursor:pointer;">
                                <input type="checkbox" name="enabled" value="1" id="autoEnabledCheck" <?php echo $autoEnabled == '1' ? 'checked' : ''; ?>>
                                <span id="autoStatusLabel" style="font-size:13px;color:<?php echo $autoEnabled == '1' ? 'var(--success)' : 'var(--gray-400)'; ?>;"><?php echo $autoEnabled == '1' ? '已启用' : '未启用'; ?></span>
                            </label>
                        </div>
                        <div>
                            <label style="display:block;font-size:12px;color:var(--gray-500);margin-bottom:6px;">备份周期</label>
                            <select name="interval" class="form-control" style="width:100%;">
                                <option value="hourly" <?php echo $autoInterval == 'hourly' ? 'selected' : ''; ?>>每小时</option>
                                <option value="daily" <?php echo $autoInterval == 'daily' ? 'selected' : ''; ?>>每天</option>
                                <option value="weekly" <?php echo $autoInterval == 'weekly' ? 'selected' : ''; ?>>每周</option>
                                <option value="monthly" <?php echo $autoInterval == 'monthly' ? 'selected' : ''; ?>>每月</option>
                            </select>
                        </div>
                        <div>
                            <label style="display:block;font-size:12px;color:var(--gray-500);margin-bottom:6px;">保留份数</label>
                            <select name="max_count" class="form-control" style="width:100%;">
                                <?php foreach ([5, 10, 20, 30] as $count): ?>
                                <option value="<?php echo $count; ?>" <?php echo $autoMaxCount == $count ? 'selected' : ''; ?>><?php echo $count; ?> 份</option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div>
                            <button type="submit" class="btn btn-success" style="white-space:nowrap;"><svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg> 保存设置</button>
                        </div>
                    </div>
                    <div style="margin-top:10px;font-size:11px;color:var(--gray-400);">说明：保存后需让服务器每分钟执行 <code>php cli/scheduler.php</code>，安装方式见 <code>cli/README.md</code>。</div>
                </form>
            </div>
        </div>

        <?php if (!empty($backups)): ?>
        <div class="content-card" style="margin-bottom:20px;">
            <div style="padding:16px 20px;border-bottom:1px solid var(--border-light);">
                <div style="font-size:14px;font-weight:600;color:var(--gray-800);display:flex;align-items:center;gap:8px;">
                    <svg style="width:22px;height:22px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="21 8 21 21 3 21 3 8"/><rect x="1" y="3" width="22" height="5"/><line x1="10" y1="12" x2="14" y2="12"/></svg> 备份记录
                </div>
            </div>
            <div style="padding:16px 20px;">
                <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;">
                    <?php foreach ($backups as $backupFile): ?>
                    <div style="border:1px solid var(--border-light);border-radius:8px;padding:14px 16px;display:flex;flex-direction:column;gap:10px;transition:all 0.15s;" class="hover-lift">
                        <div style="display:flex;align-items:center;gap:10px;">
                            <div style="width:40px;height:40px;background:#fdf4ff;border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0;">
                                <svg style="width:20px;height:20px;color:#9333ea;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
                            </div>
                            <div style="flex:1;min-width:0;">
                                <div style="font-size:13px;font-weight:500;color:var(--gray-800);word-break:break-all;" title="<?php echo htmlspecialchars($backupFile['name']); ?>"><?php echo htmlspecialchars($backupFile['name']); ?></div>
                                <div style="font-size:11px;color:var(--gray-500);margin-top:2px;">
                                    <?php echo round($backupFile['size'] / 1024, 1); ?> KB · <?php echo date('Y-m-d H:i', $backupFile['time']); ?> · <span style="color:<?php echo $backupFile['integrity']==='verified'?'#047857':($backupFile['integrity']==='failed'?'#b91c1c':'#b45309'); ?>"><?php echo $backupFile['integrity']==='verified'?'完整性已验证':($backupFile['integrity']==='failed'?'校验失败':'旧备份未登记'); ?></span>
                                </div>
                            </div>
                        </div>
                        <div style="display:flex;gap:8px;">
                            <button type="button" class="btn btn-sm btn-secondary" style="flex:1" onclick="preflightBackup('<?php echo htmlspecialchars($backupFile['name'],ENT_QUOTES,'UTF-8'); ?>')"><i class="fa fa-shield"></i> 预检</button>
                            <form method="post" action="?act=database&action=restore" style="flex:1;">
                                <input type="hidden" name="backup_file" value="<?php echo htmlspecialchars($backupFile['name']); ?>">
                                <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars(getAdminCsrfToken()); ?>">
                                <label style="display:none"><input type="checkbox" name="confirm_restore" value="1" checked required> 确认恢复</label>
                                <button type="submit" class="btn btn-sm btn-secondary" style="width:100%;" data-cms-confirm="恢复会覆盖当前数据库，系统将先自动创建恢复前快照。" data-cms-title="恢复数据库" data-cms-tone="danger" data-cms-confirm-text="确认恢复" data-cms-loading="恢复中" <?php echo empty($backupFile['restorable'])?'disabled title="预检未通过，禁止恢复"':''; ?>><svg style="width:12px;height:12px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg> 恢复</button>
                            </form>
                            <a href="?act=database&action=download_backup&file=<?php echo rawurlencode($backupFile['name']); ?>" class="btn btn-sm btn-primary" style="flex:1;text-align:center;"><svg style="width:12px;height:12px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> 下载</a>
                            <form method="post" action="?act=database&action=delete_backup" style="margin:0"><input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars(getAdminCsrfToken()); ?>"><input type="hidden" name="file" value="<?php echo htmlspecialchars($backupFile['name']); ?>"><button type="submit" class="btn btn-sm btn-danger" data-cms-confirm="备份文件及其完整性清单将永久删除，无法恢复。" data-cms-title="删除备份" data-cms-tone="danger" data-cms-confirm-text="永久删除" data-cms-loading="删除中"><svg style="width:12px;height:12px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg> 删除</button></form>
                        </div>
                    </div>
                    <?php endforeach; ?>
                </div>
            </div>
        </div>
        <?php endif; ?>

        <div class="content-card">
            <div style="padding:16px 20px;border-bottom:1px solid var(--border-light);">
                <div style="font-size:14px;font-weight:600;color:var(--gray-800);display:flex;align-items:center;gap:8px;">
                    <svg style="width:16px;height:16px;color:#2563eb;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 3H5a2 2 0 0 0-2 2v4m6-6h10a2 2 0 0 1 2 2v4M9 3v18m0 0h10a2 2 0 0 0 2-2v-4M9 21H5a2 2 0 0 1-2-2v-4m0-6v6"/></svg> 数据表
                    <span style="font-weight:400;font-size:12px;color:var(--gray-500);margin-left:auto;"><?php echo count($tables); ?> 张表</span>
                </div>
            </div>
            <?php if (empty($tables)): ?>
            <div class="empty-state">
                <div class="empty-icon"><svg style="width:22px;height:22px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg></div>
                <h3>暂无数据表</h3>
                <p>数据库连接可能异常</p>
            </div>
            <?php else: ?>
            <div style="overflow-x:auto;">
                <table class="data-table" style="min-width:700px;">
                    <thead>
                        <tr>
                            <th class="id-cell" style="width:50px;">#</th>
                            <th>数据表</th>
                            <th style="width:90px;text-align:right;">行数</th>
                            <th style="width:110px;text-align:right;">大小</th>
                            <th style="width:120px;text-align:center;">编码</th>
                            <th style="width:160px;text-align:center;">操作</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($tables as $index => $table): $canRepair=in_array(strtoupper((string)($table['Engine']??'')),['MYISAM','ARCHIVE','CSV'],true); ?>
                        <tr style="transition:background 0.1s;">
                            <td class="id-cell"><?php echo $index + 1; ?></td>
                            <td><code style="background:#f1f5f9;padding:4px 10px;border-radius:5px;font-size:12px;color:#334155;font-family:monospace;"><?php echo htmlspecialchars($table['Name']); ?></code></td>
                            <td style="text-align:right;font-family:monospace;color:var(--gray-600);"><?php echo number_format(isset($table['Rows']) ? $table['Rows'] : 0); ?></td>
                            <td style="text-align:right;font-family:monospace;color:var(--gray-600);"><?php echo round((isset($table['Data_length']) ? $table['Data_length'] : 0) / 1024, 1); ?> KB</td>
                            <td style="text-align:center;font-size:11px;color:var(--gray-500);"><?php echo isset($table['Collation']) ? $table['Collation'] : '-'; ?></td>
                            <td>
                                <div class="btn-group" style="justify-content:center;">
                                    <form method="post" action="?act=database&action=optimize" style="margin:0"><input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars(getAdminCsrfToken()); ?>"><input type="hidden" name="table" value="<?php echo htmlspecialchars($table['Name']); ?>"><button type="submit" class="btn btn-sm btn-secondary" data-cms-confirm="将优化数据表 <?php echo htmlspecialchars($table['Name'],ENT_QUOTES,'UTF-8'); ?>，执行期间可能短暂占用数据库资源。" data-cms-title="优化数据表" data-cms-confirm-text="开始优化" data-cms-loading="优化中" title="优化"><svg style="width:12px;height:12px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg> 优化</button></form>
                                    <form method="post" action="?act=database&action=repair" style="margin:0"><input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars(getAdminCsrfToken()); ?>"><input type="hidden" name="table" value="<?php echo htmlspecialchars($table['Name']); ?>"><button type="submit" class="btn btn-sm btn-secondary" data-cms-confirm="将尝试修复数据表 <?php echo htmlspecialchars($table['Name'],ENT_QUOTES,'UTF-8'); ?>。建议先确认已有可用备份。" data-cms-title="修复数据表" data-cms-tone="warning" data-cms-confirm-text="开始修复" data-cms-loading="修复中" <?php echo $canRepair?'title="修复"':'disabled title="当前存储引擎不支持 REPAIR TABLE"'; ?>><svg style="width:12px;height:12px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg> 修复</button></form>
                                </div>
                            </td>
                        </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
            <?php endif; ?>
        </div>
    </div>
    <?php
}
?>
<div id="preflightModal" class="db-preflight-modal" hidden><div><button type="button" class="db-preflight-close" onclick="closePreflight()">×</button><small>BACKUP PREFLIGHT</small><h2>备份预检</h2><div id="preflightState"></div><dl><div><dt>文件</dt><dd id="preflightFile">—</dd></div><div><dt>完整性</dt><dd id="preflightIntegrity">—</dd></div><div><dt>SQL 语句</dt><dd id="preflightStatements">—</dd></div><div><dt>文件大小</dt><dd id="preflightSize">—</dd></div></dl><p id="preflightMessage"></p></div></div>
<style>
.btn-group { display:flex; gap:6px; align-items:center; }
.btn-sm { padding:5px 10px; font-size:12px; }
.db-preflight-modal{position:fixed;inset:0;z-index:9999;background:rgba(15,23,42,.48);backdrop-filter:blur(4px)}.db-preflight-modal:not([hidden]){display:flex;align-items:center;justify-content:center;padding:24px}.db-preflight-modal>div{position:relative;width:min(560px,100%);padding:26px;border-radius:20px;background:#fff;box-shadow:0 24px 70px rgba(15,23,42,.22)}.db-preflight-close{position:absolute;right:18px;top:13px;border:0;background:none;color:#64748b;font-size:28px}.db-preflight-modal small{color:#94a3b8;letter-spacing:.13em}.db-preflight-modal h2{margin:7px 0 14px;color:#1e293b}.db-preflight-modal dl{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin:15px 0}.db-preflight-modal dl div{padding:11px;border-radius:10px;background:#f8fafc}.db-preflight-modal dt{color:#94a3b8;font-size:10px}.db-preflight-modal dd{overflow:hidden;margin:5px 0 0;color:#334155;font-size:12px;text-overflow:ellipsis}.db-preflight-modal p{margin:0;color:#64748b;line-height:1.7}.db-preflight-state{display:inline-block;padding:6px 10px;border-radius:9px;background:#ecfdf5;color:#047857;font-size:11px;font-weight:700}.db-preflight-state.is-error{background:#fef2f2;color:#b91c1c}
</style>
<script>
function preflightBackup(file) {
    fetch('?act=database&action=preflight_backup&file=' + encodeURIComponent(file)).then(function (response) { return response.json(); }).then(function (result) {
        var data = result.data || {};
        document.getElementById('preflightFile').textContent=data.filename||file;document.getElementById('preflightIntegrity').textContent=data.integrity==='verified'?'SHA-256 已验证':(data.integrity==='failed'?'校验失败':'旧备份未登记');document.getElementById('preflightStatements').textContent=data.statement_count||0;document.getElementById('preflightSize').textContent=data.size?Math.round(data.size/1024*10)/10+' KB':'—';document.getElementById('preflightMessage').textContent=result.msg||'';var state=document.getElementById('preflightState');state.className='db-preflight-state'+(result.code===200?'':' is-error');state.textContent=result.code===200?'预检通过，可以恢复':'预检未通过，禁止恢复';document.getElementById('preflightModal').hidden=false;
    }).catch(function(){CMSAdminUI.toast('预检请求失败，请稍后重试','error');});
}
function closePreflight(){document.getElementById('preflightModal').hidden=true;}
document.addEventListener('keydown',function(event){if(event.key==='Escape')closePreflight();});
function toggleAutoBackup() {
    var panel = document.getElementById('autoBackupPanel');
    panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
}
var autoEnabledCheck = document.getElementById('autoEnabledCheck');
if (autoEnabledCheck) {
    autoEnabledCheck.addEventListener('change', function () {
        var label = document.getElementById('autoStatusLabel');
        label.textContent = this.checked ? '已启用' : '未启用';
        label.style.color = this.checked ? 'var(--success)' : 'var(--gray-400)';
    });
}
if (typeof lucide !== 'undefined' && lucide.createIcons) {
    if (window.lucide && typeof window.lucide.createIcons === 'function') { window.lucide.createIcons(); }
}
</script>
