SecHub/index.php

373 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* SecHub - 网络安全工具导航页(紧凑版)
*/
// 定义路径
$jsonDir = __DIR__ . '/assets/json/';
$dbDir = __DIR__ . '/assets/db/';
$dbPath = $dbDir . 'sechub.db';
// 确保数据库目录存在
if (!is_dir($dbDir)) {
mkdir($dbDir, 0755, true);
}
// 引入数据库类
require_once __DIR__ . '/db.php';
// 初始化数据库并同步数据
try {
$database = new SecHubDatabase($dbPath, $jsonDir);
if (!file_exists($dbPath)) {
error_log("数据库文件不存在,将在同步时创建");
}
$database->syncJsonToDatabase();
// 获取栏目配置
$sections = $database->getSectionsConfig();
} catch (Exception $e) {
error_log("数据库初始化失败: " . $e->getMessage());
$sections = [];
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="./assets/imgs/favicon.ico" type="image/x-icon">
<title>SecHub简洁版</title>
<link rel="stylesheet" href="assets/css/style_simple.css">
</head>
<body>
<div class="container">
<header>
<h1>SecHub 网安工具集</h1>
<p>高密度浏览模式 | 一屏查看更多工具</p>
<!-- 全局搜索框 -->
<div class="search-container">
<input type="text"
id="global-search"
class="search-input"
placeholder="🔍 搜索工具名称、描述或链接..."
autocomplete="off">
<div id="search-results" class="search-results"></div>
</div>
<button id="theme-toggle" class="theme-toggle" title="点击切换白天/黑夜模式">
<span class="theme-icon">🌙</span>
</button>
<button id="view-toggle" class="view-toggle" title="切换视图">
<span class="view-icon">📑</span>
</button>
<div id="view-menu" class="view-menu">
<button class="view-menu-item active">
<span class="view-menu-icon">📋</span>
<span>简洁视图</span>
</button>
<button class="view-menu-item" onclick="window.location.href='full.php'">
<span class="view-menu-icon">📄</span>
<span>标准视图</span>
</button>
<button class="view-menu-item" onclick="window.location.href='table.php'">
<span class="view-menu-icon">📊</span>
<span>表格视图</span>
</button>
</div>
</header>
<?php foreach ($sections as $key => $config): ?>
<?php
$items = $database->getItemsBySection($key);
// 获取对应的JSON文件名
$jsonFile = $key . '.json';
?>
<section class="section" data-section="<?= htmlspecialchars($key) ?>">
<div class="section-header">
<div class="section-title-wrapper">
<h2 class="section-title">
<?= htmlspecialchars($config['title']) ?>
</h2>
<!-- 下载按钮 -->
<button class="download-btn"
onclick="downloadJson('<?= htmlspecialchars($jsonFile) ?>')"
title="下载JSON文件">
📥 下载数据源
</button>
</div>
<!-- 单项搜索框 -->
<input type="text"
class="section-search"
data-section="<?= htmlspecialchars($key) ?>"
placeholder="🔍 在此栏目中搜索..."
autocomplete="off">
</div>
<?php if (empty($items)): ?>
<div class="empty-state">
<p>暂无数据</p>
</div>
<?php else: ?>
<div class="compact-grid" data-section-items="<?= htmlspecialchars($key) ?>">
<?php foreach ($items as $item): ?>
<a href="<?= htmlspecialchars($item['url'] ?? '#') ?>" target="_blank" class="card-link-wrapper">
<div class="compact-card">
<div class="compact-card-header">
<h3 class="compact-card-title"><?= htmlspecialchars($item['name'] ?? '未命名') ?></h3>
<span class="compact-card-link"><?= htmlspecialchars($item['url'] ?? '') ?></span>
</div>
<p class="compact-card-description"><?= htmlspecialchars($item['description'] ?? '暂无简介') ?></p>
</div>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</section>
<?php endforeach; ?>
<footer class="panel-footer">
<div class="footer-content">
<p class="copyright">&copy; <?= date('Y') ?> <a href="https://git.masonliu.com/MasonLiu/SecHub" target="_blank" rel="noopener noreferrer">SecHub - 网络安全工具集</a></p>
<p class="copyright">Gaming Master Cybersecurity | MasonLiu</p>
<div class="beian-info">
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer" class="beian-link">蜀ICP备2026025173号</a>
<span class="beian-divider">|</span>
<a href="https://beian.mps.gov.cn/#/query/webSearch?code=51162302000285" target="_blank" rel="noopener noreferrer" class="beian-link">
<img src="/assets/imgs/beian.png" alt="公安备案" width="16" height="16" class="beian-icon"/>
<span>川公网安备51162302000285号</span>
</a>
</div>
</div>
</footer>
</div>
<script>
// HTML转义函数
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 渲染紧凑卡片(用于搜索结果)
function renderCompactCardJS(item) {
const name = item.name || '未命名';
const url = item.url || '#';
const description = item.description || '暂无简介';
return `
<a href="${url}" target="_blank" class="card-link-wrapper">
<div class="compact-card">
<div class="compact-card-header">
<h3 class="compact-card-title">${escapeHtml(name)}</h3>
<span class="compact-card-link">${escapeHtml(url)}</span>
</div>
<p class="compact-card-description">${escapeHtml(description)}</p>
</div>
</a>`;
}
// 全局搜索功能
const globalSearchInput = document.getElementById('global-search');
const searchResults = document.getElementById('search-results');
let searchTimeout;
globalSearchInput.addEventListener('input', function() {
clearTimeout(searchTimeout);
const keyword = this.value.trim();
if (keyword.length === 0) {
searchResults.style.display = 'none';
return;
}
searchTimeout = setTimeout(() => {
performGlobalSearch(keyword);
}, 300);
});
function performGlobalSearch(keyword) {
fetch(`search.php?action=global&keyword=${encodeURIComponent(keyword)}`)
.then(response => response.json())
.then(data => {
displaySearchResults(data);
})
.catch(error => {
console.error('搜索失败:', error);
});
}
function displaySearchResults(results) {
searchResults.innerHTML = '';
if (Object.keys(results).length === 0) {
searchResults.innerHTML = '<div class="no-results">未找到相关结果</div>';
searchResults.style.display = 'block';
return;
}
Object.keys(results).forEach(tableName => {
const section = results[tableName];
const sectionDiv = document.createElement('div');
sectionDiv.className = 'search-result-section';
const title = document.createElement('h3');
title.className = 'search-result-title';
title.textContent = section.section;
sectionDiv.appendChild(title);
const cardsGrid = document.createElement('div');
cardsGrid.className = 'compact-grid';
section.items.forEach(item => {
const cardDiv = document.createElement('div');
cardDiv.innerHTML = renderCompactCardJS(item);
cardsGrid.appendChild(cardDiv.firstElementChild);
});
sectionDiv.appendChild(cardsGrid);
searchResults.appendChild(sectionDiv);
});
searchResults.style.display = 'block';
}
// 单项搜索功能
document.querySelectorAll('.section-search').forEach(input => {
input.addEventListener('input', function() {
const section = this.dataset.section;
const keyword = this.value.trim().toLowerCase();
const cardsGrid = document.querySelector(`[data-section-items="${section}"]`);
if (!cardsGrid) return;
const cards = cardsGrid.querySelectorAll('.compact-card');
cards.forEach(card => {
const title = card.querySelector('.compact-card-title').textContent.toLowerCase();
const description = card.querySelector('.compact-card-description').textContent.toLowerCase();
const link = card.querySelector('.compact-card-link').textContent.toLowerCase();
if (keyword === '' ||
title.includes(keyword) ||
description.includes(keyword) ||
link.includes(keyword)) {
card.style.display = 'block';
} else {
card.style.display = 'none';
}
});
});
});
// 下载JSON文件
function downloadJson(filename) {
const url = `assets/json/${filename}`;
const link = document.createElement('a');
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// 点击其他地方关闭搜索结果
document.addEventListener('click', function(e) {
if (!e.target.closest('.search-container')) {
searchResults.style.display = 'none';
}
});
// 黑夜模式切换功能
(function() {
const themeToggle = document.getElementById('theme-toggle');
const themeIcon = themeToggle.querySelector('.theme-icon');
const body = document.body;
// 从 localStorage 读取主题设置
const currentTheme = localStorage.getItem('theme') || 'light';
// 应用保存的主题
if (currentTheme === 'dark') {
body.classList.add('dark-mode');
themeIcon.textContent = '☀️';
}
// 切换主题
themeToggle.addEventListener('click', function() {
body.classList.toggle('dark-mode');
if (body.classList.contains('dark-mode')) {
themeIcon.textContent = '☀️';
localStorage.setItem('theme', 'dark');
} else {
themeIcon.textContent = '🌙';
localStorage.setItem('theme', 'light');
}
});
})();
// 视图切换下拉菜单
(function() {
const viewToggle = document.getElementById('view-toggle');
const viewMenu = document.getElementById('view-menu');
// 点击按钮显示/隐藏菜单
viewToggle.addEventListener('click', function(e) {
e.stopPropagation();
viewMenu.classList.toggle('show');
});
// 点击其他地方关闭菜单
document.addEventListener('click', function(e) {
if (!viewToggle.contains(e.target) && !viewMenu.contains(e.target)) {
viewMenu.classList.remove('show');
}
});
// ESC 键关闭菜单
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
viewMenu.classList.remove('show');
}
});
})();
</script>
<!-- 返回顶部按钮 -->
<button id="back-to-top" class="back-to-top" title="返回顶部">
</button>
<script>
// 返回顶部功能
(function() {
const backToTopBtn = document.getElementById('back-to-top');
// 滚动时显示/隐藏按钮
window.addEventListener('scroll', function() {
if (window.pageYOffset > 300) {
backToTopBtn.classList.add('show');
} else {
backToTopBtn.classList.remove('show');
}
});
// 点击返回顶部
backToTopBtn.addEventListener('click', function() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
})();
</script>
</body>
</html>