- <?php
- /**
- * 炫彩导航 - 终极管理版 (全兼容修复版)
- * 功能:搜索、排序、编辑、审核、后台改密
- * 兼容:PHP 5.2+
- * 修复:解决 header 已发送错误
- */
- // ========== 所有业务逻辑必须放在最顶部(无任何输出之前) ==========
- // 兼容PHP 5.2时区问题
- if(function_exists('date_default_timezone_set')){
- date_default_timezone_set('PRC');
- }
- // 开启输出缓冲(关键:解决header已发送问题)
- ob_start();
- // 字符编码设置(PHP 5.2兼容)
- header("Content-Type: text/html; charset=utf-8");
- @session_start(); // 兼容PHP 5.2的session启动方式
- // 配置常量(便于维护)
- define('DATA_FILE', 'nav_db.json');
- define('SITE_TITLE', '炫彩收录导航');
- define('DEFAULT_PASS', 'Tmd123');
- define('COOKIE_EXPIRE', 3600*24);
- // --- 安全函数(PHP 5.2兼容) ---
- /**
- * 安全过滤输入
- * @param string $str 输入字符串
- * @return string 过滤后的字符串
- */
- function safe_input($str) {
- if(empty($str)) return '';
- // PHP 5.2兼容的过滤
- if(get_magic_quotes_gpc()){
- $str = stripslashes($str);
- }
- return htmlspecialchars(trim($str), ENT_QUOTES, 'UTF-8');
- }
- /**
- * JSON编码(兼容PHP 5.2中文)
- * @param array $data 数据数组
- * @return string JSON字符串
- */
- function json_encode_52($data) {
- if(function_exists('json_encode')){
- // PHP 5.2 json_encode中文转义问题修复
- return preg_replace("#\\\u([0-9a-f]{4})#ie", "iconv('UCS-2BE', 'UTF-8', pack('H4', '\\1'))", json_encode($data));
- }
- return '';
- }
- /**
- * 安全写入文件(加锁防止并发问题)
- * @param string $file 文件路径
- * @param string $content 内容
- * @return bool 是否成功
- */
- function safe_file_put_contents($file, $content) {
- $dir = dirname($file);
- // 检查目录是否存在
- if(!is_dir($dir)){
- @mkdir($dir, 0755, true);
- }
-
- // 加锁写入(PHP 5.2兼容)
- $fp = fopen($file, 'w');
- if(!$fp) return false;
-
- flock($fp, LOCK_EX);
- $result = fwrite($fp, $content);
- flock($fp, LOCK_UN);
- fclose($fp);
-
- return $result !== false;
- }
- // --- 数据读取与初始化 ---
- if (!file_exists(DATA_FILE)) {
- $data = array(
- "config" => array("password" => md5(DEFAULT_PASS)), // 密码加密存储
- "active" => array(array("name"=>"百度","url"=>"https://www.baidu.com", "time"=>date("Y-m-d"))),
- "pending" => array()
- );
- safe_file_put_contents(DATA_FILE, json_encode_52($data));
- } else {
- // 读取文件(处理空文件情况)
- $fileContent = @file_get_contents(DATA_FILE);
- if($fileContent === false || $fileContent === ''){
- $data = array(
- "config" => array("password" => md5(DEFAULT_PASS)),
- "active" => array(),
- "pending" => array()
- );
- }else{
- $data = json_decode($fileContent, true);
- // 兼容旧数据结构
- if (!is_array($data)) $data = array();
- if (!isset($data['config']) || !is_array($data['config'])) {
- $data['config'] = array("password" => md5(DEFAULT_PASS));
- }
- // 确保密码是加密的(升级旧数据)
- if(isset($data['config']['password']) && strlen($data['config']['password']) != 32){
- $data['config']['password'] = md5($data['config']['password']);
- }
- if (!isset($data['active']) || !is_array($data['active'])) $data['active'] = array();
- if (!isset($data['pending']) || !is_array($data['pending'])) $data['pending'] = array();
- }
- }
- $admin_pass = isset($data['config']['password']) ? $data['config']['password'] : md5(DEFAULT_PASS);
- // --- 登录/退出逻辑 ---
- $isAdmin = false;
- // 验证管理员身份(PHP 5.2兼容)
- if (isset($_SESSION['admin']) && $_SESSION['admin'] === "logined") {
- $isAdmin = true;
- } elseif (isset($_COOKIE['admin_auth']) && $_COOKIE['admin_auth'] === $admin_pass) {
- $_SESSION['admin'] = "logined"; // 同步session
- $isAdmin = true;
- }
- // ========== 所有涉及header跳转的逻辑必须放在HTML输出前 ==========
- // 登录处理
- $login_error = '';
- if (isset($_POST['login_pass']) && !empty($_POST['login_pass'])) {
- $login_pass = md5(trim($_POST['login_pass']));
- if ($login_pass === $admin_pass) {
- $_SESSION['admin'] = "logined";
- setcookie("admin_auth", $admin_pass, time()+COOKIE_EXPIRE, '/'); // 指定路径,兼容多目录
- header("Location: " . $_SERVER['PHP_SELF']);
- exit; // 必须exit,防止后续代码执行
- } else {
- $login_error = "<script>alert('密码错误!');</script>";
- }
- }
- // 退出处理
- if (isset($_GET['logout']) && $_GET['logout'] == 1) {
- $_SESSION = array(); // 清空session
- if (isset($_COOKIE[session_name()])) {
- setcookie(session_name(), '', time()-COOKIE_EXPIRE, '/');
- }
- setcookie("admin_auth", "", time()-COOKIE_EXPIRE, '/');
- session_destroy();
- header("Location: " . $_SERVER['PHP_SELF']);
- exit; // 必须exit
- }
- // 修改密码
- $pw_error = '';
- if ($isAdmin && isset($_POST['change_pw'])) {
- $new_pw = trim($_POST['new_password']);
- if (!empty($new_pw) && strlen($new_pw) >= 6) {
- $data['config']['password'] = md5($new_pw);
- safe_file_put_contents(DATA_FILE, json_encode_52($data));
- header("Location: ?logout=1");
- exit; // 必须exit
- } else {
- $pw_error = "<script>alert('新密码不能为空且至少6位!');</script>";
- }
- }
- // 保存/提交链接
- $save_error = '';
- if (isset($_POST['save_node'])) {
- $name = safe_input($_POST['app_name']);
- $url = safe_input($_POST['app_url']);
- $edit_id = isset($_POST['edit_id']) ? trim($_POST['edit_id']) : "";
-
- // 验证输入
- if(empty($name) || empty($url)){
- $save_error = "<script>alert('网站名称和网址不能为空!');</script>";
- } elseif(!preg_match('/^https?:\/\/.+/i', $url)){
- $save_error = "<script>alert('请输入有效的网址(以http/https开头)!');</script>";
- } else {
- if ($edit_id !== "" && $isAdmin) {
- $edit_id_int = intval($edit_id);
- if(isset($data['active'][$edit_id_int])){
- $data['active'][$edit_id_int] = array(
- "name"=>$name,
- "url"=>$url,
- "time"=>date("Y-m-d")
- );
- }
- } else {
- $data['pending'][] = array(
- "name"=>$name,
- "url"=>$url,
- "time"=>date("Y-m-d")
- );
- $save_error = "<script>alert('申请已提交');</script>";
- }
-
- safe_file_put_contents(DATA_FILE, json_encode_52($data));
- header("Location: " . $_SERVER['PHP_SELF']);
- exit; // 必须exit
- }
- }
- // 管理员操作(审核/删除/排序)
- if ($isAdmin && isset($_GET['action']) && isset($_GET['id'])) {
- $id = intval($_GET['id']);
- $action = safe_input($_GET['action']);
-
- // 验证操作类型
- $allowed_actions = array('approve', 'reject', 'del', 'up', 'down');
- if(in_array($action, $allowed_actions)){
- switch ($action) {
- case 'approve':
- if(isset($data['pending'][$id])){
- $data['active'][] = $data['pending'][$id];
- unset($data['pending'][$id]);
- }
- break;
- case 'reject':
- if(isset($data['pending'][$id])){
- unset($data['pending'][$id]);
- }
- break;
- case 'del':
- if(isset($data['active'][$id])){
- unset($data['active'][$id]);
- }
- break;
- case 'up':
- if($id > 0 && isset($data['active'][$id]) && isset($data['active'][$id-1])){
- $tmp = $data['active'][$id];
- $data['active'][$id] = $data['active'][$id-1];
- $data['active'][$id-1] = $tmp;
- }
- break;
- case 'down':
- $active_count = count($data['active']);
- if($id < $active_count-1 && isset($data['active'][$id]) && isset($data['active'][$id+1])){
- $tmp = $data['active'][$id];
- $data['active'][$id] = $data['active'][$id+1];
- $data['active'][$id+1] = $tmp;
- }
- break;
- }
-
- // 重新索引数组(PHP 5.2兼容)
- $data['pending'] = isset($data['pending']) && is_array($data['pending']) ? array_values($data['pending']) : array();
- $data['active'] = isset($data['active']) && is_array($data['active']) ? array_values($data['active']) : array();
-
- safe_file_put_contents(DATA_FILE, json_encode_52($data));
- }
- header("Location: " . $_SERVER['PHP_SELF']);
- exit; // 必须exit
- }
- // 编辑链接数据准备
- $edit_node = array("name"=>"", "url"=>"", "id"=>"");
- if($isAdmin && isset($_GET['edit']) && is_numeric($_GET['edit'])){
- $eid = intval($_GET['edit']);
- if(isset($data['active'][$eid])){
- $edit_node = $data['active'][$eid];
- $edit_node['id'] = $eid;
- }
- }
- // ========== HTML输出部分(所有跳转逻辑已处理完毕) ==========
- ?>
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title><?php echo htmlspecialchars(SITE_TITLE); ?></title>
- <style>
- @keyframes gradient { 0% {background-position: 0% 50%;} 50% {background-position: 100% 50%;} 100% {background-position: 0% 50%;} }
- body { margin: 0; min-height: 100vh; background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); background-size: 400% 400%; animation: gradient 15s ease infinite; font-family: sans-serif; color: #fff; }
- .container { max-width: 900px; margin: 0 auto; padding: 20px; }
- .glass { background: rgba(255, 255, 255, 0.15); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 15px; padding: 20px; margin-bottom: 20px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); }
- .search-area { text-align: center; margin-bottom: 30px; }
- #search-input { width: 60%; max-width: 400px; background: rgba(255,255,255,0.2); border: 1px solid rgba(255,255,255,0.4); padding: 12px 20px; border-radius: 30px; color: #fff; font-size: 16px; outline: none; transition: 0.3s; }
- #search-input:focus { width: 80%; background: #fff; color: #333; }
- .engine-btns { margin-top: 12px; display: flex; justify-content: center; gap: 8px; flex-wrap: wrap; }
- .engine-btns button { background: rgba(255,255,255,0.2); border: 1px solid rgba(255,255,255,0.2); color: #fff; padding: 6px 15px; border-radius: 20px; cursor: pointer; font-size: 13px; }
- .nav-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 15px; }
- .card-wrap { position: relative; }
- .nav-card { background: rgba(255, 255, 255, 0.2); padding: 20px; border-radius: 12px; text-decoration: none; color: #fff; text-align: center; font-weight: bold; display: block; border: 1px solid rgba(255,255,255,0.1); transition: 0.3s; overflow: hidden; text-overflow: ellipsis; }
- .nav-card:hover { background: #fff; color: #e73c7e; transform: translateY(-3px); }
- .sort-tools { position: absolute; bottom: -8px; left: 50%; transform: translateX(-50%); display: none; background: rgba(0,0,0,0.7); border-radius: 10px; padding: 2px 8px; gap: 8px; z-index: 5; }
- .card-wrap:hover .sort-tools { display: flex; }
- .sort-tools a { color: #fff; text-decoration: none; font-size: 11px; }
- .del-tag { position: absolute; top: -8px; right: -5px; background: #ff4d4d; color: white; border-radius: 50%; width: 18px; height: 18px; text-decoration: none; font-size: 12px; line-height: 18px; text-align:center; }
- .edit-tag { position: absolute; top: -8px; left: -5px; background: #23a6d5; color: white; border-radius: 50%; width: 18px; height: 18px; text-decoration: none; font-size: 11px; line-height: 18px; text-align:center; }
- input { background: rgba(255,255,255,0.2); border: 1px solid rgba(255,255,255,0.3); padding: 10px; border-radius: 8px; color: #fff; outline: none; margin: 5px 0;}
- .btn-main { background: #fff; color: #e73c7e; border: none; padding: 10px 20px; border-radius: 8px; font-weight: bold; cursor: pointer; }
- footer { text-align: center; padding: 40px; background: rgba(0,0,0,0.2); margin-top: 40px; }
- </style>
- </head>
- <body>
- <?php
- // 输出错误提示(放在HTML内,避免header冲突)
- echo $login_error . $pw_error . $save_error;
- ?>
- <div class="container">
- <h1 style="text-align:center; text-shadow: 0 4px 10px rgba(0,0,0,0.2);">✨ <?php echo htmlspecialchars(SITE_TITLE); ?></h1>
- <div class="search-area">
- <input type="text" id="search-input" placeholder="输入关键词搜索..." onkeyup="filterLinks()">
- <div class="engine-btns">
- <button onclick="webSearch('https://www.baidu.com/s?wd=')">百度</button>
- <button onclick="webSearch('https://www.google.com/search?q=')">Google</button>
- <button onclick="webSearch('https://yandex.com/search/?text=')">Yandex</button>
- </div>
- </div>
- <?php if($isAdmin): ?>
- <div class="glass" style="border-left: 5px solid #00ff00;">
- <div style="display:flex; justify-content:space-between; align-items:flex-start;">
- <div>
- <h3 style="margin:0">🛠️ 管理中心 | <a href="?logout=1" style="color:yellow; text-decoration:none;">退出登录</a></h3>
- <div style="margin-top:10px;">
- <form method="POST" style="display:flex; gap:5px;">
- <input type="password" name="new_password" placeholder="设置新管理密码(至少6位)" style="padding:5px; font-size:12px;">
- <button type="submit" name="change_pw" class="btn-main" style="padding:5px 10px; font-size:12px;">修改密码</button>
- </form>
- </div>
- </div>
- <div style="text-align:right;">
- <?php if(!empty($data['pending'])): ?>
- <p style="margin:0; font-size:14px;">待审核 (<?php echo count($data['pending']); ?>)</p>
- <?php foreach($data['pending'] as $id => $item): ?>
- <div style="font-size:12px; margin-top:5px;">
- <?php echo htmlspecialchars($item['name']); ?>
- <a href="?action=approve&id=<?php echo $id; ?>" style="color:#0f0;">[准]</a>
- <a href="?action=reject&id=<?php echo $id; ?>" style="color:#f44;" onclick="return confirm('确定拒绝?')">[拒]</a>
- </div>
- <?php endforeach; ?>
- <?php endif; ?>
- </div>
- </div>
- </div>
- <?php endif; ?>
- <div class="nav-grid" id="link-container">
- <?php if(!empty($data['active'])): ?>
- <?php foreach($data['active'] as $id => $nav): ?>
- <div class="card-wrap" data-name="<?php echo strtolower(htmlspecialchars($nav['name'])); ?>">
- <a href="<?php echo htmlspecialchars($nav['url']); ?>" target="_blank" class="nav-card"><?php echo htmlspecialchars($nav['name']); ?></a>
- <?php if($isAdmin): ?>
- <a href="?edit=<?php echo $id; ?>#form" class="edit-tag">✎</a>
- <a href="?action=del&id=<?php echo $id; ?>" class="del-tag" onclick="return confirm('确定删除?')">×</a>
- <div class="sort-tools">
- <a href="?action=up&id=<?php echo $id; ?>">◀ 前移</a> <a href="?action=down&id=<?php echo $id; ?>">后移 ▶</a>
- </div>
- <?php endif; ?>
- </div>
- <?php endforeach; ?>
- <?php endif; ?>
- </div>
- <div id="form" class="glass" style="margin-top:40px; text-align:center;">
- <h3><?php echo ($edit_node['id']!=="") ? "📝 修改内容" : "📩 申请收录"; ?></h3>
- <form method="POST">
- <input type="hidden" name="edit_id" value="<?php echo htmlspecialchars($edit_node['id']); ?>">
- <input type="text" name="app_name" placeholder="网站名称" required style="width:120px;" value="<?php echo htmlspecialchars($edit_node['name']); ?>">
- <input type="url" name="app_url" placeholder="网址 https://..." required style="width:250px;" value="<?php echo htmlspecialchars($edit_node['url']); ?>">
- <button type="submit" name="save_node" class="btn-main"><?php echo ($edit_node['id']!=="") ? "保存修改" : "提交申请"; ?></button>
- <?php if($edit_node['id']!==""): ?><br><a href="<?php echo $_SERVER['PHP_SELF']; ?>" style="color:#fff; font-size:12px;">取消编辑</a><?php endif; ?>
- </form>
- </div>
- </div>
- <script>
- function filterLinks() {
- var input = document.getElementById('search-input').value.toLowerCase();
- var cards = document.getElementsByClassName('card-wrap');
- for (var i = 0; i < cards.length; i++) {
- var name = cards[i].getAttribute('data-name');
- cards[i].style.display = name.indexOf(input) > -1 ? "block" : "none";
- }
- }
- function webSearch(baseUrl) {
- var query = document.getElementById('search-input').value;
- if (query) {
- window.open(baseUrl + encodeURIComponent(query), '_blank');
- } else {
- alert('请输入搜索关键词');
- }
- }
- </script>
- <footer>
- <?php if(!$isAdmin): ?>
- <form method="POST">
- <input type="password" name="login_pass" placeholder="管理密码" style="width:80px; font-size:12px;">
- <button type="submit" class="btn-main" style="padding:5px 10px; font-size:12px;">登录</button>
- </form>
- <?php endif; ?>
- <p style="font-size:10px; opacity:0.5; margin-top:15px;">© 2025 <?php echo htmlspecialchars(SITE_TITLE); ?> | PHP 5.2兼容版</p>
- </footer>
- </body>
- </html>
- <?php
- // 结束输出缓冲并发送内容
- ob_end_flush();
- ?>
复制代码 |