diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..e69de29
diff --git a/admin.php b/admin.php
index 64a3c4b..7d77973 100644
--- a/admin.php
+++ b/admin.php
@@ -1,21 +1,16 @@
-
🔐 SecHub 管理后台
+
SecHub 管理后台
= htmlspecialchars($error) ?>
@@ -69,494 +131,1055 @@ if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
exit;
}
-// 处理保存排序
+// 初始化 CSRF token
+if (!isset($_SESSION['csrf_token'])) {
+ $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
+}
+
+// 初始化数据库
+try {
+ $database = new SecHubDatabase(DB_PATH, JSON_DIR);
+ $database->syncJsonToDatabase();
+} catch (Exception $e) {
+ $error = '数据库初始化失败: ' . $e->getMessage();
+}
+
+// 消息提示
+$message = '';
+$messageType = 'success';
+
+// ======================== 处理POST动作 ========================
+
+// CSRF 校验(对非登录请求)
+$csrfOk = true;
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && !isset($_POST['login'])) {
+ $token = $_POST['csrf_token'] ?? '';
+ if ($token !== $_SESSION['csrf_token']) {
+ $csrfOk = false;
+ $message = '安全令牌无效,请刷新页面重试';
+ $messageType = 'error';
+ }
+}
+
+if ($csrfOk) {
+
+// --- 保存排序 ---
if (isset($_POST['save_order']) && isset($_POST['orders'])) {
- $success = false;
- $message = '';
-
try {
- // orders 数组已经按拖拽后的顺序排列
$newOrder = 1;
foreach ($_POST['orders'] as $filename) {
- $filePath = $jsonDir . $filename;
-
+ $filePath = JSON_DIR . $filename;
if (file_exists($filePath)) {
- // 读取JSON文件(保持原始格式)
$content = file_get_contents($filePath);
$data = json_decode($content, true);
-
if (json_last_error() === JSON_ERROR_NONE && is_array($data) && !empty($data)) {
- // 只更新第一个数据项的 no 字段
$data[0]['no'] = $newOrder;
-
- // 写回JSON文件(保持原有格式,不转义)
$newContent = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
file_put_contents($filePath, $newContent . "\n");
-
$newOrder++;
}
}
}
-
- $success = true;
- $message = '排序保存成功!';
-
- // 重新同步数据库
- $database = new SecHubDatabase($dbPath, $jsonDir);
$database->syncJsonToDatabase();
-
+ $message = '排序保存成功!';
+ $messageType = 'success';
} catch (Exception $e) {
$message = '保存失败: ' . $e->getMessage();
+ $messageType = 'error';
}
}
-// 处理删除数据库
+// --- 添加工具 ---
+if (isset($_POST['add_item'])) {
+ $tableName = $_POST['table_name'] ?? '';
+ $name = trim($_POST['name'] ?? '');
+ $url = trim($_POST['url'] ?? '');
+ $description = trim($_POST['description'] ?? '');
+ if (empty($name)) {
+ $message = '工具名称不能为空';
+ $messageType = 'error';
+ } else {
+ $result = $database->addItemToSection($tableName, $name, $url, $description);
+ if ($result['success']) {
+ $message = '工具添加成功!';
+ } else {
+ $message = '添加失败: ' . ($result['error'] ?? '未知错误');
+ $messageType = 'error';
+ }
+ }
+}
+
+// --- 更新工具 ---
+if (isset($_POST['update_item'])) {
+ $tableName = $_POST['table_name'] ?? '';
+ $itemIndex = intval($_POST['item_index'] ?? 0);
+ $name = trim($_POST['name'] ?? '');
+ $url = trim($_POST['url'] ?? '');
+ $description = trim($_POST['description'] ?? '');
+ if (empty($name)) {
+ $message = '工具名称不能为空';
+ $messageType = 'error';
+ } else {
+ $result = $database->updateItemInSection($tableName, $itemIndex, $name, $url, $description);
+ if ($result['success']) {
+ $message = '工具更新成功!';
+ } else {
+ $message = '更新失败: ' . ($result['error'] ?? '未知错误');
+ $messageType = 'error';
+ }
+ }
+}
+
+// --- 删除工具 ---
+if (isset($_POST['delete_item'])) {
+ $tableName = $_POST['table_name'] ?? '';
+ $itemIndex = intval($_POST['item_index'] ?? 0);
+ $result = $database->deleteItemFromSection($tableName, $itemIndex);
+ if ($result['success']) {
+ $message = '工具已删除!';
+ } else {
+ $message = '删除失败: ' . ($result['error'] ?? '未知错误');
+ $messageType = 'error';
+ }
+}
+
+// --- 新增栏目 ---
+if (isset($_POST['add_section'])) {
+ $sectionName = trim($_POST['section_name'] ?? '');
+ if (empty($sectionName)) {
+ $message = '栏目名称不能为空';
+ $messageType = 'error';
+ } else {
+ $result = $database->createSection($sectionName);
+ if ($result['success']) {
+ $message = '栏目 "' . htmlspecialchars($sectionName) . '" 创建成功!';
+ } else {
+ $message = '创建失败: ' . ($result['error'] ?? '未知错误');
+ $messageType = 'error';
+ }
+ }
+}
+
+// --- 删除栏目 ---
+if (isset($_POST['delete_section'])) {
+ $tableName = $_POST['table_name'] ?? '';
+ $confirm = $_POST['confirm'] ?? '';
+ if ($confirm !== 'yes') {
+ $message = '请确认删除操作';
+ $messageType = 'error';
+ } else {
+ $result = $database->removeSection($tableName);
+ if ($result['success']) {
+ $message = '栏目已删除!';
+ } else {
+ $message = '删除失败: ' . ($result['error'] ?? '未知错误');
+ $messageType = 'error';
+ }
+ }
+}
+
+// --- 重命名栏目 ---
+if (isset($_POST['rename_section'])) {
+ $tableName = $_POST['table_name'] ?? '';
+ $newName = trim($_POST['new_name'] ?? '');
+ if (empty($newName)) {
+ $message = '栏目名称不能为空';
+ $messageType = 'error';
+ } else {
+ $result = $database->renameSection($tableName, $newName);
+ if ($result['success']) {
+ $message = '栏目重命名成功!';
+ } else {
+ $message = '重命名失败: ' . ($result['error'] ?? '未知错误');
+ $messageType = 'error';
+ }
+ }
+}
+
+// --- 批量导入 ---
+if (isset($_POST['batch_import'])) {
+ $tableName = $_POST['table_name'] ?? '';
+ $importData = trim($_POST['import_data'] ?? '');
+ $format = $_POST['import_format'] ?? 'csv';
+
+ if (empty($importData)) {
+ $message = '导入数据不能为空';
+ $messageType = 'error';
+ } else {
+ $lines = explode("\n", str_replace("\r\n", "\n", $importData));
+ $items = [];
+
+ foreach ($lines as $line) {
+ $line = trim($line);
+ if (empty($line)) continue;
+
+ if ($format === 'csv') {
+ // CSV格式: 名称,URL,描述
+ $parts = str_getcsv($line);
+ $items[] = [
+ trim($parts[0] ?? ''),
+ trim($parts[1] ?? ''),
+ trim($parts[2] ?? '')
+ ];
+ } else {
+ // 竖线格式: 名称 | URL | 描述
+ $parts = explode('|', $line);
+ $items[] = [
+ trim($parts[0] ?? ''),
+ trim($parts[1] ?? ''),
+ trim($parts[2] ?? '')
+ ];
+ }
+ }
+
+ $result = $database->batchAddItems($tableName, $items);
+ if ($result['success']) {
+ $message = "批量导入成功!成功添加 {$result['count']} 个工具(共 {$result['total']} 条数据)";
+ } else {
+ $message = '导入失败: ' . ($result['error'] ?? '未知错误');
+ $messageType = 'error';
+ }
+ }
+}
+
+// --- 导出JSON ---
+if (isset($_POST['export_json'])) {
+ $tableName = $_POST['table_name'] ?? 'all';
+ if ($tableName === 'all') {
+ $data = $database->exportAllSections();
+ $filename = 'sechub_all_sections.json';
+ $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
+ } elseif ($database->getSectionInfo($tableName)) {
+ $filePath = JSON_DIR . $tableName . '.json';
+ if (file_exists($filePath)) {
+ $json = file_get_contents($filePath);
+ $filename = $tableName . '.json';
+ } else {
+ $message = '文件不存在';
+ $messageType = 'error';
+ $json = null;
+ }
+ } else {
+ $message = '无效的栏目';
+ $messageType = 'error';
+ $json = null;
+ }
+
+ if (isset($json)) {
+ header('Content-Type: application/json; charset=utf-8');
+ header('Content-Disposition: attachment; filename="' . $filename . '"');
+ echo $json;
+ $database->close();
+ exit;
+ }
+}
+
+// --- 导出CSV ---
+if (isset($_POST['export_csv'])) {
+ $tableName = $_POST['table_name'] ?? '';
+
+ if (empty($tableName)) {
+ $message = '请选择要导出的栏目';
+ $messageType = 'error';
+ } else {
+ $csv = $database->exportSectionToCsv($tableName);
+ if (!empty($csv)) {
+ $info = $database->getSectionInfo($tableName);
+ $filename = ($info['title'] ?? $tableName) . '.csv';
+ header('Content-Type: text/csv; charset=utf-8');
+ header('Content-Disposition: attachment; filename="' . $filename . '"');
+ echo "\xEF\xBB\xBF" . $csv;
+ $database->close();
+ exit;
+ } else {
+ $message = '导出失败或栏目为空';
+ $messageType = 'error';
+ }
+ }
+}
+
+// --- 强制重新同步 ---
+if (isset($_POST['resync_db'])) {
+ try {
+ $database->syncJsonToDatabase();
+ $message = '数据库重新同步成功!';
+ } catch (Exception $e) {
+ $message = '同步失败: ' . $e->getMessage();
+ $messageType = 'error';
+ }
+}
+
+// --- 删除数据库 ---
if (isset($_POST['delete_db'])) {
- if (file_exists($dbPath)) {
- unlink($dbPath);
- $success = true;
- $message = '数据库已删除!刷新页面后将重新创建。';
+ if (file_exists(DB_PATH)) {
+ unlink(DB_PATH);
+ $message = '数据库已删除!刷新后自动重建。';
} else {
$message = '数据库文件不存在。';
+ $messageType = 'error';
}
}
-// 获取所有JSON文件信息
-$jsonFiles = glob($jsonDir . '*.json');
-$fileInfos = [];
+// --- 修改密码 ---
+if (isset($_POST['change_password'])) {
+ $oldPwd = $_POST['old_password'] ?? '';
+ $newPwd = $_POST['new_password'] ?? '';
+ $confirmPwd = $_POST['confirm_password'] ?? '';
-foreach ($jsonFiles as $filePath) {
- $filename = basename($filePath);
- $content = file_get_contents($filePath);
- $data = json_decode($content, true);
-
- if (json_last_error() === JSON_ERROR_NONE && is_array($data) && !empty($data)) {
- $firstItem = $data[0];
- $fileInfos[] = [
- 'filename' => $filename,
- 'section' => $firstItem['section'] ?? pathinfo($filename, PATHINFO_FILENAME),
- 'no' => $firstItem['no'] ?? 0,
- 'item_count' => count($data) - 1 // 减去第一个配置项
- ];
+ if ($oldPwd !== ADMIN_PASSWORD) {
+ $message = '当前密码错误';
+ $messageType = 'error';
+ } elseif (strlen($newPwd) < 6) {
+ $message = '新密码长度不能少于6位';
+ $messageType = 'error';
+ } elseif ($newPwd !== $confirmPwd) {
+ $message = '两次密码输入不一致';
+ $messageType = 'error';
+ } else {
+ // 更新config.php中的密码
+ $configPath = __DIR__ . '/config.php';
+ $configContent = file_get_contents($configPath);
+ $newContent = preg_replace(
+ "/define\('ADMIN_PASSWORD',\s*'[^']*'\)/",
+ "define('ADMIN_PASSWORD', '" . addslashes($newPwd) . "')",
+ $configContent
+ );
+ if (file_put_contents($configPath, $newContent)) {
+ $message = '密码修改成功!';
+ // 更新当前session中的标记(不需要改密码,因为会话还在)
+ } else {
+ $message = '密码修改失败(文件写入错误)';
+ $messageType = 'error';
+ }
}
}
-// 按当前 no 排序
-usort($fileInfos, function($a, $b) {
- return $a['no'] - $b['no'];
-});
+} // end CSRF check
+
+// 获取栏目列表
+$allSections = $database->getAllSectionsWithInfo();
+
+// 当前选中的栏目(从URL参数或第一个)
+$currentTab = $_GET['tab'] ?? 'sort';
+$selectedSection = $_GET['section'] ?? ($allSections[0]['name'] ?? '');
+
+// 获取选中栏目的工具列表
+$currentItems = [];
+$currentSectionInfo = null;
+if ($selectedSection) {
+ $currentItems = $database->getItemsBySection($selectedSection);
+ $currentSectionInfo = $database->getSectionInfo($selectedSection);
+}
?>
-
-
SecHub - 排序管理
-
+
SecHub - 管理后台
+
-