2026年1月

本文转自https://blog.ybyq.wang/archives/325.html,感谢大佬的分享,转载仅做存档,侵权删除

背景说明

这个版本和之前的做了以下升级: 1.新增了缓存命中率系统信息操作系统连接类型当前域名的信息显示 2.添加了对应信息项的Awesome图标 3.固定了标签颜色,使其不受插件标签颜色的影响, 4.优化了性能并增加了对Apache的适配。 5.保留了在长亭雷池WAF或宝塔WAF保护下IP的真实性

功能特点

  • 实时监控服务器CPU、内存、磁盘使用率
  • 显示服务器运行时间、IO和网络状态
  • 准确获取访客IP、地理位置、设备和浏览器信息
  • 全面支持电信、联通、移动等各类网络环境
  • 兼容Linux系统(CentOS Stream 9、Ubuntu 22.04、Debian 10.2等)
  • 环境是Linux系统PHP8.0(7.4以上即可),typecho1.2.1

实现步骤

1. 安装必要的系统工具(可选)

首先,确保系统安装了必要的工具:

apt-get update
apt-get install -y procps net-tools sysstat

2. 修改 headnav.php 文件

文件位置:usr/themes/handsome/component/headnav.php

在找到如下代码段:

<!-- statitic info-->
<?php
if (@Utils::getExpertValue("show_static",true) !== false): ?>
<ul class="nav navbar-nav hidden-sm">

    <!-- 在此追加代码 -->

    <li class="dropdown pos-stc">

添加代码:

[zd-plane title="折叠标题" open="0"]

<!-- 引入Font Awesome图标库 -->
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
        <li class="dropdown pos-stc" id="StateDataPos">
            <a id="StateData" href="#" data-toggle="dropdown" class="dropdown-toggle feathericons dropdown-toggle">
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-activity">
                    <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
                </svg>
                <span class="caret"></span>
            </a>
            <div class="dropdown-menu wrapper w-full bg-white">
                <div class="row">
                    <div class="col-sm-4 b-l b-light">
                        <div class="m-t-xs m-b-xs font-bold">运行状态</div>
                        <div class="">
                            <span class="pull-right text-danger" id="cpu">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-microchip fa-fw" aria-hidden="true"></i> CPU占用
                                <span class="badge badge-sm bg-dark">8核心</span>
                            </span>
                        </div>
                        <div class="progress progress-xs m-t-sm bg-default">
                            <div id="cpu_css" class="progress-bar bg-danger" data-toggle="tooltip" style="width: 100%"></div>
                        </div>
                        <div class="">
                            <span class="pull-right text-danger" id="memory">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-memory fa-fw" aria-hidden="true"></i> 占用内存
                                <span class="badge badge-sm bg-dark" id="memory_data">
                                    <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                                </span>
                            </span>
                        </div>
                        <div class="progress progress-xs m-t-sm bg-default">
                            <div id="memory_css" class="progress-bar bg-danger" data-toggle="tooltip" style="width: 100%"></div>
                        </div>
                        <div class="">
                            <span class="pull-right text-danger" id="disk">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-hdd fa-fw" aria-hidden="true"></i> 磁盘占用
                                <span class="badge badge-sm bg-dark" id="disk_data">
                                    <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                                </span>
                            </span>
                        </div>
                        <div class="progress progress-xs m-t-sm bg-default">
                            <div id="disk_css" class="progress-bar bg-danger" data-toggle="tooltip" style="width: 100%"></div>
                        </div>
                        <div class="">
                            <span class="pull-right text-danger" id="memCached">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-database fa-fw" aria-hidden="true"></i> 内存缓存
                                <span class="badge badge-sm bg-dark" id="memCached_data">
                                    <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                                </span>
                            </span>
                        </div>
                        <div class="progress progress-xs m-t-sm bg-default">
                            <div id="memCached_css" class="progress-bar bg-danger" data-toggle="tooltip" style="width: 100%"></div>
                        </div>
                        <div class="">
                            <span class="pull-right text-danger" id="memBuffers">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-layer-group fa-fw" aria-hidden="true"></i> 内存缓冲
                                <span class="badge badge-sm bg-dark" id="memBuffers_data">
                                    <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                                </span>
                            </span>
                        </div>
                        <div class="progress progress-xs m-t-sm bg-default">
                            <div id="memBuffers_css" class="progress-bar bg-danger" data-toggle="tooltip" style="width: 100%"></div>
                        </div>
                        <div class="">
                            <span class="pull-right text-danger" id="state_s">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-tachometer-alt fa-fw" aria-hidden="true"></i> 系统负载
                                <span id="state">
                                    <span class="badge badge-sm bg-dark">
                                        <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                                    </span>
                                </span>
                            </span>
                        </div>
                        <div class="progress progress-xs m-t-sm bg-default">
                            <div id="state_css" class="progress-bar bg-danger" data-toggle="tooltip" style="width: 100%"></div>
                        </div>
                        <div class="">
                            <span class="pull-right text-danger" id="cacheHitRate">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-bolt fa-fw" aria-hidden="true"></i> 缓存命中率</span>
                        </div>
                        <div class="progress progress-xs m-t-sm bg-default">
                            <div id="cacheHitRate_css" class="progress-bar bg-info" data-toggle="tooltip" style="width: 0%"></div>
                        </div>
                    </div>
                    <div class="col-sm-4 b-l b-light visible-lg visible-md">
                        <div class="m-t-xs m-b-xs font-bold">网络状态</div>
                        <div class="">
                            <span class="pull-right text-default" id="io">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-exchange-alt fa-fw" aria-hidden="true"></i> IO</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default" id="io1">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-random fa-fw" aria-hidden="true"></i> 实时IO</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default" id="eth">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-globe fa-fw" aria-hidden="true"></i> 网络</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default" id="eth1">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-wifi fa-fw" aria-hidden="true"></i> 实时网络</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default" id="time">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="far fa-clock fa-fw" aria-hidden="true"></i> 服务器时间</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default">
                                <span class="badge badge-sm bg-dark"><?php echo $_SERVER['SERVER_SOFTWARE']; ?></span></span>
                            <span><i class="fas fa-server fa-fw" aria-hidden="true"></i> WEB服务器</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default">
                                <span class="badge badge-sm bg-dark"><?php echo $_SERVER['SERVER_PROTOCOL']; ?></span>
                            </span>
                            <span><i class="fas fa-route fa-fw" aria-hidden="true"></i> 通信协议</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default">
                                <span class="badge badge-sm bg-dark"><?php echo PHP_VERSION; ?></span>
                            </span>
                            <span><i class="fas fa-code fa-fw" aria-hidden="true"></i> PHP版本</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default" id="sys_info_badge">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-cogs fa-fw" aria-hidden="true"></i> 系统信息</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default" id="os_info_badge">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-server fa-fw" aria-hidden="true"></i> 操作系统</span>
                        </div>
                        <br />
                    </div>
                    <div class="col-sm-4 b-l b-light visible-lg visible-md">
                        <div class="m-t-xs m-b-sm font-bold">访客信息</div>
                        <div class="">
                            <span class="pull-right text-default" id="u_time">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-hourglass-half fa-fw" aria-hidden="true"></i> 持续运行</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default" id="ip">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-map-marker-alt fa-fw" aria-hidden="true"></i> 您的IP</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default" id="address">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-location-arrow fa-fw" aria-hidden="true"></i> 网络地址</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default" id="b">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fab fa-chrome fa-fw" aria-hidden="true"></i> 浏览器信息</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default" id="sys">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="fas fa-laptop fa-fw" aria-hidden="true"></i> 您的设备</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default">
                                <span class="badge badge-sm bg-dark"><?php echo $_SERVER['REQUEST_METHOD']; ?></span></span>
                            <span><i class="fas fa-paper-plane fa-fw" aria-hidden="true"></i> 请求方法</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default">
                                <span class="badge badge-sm bg-dark"><?php echo $_SERVER['HTTP_ACCEPT_LANGUAGE']; ?></span></span>
                            <span><i class="fas fa-language fa-fw" aria-hidden="true"></i> 服务语言</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default">
                                <span class="badge badge-sm bg-dark"><?php echo (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? "HTTPS" : "HTTP"; ?></span>
                            </span>
                            <span><i class="fas fa-lock fa-fw" aria-hidden="true"></i> 连接类型</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default">
                                <span class="badge badge-sm bg-dark"><?php echo $_SERVER['HTTP_HOST']; ?></span>
                            </span>
                            <span><i class="fas fa-globe fa-fw" aria-hidden="true"></i> 当前域名</span>
                        </div>
                        <br />
                        <div class="">
                            <span class="pull-right text-default" id="sys_times">
                                <i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
                            </span>
                            <span><i class="far fa-calendar-alt fa-fw" aria-hidden="true"></i> 您的设备时间</span>
                        </div>
                    </div>
                </div>
            </div>
        </li>

[/zd-plane]

3. 修改 footer.php 文件

文件位置:usr/themes/handsome/component/footer.php

在找到如下代码位置:

<?php $this->options->bottomHtml(); ?>

   <!-- 在此追加代码 -->

</body>
</html><!--html end-->

添加如下JavaScript代码:

[zd-plane title="折叠标题" open="0"]

<!-- 这里开始是新追加的内容 -->
<script>
var stateUrl = '/serverInfo.php';
    var se_rx = null;
    var se_tx = null;
    var si_rx = null;
    var si_tx = null;
    var lastCachedValue = 0;
    var lastBuffersValue = 0;
    var cacheHitRate = 0;

    function returnFloat(value) {
        return value.toFixed(2) + '%';
}

    function floats(value) {
    return value.toFixed(2);
}

function getPercent(curNum, totalNum, isHasPercentStr) {
    curNum = parseFloat(curNum);
    totalNum = parseFloat(totalNum);

    if (isNaN(curNum) || isNaN(totalNum)) {
        return 'Error';
    }

    return isHasPercentStr ?
        totalNum <= 0 ? '0%' : (Math.round(curNum / totalNum * 10000) / 100.00 + '%') :
            totalNum <= 0 ? 0 : (Math.round(curNum / totalNum * 10000) / 100.00 + '%');
}

function getPercents(curNum, totalNum, isHasPercentStr) {
    curNum = parseFloat(curNum);
    totalNum = parseFloat(totalNum);

    if (isNaN(curNum) || isNaN(totalNum)) {
        return 'Error';
    }

    return isHasPercentStr ?
        totalNum <= 0 ? '0%' : (Math.round(curNum / totalNum * 10000) / 100.00) :
        totalNum <= 0 ? 0 : (Math.round(curNum / totalNum * 10000) / 100.00);
}

    function setSize(value, d) {
    switch (d) {
        case 'bit':
                return bit = value * 8;
            break;
        case 'bytes':
            return value;
            break;
        case 'kb':
                return value / 1024;
            break;
        case 'mb':
                return value / 1024 / 1024;
            break;
        case 'gb':
                return value / 1024 / 1024 / 1024;
            break;
        case 'tb':
                return value / 1024 / 1024 / 1024 / 1024;
            break;
    }
}

    function ForDight(Dight) {
        if (Dight < 0) {
            var Last = 0 + "B/s";
        } else if (Dight < 1024) {
            var Last = setSize(Dight, 'bytes').toFixed(0) + "B/s";
        } else if (Dight < 1048576) {
            var Last = floats(setSize(Dight, 'kb')) + "K/s";
        } else {
            var Last = floats(setSize(Dight, 'mb')) + "MB/s";
    }
    return Last; 
}

    function state() {
    $.ajax({
        url: stateUrl,
        type: 'get',
        dataType: 'json',
        data: {
                action: 'fetch',
        },
            beforeSend: function() {
  
        },
            complete: function() {
  
        },
            error: function(xhr, status, error) {
                console.error("AJAX Error:", status, error);
                // Maybe show a global error message in the UI
        }, 
            success: function(data) {
                // Defensively check for data and nested properties
                var serverStatus = data.serverStatus || {};
                var serverInfo = data.serverInfo || {};
                var networkStats = data.networkStats || {};
                var networks = networkStats.networks || {};

                // CPU Usage
                try {
                    var cpuData = serverStatus.cpuUsage || {};
                    var cpu = (parseFloat(cpuData['user']) || 0) + (parseFloat(cpuData['nice']) || 0) + (parseFloat(cpuData['sys']) || 0);
            $("#cpu").html(returnFloat(cpu));
                    $("#cpu_css").css("width", returnFloat(cpu));
                    if (cpu < 70) {
                        $("#cpu_css").removeClass().addClass("progress-bar bg-success");
                        $("#cpu").removeClass().addClass("pull-right text-success");
                    } else if (cpu >= 90) {
                        $("#cpu_css").removeClass().addClass("progress-bar bg-danger");
                        $("#cpu").removeClass().addClass("pull-right text-danger");
                    } else {
                        $("#cpu_css").removeClass().addClass("progress-bar bg-warning");
                        $("#cpu").removeClass().addClass("pull-right text-warning");
            }
                } catch (e) {
                    console.error("Error processing CPU:", e);
                    $("#cpu").html('Error');
                }

                // Memory Usage
                try {
                    var memData = serverStatus.memRealUsage || {};
                    var memory_value = memData.value || 0;
                    var memory_max = memData.max || 1; // Avoid division by zero
                    var memPercent = getPercent(memory_value, memory_max, true);
                    var me = getPercents(memory_value, memory_max, false);
      
                    $("#memory").html(memPercent);
                    $("#memory_css").css("width", memPercent);

                    if (me < 70) {
                        $("#memory_css").removeClass().addClass("progress-bar bg-success");
                        $("#memory").removeClass().addClass("pull-right text-success");
                    } else if (me >= 90) {
                        $("#memory_css").removeClass().addClass("progress-bar bg-danger");
                        $("#memory").removeClass().addClass("pull-right text-danger");
                    } else {
                        $("#memory_css").removeClass().addClass("progress-bar bg-warning");
                        $("#memory").removeClass().addClass("pull-right text-warning");
            }

                    var memory_data_value = (floats(setSize(memory_value, 'mb')) > 1024) ? floats(setSize(memory_value, 'gb')) + "GB" : floats(setSize(memory_value, 'mb')) + "MB";
                    var memory_data_max = (floats(setSize(memory_max, 'mb')) > 1024) ? floats(setSize(memory_max, 'gb')) + "GB" : floats(setSize(memory_max, 'mb')) + "MB";
                    $("#memory_data").html(memory_data_value + " / " + memory_data_max);
                } catch (e) {
                    console.error("Error processing Memory:", e);
                    $("#memory").html('Error');
                }

                try {
                    var diskData = serverInfo.diskUsage || {};
                    var disk_value = diskData.value || 0;
                    var disk_max = diskData.max || 0;
                    var diskPercent = getPercent(disk_value, disk_max, true);
                    var dk = getPercents(disk_value, disk_max, false);

                    $("#disk").html(diskPercent);
                    $("#disk_css").css("width", diskPercent);

                    if (dk < 70) {
                        $("#disk_css").removeClass().addClass("progress-bar bg-success");
                        $("#disk").removeClass().addClass("pull-right text-success");
                    } else if (dk >= 90) {
                        $("#disk_css").removeClass().addClass("progress-bar bg-danger");
                        $("#disk").removeClass().addClass("pull-right text-danger");
                    } else {
                        $("#disk_css").removeClass().addClass("progress-bar bg-warning");
                        $("#disk").removeClass().addClass("pull-right text-warning");
            }

                    var disk_data_value = (floats(setSize(disk_value, 'mb')) > 1024) ? floats(setSize(disk_value, 'gb')) + "GB" : floats(setSize(disk_value, 'mb')) + "MB";
                    var disk_data_max = (floats(setSize(disk_max, 'mb')) > 1024) ? floats(setSize(disk_max, 'gb')) + "GB" : floats(setSize(disk_max, 'mb')) + "MB";
                    $("#disk_data").html(disk_data_value + " / " + disk_data_max);
                } catch (e) { console.error("Error processing Disk:", e); }

                try {
                    var memCachedData = serverStatus.memCached || {};
                    var memCached_value = memCachedData.value || 0;
                    var memCached_max = memCachedData.max || 0;
                    var memCachedPercent = getPercent(memCached_value, memCached_max, true);
                    var mem = getPercents(memCached_value, memCached_max, false);

                    $("#memCached").html(memCachedPercent);
                    $("#memCached_css").css("width", memCachedPercent);

                    if (mem < 70) {
                        $("#memCached_css").removeClass().addClass("progress-bar bg-success");
                        $("#memCached").removeClass().addClass("pull-right text-success");
                    } else if (mem >= 90) {
                        $("#memCached_css").removeClass().addClass("progress-bar bg-danger");
                        $("#memCached").removeClass().addClass("pull-right text-danger");
                    } else {
                        $("#memCached_css").removeClass().addClass("progress-bar bg-warning");
                        $("#memCached").removeClass().addClass("pull-right text-warning");
            }

                    var memCached_data_value = (floats(setSize(memCached_value, 'mb')) > 1024) ? floats(setSize(memCached_value, 'gb')) + "GB" : floats(setSize(memCached_value, 'mb')) + "MB";
                    var memCached_data_max = (floats(setSize(memCached_max, 'mb')) > 1024) ? floats(setSize(memCached_max, 'gb')) + "GB" : floats(setSize(memCached_max, 'mb')) + "MB";
                    $("#memCached_data").html(memCached_data_value + " / " + memCached_data_max);
                } catch (e) { console.error("Error processing MemCached:", e); }

                try {
                    var memBuffersData = serverStatus.memBuffers || {};
                    var memBuffers_value = memBuffersData.value || 0;
                    var memBuffers_max = memBuffersData.max || 0;
                    var memBuffersPercent = getPercent(memBuffers_value, memBuffers_max, true);
                    var memB = getPercents(memBuffers_value, memBuffers_max, false);

                    $("#memBuffers").html(memBuffersPercent);
                    $("#memBuffers_css").css("width", memBuffersPercent);

                    if (memB < 70) {
                        $("#memBuffers_css").removeClass().addClass("progress-bar bg-success");
                        $("#memBuffers").removeClass().addClass("pull-right text-success");
                    } else if (memB >= 90) {
                        $("#memBuffers_css").removeClass().addClass("progress-bar bg-danger");
                        $("#memBuffers").removeClass().addClass("pull-right text-danger");
                    } else {
                        $("#memBuffers_css").removeClass().addClass("progress-bar bg-warning");
                        $("#memBuffers").removeClass().addClass("pull-right text-warning");
            }

                    var memBuffers_data_value = (floats(setSize(memBuffers_value, 'mb')) > 1024) ? floats(setSize(memBuffers_value, 'gb')) + "GB" : floats(setSize(memBuffers_value, 'mb')) + "MB";
                    var memBuffers_data_max = (floats(setSize(memBuffers_max, 'mb')) > 1024) ? floats(setSize(memBuffers_max, 'gb')) + "GB" : floats(setSize(memBuffers_max, 'mb')) + "MB";
                    $("#memBuffers_data").html(memBuffers_data_value + " / " + memBuffers_data_max);
                } catch (e) { console.error("Error processing MemBuffers:", e); }

                try {
                    var sysLoad = serverStatus.sysLoad || [0, 0, 0];
            var state = "";
                    for (var i = 0; i < sysLoad.length; i++) {
                        state += '<span class="badge badge-sm bg-dark">' + sysLoad[i] + '</span> '
            }
            $("#state").html(state);
                    var state_s = getPercent(sysLoad[0], 2, true);
                    var sta = getPercents(sysLoad[0], 2, false);

                    $("#state_css").css("width", state_s);
            $("#state_s").html(state_s);

                    if (sta < 70) {
                        $("#state_css").removeClass().addClass("progress-bar bg-success");
                        $("#state_s").removeClass().addClass("pull-right text-success");
                    } else if (sta >= 90) {
                        $("#state_css").removeClass().addClass("progress-bar bg-danger");
                        $("#state_s").removeClass().addClass("pull-right text-danger");
                    } else {
                        $("#state_css").removeClass().addClass("progress-bar bg-warning");
                        $("#state_s").removeClass().addClass("pull-right text-warning");
                    }
                } catch (e) { console.error("Error processing SysLoad:", e); }
  
                $("#time").html('<span class="badge badge-sm bg-dark">' + data.serverInfo.serverTime + '</span>');

                // 更新动态系统信息
                if (data.serverInfo.sysInfo) {
                    $("#sys_info_badge").html('<span class="badge badge-sm bg-dark">' + data.serverInfo.sysInfo + '</span>');
                }
                if (data.serverInfo.osInfo) {
                    $("#os_info_badge").html('<span class="badge badge-sm bg-dark">' + data.serverInfo.osInfo + '</span>');
                }
  
                $("#u_time").html('<span class="badge badge-sm bg-dark">' + data.serverInfo.serverUptime["days"] + ' 天' + data.serverInfo.serverUptime["hours"] + ' 时 ' + data.serverInfo.serverUptime["mins"] + ' 分' + data.serverInfo.serverUptime["secs"] + ' 秒</span>');

                // Network Stats
                try {
                    var primaryNet = null;
                    var netPriority = ['eth0', 'ens3', 'ens33', 'eno1'];
                    for (var i = 0; i < netPriority.length; i++) {
                        if (networks[netPriority[i]]) {
                            primaryNet = netPriority[i];
                            break;
                        }
                    }
                    if (!primaryNet) {
                        for (var dev in networks) {
                            if (dev !== 'lo' && dev.indexOf('veth') === -1 && dev.indexOf('br-') === -1 && dev.indexOf('docker') === -1) {
                                primaryNet = dev;
                                break;
                            }
                        }
                    }

                    if (primaryNet && networks[primaryNet]) {
                        var net_tx = networks[primaryNet].tx || 0;
                        var net_rx = networks[primaryNet].rx || 0;

                        var aaa_tx = (floats(setSize(net_tx, 'mb')) > 1024) ?
                            floats(setSize(net_tx, 'gb')) + "GB" :
                            floats(setSize(net_tx, 'mb')) + "MB";

                        var aaa_rx = (floats(setSize(net_rx, 'mb')) > 1024) ?
                            floats(setSize(net_rx, 'gb')) + "GB" :
                            floats(setSize(net_rx, 'mb')) + "MB";

  
                        $("#eth").html('<span class="badge badge-sm bg-dark"><i class="fas fa-upload" aria-hidden="true"></i> ' + aaa_tx + '</span> ' +
                            '<span class="badge badge-sm bg-dark"><i class="fas fa-download" aria-hidden="true"></i> ' + aaa_rx + '</span>');

                        if (se_tx === null) {
                            se_tx = net_tx;
                            se_rx = net_rx;
                        }

                        $("#eth1").html('<span class="badge badge-sm bg-dark"><i class="fas fa-cloud-upload-alt" aria-hidden="true"></i> ' + ForDight(net_tx - se_tx) + '</span> ' +
                            '<span class="badge badge-sm bg-dark"><i class="fas fa-cloud-download-alt" aria-hidden="true"></i> ' + ForDight(net_rx - se_rx) + '</span>');
                        se_tx = net_tx;
                        se_rx = net_rx;
                    } else {
                        $("#eth").html('<span class="badge badge-sm bg-danger">N/A</span>');
                        $("#eth1").html('<span class="badge badge-sm bg-danger">N/A</span>');
                    }

                    var lo_data = networks['lo'] || {};
                    var lo_tx_val = lo_data.tx || 0;
                    var lo_rx_val = lo_data.rx || 0;

                    var lo_tx = (floats(setSize(lo_tx_val, 'mb')) > 1024) ?
                        floats(setSize(lo_tx_val, 'gb')) + "GB" :
                        floats(setSize(lo_tx_val, 'mb')) + "MB";

                    var lo_rx = (floats(setSize(lo_rx_val, 'mb')) > 1024) ?
                        floats(setSize(lo_rx_val, 'gb')) + "GB" :
                        floats(setSize(lo_rx_val, 'mb')) + "MB";

                    $("#io").html('<span class="badge badge-sm bg-dark"><i class="fas fa-upload" aria-hidden="true"></i> ' + lo_tx + '</span> ' +
                        '<span class="badge badge-sm bg-dark"><i class="fas fa-download" aria-hidden="true"></i> ' + lo_rx + '</span>');

                    if (si_tx === null) {
                        si_tx = lo_tx_val;
                        si_rx = lo_rx_val;
                    }

                    $("#io1").html('<span class="badge badge-sm bg-dark"><i class="fas fa-cloud-upload-alt" aria-hidden="true"></i> ' + ForDight(lo_tx_val - si_tx) + '</span> ' +
                        '<span class="badge badge-sm bg-dark"><i class="fas fa-cloud-download-alt" aria-hidden="true"></i> ' + ForDight(lo_rx_val - si_rx) + '</span>');
                    si_tx = lo_tx_val;
                    si_rx = lo_rx_val;
                } catch(e) {
                    console.error("Error processing Network:", e);
                    $("#eth, #eth1, #io, #io1").html('<span class="badge badge-sm bg-danger">Error</span>');
                }

                // 计算缓存命中率
                var memTotal = data.serverStatus.memRealUsage['max'];
                var memCached = data.serverStatus.memCached['value'];
                var memBuffers = data.serverStatus.memBuffers['value'];

                // 计算缓存命中率 - 使用缓存和缓冲占总内存的百分比
                var cacheRatio = ((memCached + memBuffers) / memTotal * 100).toFixed(2);

                // 如果是第一次加载,初始化lastCachedValue和lastBuffersValue
                if (lastCachedValue === 0 && lastBuffersValue === 0) {
                    lastCachedValue = memCached;
                    lastBuffersValue = memBuffers;
                    cacheHitRate = cacheRatio; // 初始值
                } else {
                    // 计算缓存命中率变化趋势 (平滑处理)
                    cacheHitRate = (parseFloat(cacheHitRate) * 0.7 + parseFloat(cacheRatio) * 0.3).toFixed(2);
                    if (cacheHitRate < 0) cacheHitRate = 0;
                    if (cacheHitRate > 100) cacheHitRate = 100;
                    lastCachedValue = memCached;
                    lastBuffersValue = memBuffers;
                }

                // 添加缓存命中率信息
                var hitRateHtml = '<span class="badge badge-sm bg-dark">' + cacheHitRate + '%</span>';
                $("#cacheHitRate").html(hitRateHtml);
                $("#cacheHitRate_css").css("width", cacheHitRate + "%");

                // 根据命中率调整颜色
                if (cacheHitRate < 30) {
                    $("#cacheHitRate_css").removeClass();
                    $("#cacheHitRate_css").addClass("progress-bar bg-danger");
                    $("#cacheHitRate").removeClass();
                    $("#cacheHitRate").addClass("pull-right text-danger");
                }
                if (cacheHitRate >= 30 && cacheHitRate < 70) {
                    $("#cacheHitRate_css").removeClass();
                    $("#cacheHitRate_css").addClass("progress-bar bg-warning");
                    $("#cacheHitRate").removeClass();
                    $("#cacheHitRate").addClass("pull-right text-warning");
                }
                if (cacheHitRate >= 70) {
                    $("#cacheHitRate_css").removeClass();
                    $("#cacheHitRate_css").addClass("progress-bar bg-success");
                    $("#cacheHitRate").removeClass();
                    $("#cacheHitRate").addClass("pull-right text-success");
                }
        }
    });
}

    function getNowFormatDate() {
    var date = new Date();
    var seperator1 = "-";
    var seperator2 = ":";
    var month = date.getMonth() + 1;
    var strDate = date.getDate();
    if (month >= 1 && month <= 9) {
      month = "0" + month;
    }
    if (strDate >= 0 && strDate <= 9) {
      strDate = "0" + strDate;
    }
        var currentdate = date.getFullYear() + seperator1 + month + seperator1 + strDate +
            " " + date.getHours() + seperator2 + date.getMinutes() +
            seperator2 + date.getSeconds();
    return currentdate;
}

    function UserInfo() {
    // 先根据UA设置设备和浏览器信息
    var ua = navigator.userAgent;
    var browserInfo = getBrowserInfo(ua);
    var osInfo = getOsInfo(ua);
  
    // 立即显示浏览器和系统信息(不依赖API)
        $("#b").html('<span class="badge badge-sm bg-dark">' + browserInfo + '</span>');
        $("#sys").html('<span class="badge badge-sm bg-dark">' + osInfo + '</span>');
  
    // 使用我们自己的serverInfo.php获取IP信息,避免跨域问题
    $.ajax({
        type: "get",
        url: stateUrl, // 使用之前已定义的stateUrl变量 (指向serverInfo.php)
            data: {
                action: 'getip'
            },
        async: true,
        dataType: "json",
            beforeSend: function() {
            $("#ip").html('<span class="badge badge-sm bg-dark">获取中...</span>');
            $("#address").html('<span class="badge badge-sm bg-dark">获取中...</span>');
        },
            error: function() {
                $("#ip").html('<span class="badge badge-sm bg-dark">' + window.location.hostname + '</span>');
            $("#address").html('<span class="badge badge-sm bg-dark">本地访问</span>');
        },
            success: function(data) {
                if (data && data.ip) {
                    $("#ip").html('<span class="badge badge-sm bg-dark">' + data.ip + '</span>');
  
                    if (data.location) {
                        $("#address").html('<span class="badge badge-sm bg-dark">' + data.location + '</span>');
                } else {
                    $("#address").html('<span class="badge badge-sm bg-dark">本地网络</span>');
                }
            } else {
                    $("#ip").html('<span class="badge badge-sm bg-dark">' + window.location.hostname + '</span>');
                $("#address").html('<span class="badge badge-sm bg-dark">本地访问</span>');
            }
        }
    });
}

// 获取设备系统信息的函数
function getOsInfo(ua) {
        if (!ua) ua = navigator.userAgent;
  
    if (ua.indexOf("Windows NT 10.0") != -1) {
        // 检测Win11
        if (ua.indexOf("Windows NT 10.0; Win64") != -1 && 
            ((ua.indexOf("Chrome/") != -1 && parseInt(ua.split("Chrome/")[1]) >= 90) || 
             (ua.indexOf("Firefox/") != -1 && parseInt(ua.split("Firefox/")[1]) >= 90))) {
            return "Windows 11";
        }
        return "Windows 10";
    }
    if (ua.indexOf("Windows NT 6.3") != -1) return "Windows 8.1";
    if (ua.indexOf("Windows NT 6.2") != -1) return "Windows 8";
    if (ua.indexOf("Windows NT 6.1") != -1) return "Windows 7";
    if (ua.indexOf("Windows NT 6.0") != -1) return "Windows Vista";
    if (ua.indexOf("Windows NT 5.1") != -1) return "Windows XP";
        if (ua.indexOf("Android") != -1) return "Android " + (ua.match(/Android [\d\.]+;/) ? ua.match(/Android [\d\.]+;/)[0].replace(";", "").replace("Android ", "") : "");
    if (ua.indexOf("iPhone") != -1) return "iPhone";
    if (ua.indexOf("iPad") != -1) return "iPad";
    if (ua.indexOf("Mac OS X") != -1) return "macOS";
    if (ua.indexOf("Linux") != -1) return "Linux";
    if (ua.indexOf("CentOS") != -1) return "CentOS";
    if (ua.indexOf("Ubuntu") != -1) return "Ubuntu";
  
    return "未知设备";
}

// 获取浏览器信息
function getBrowserInfo(ua) {
        if (!ua) ua = navigator.userAgent;
  
    // Edge
    if (ua.indexOf("Edg/") > -1) {
        return "Edge " + ua.match(/Edg\/([\d.]+)/)[1].split('.')[0];
    }
    // Chrome
    else if (ua.indexOf("Chrome/") > -1 && ua.indexOf("Safari/") > -1 && ua.indexOf("OPR/") == -1) {
        return "Chrome " + ua.match(/Chrome\/([\d.]+)/)[1].split('.')[0];
    }
    // Firefox
    else if (ua.indexOf("Firefox/") > -1) {
        return "Firefox " + ua.match(/Firefox\/([\d.]+)/)[1].split('.')[0];
    }
    // Opera
    else if (ua.indexOf("OPR/") > -1 || ua.indexOf("Opera/") > -1) {
        return "Opera " + (ua.match(/OPR\/([\d.]+)/) || ua.match(/Opera\/([\d.]+)/))[1].split('.')[0];
    }
    // Safari
    else if (ua.indexOf("Safari/") > -1 && ua.indexOf("Chrome/") == -1) {
        return "Safari " + (ua.match(/Version\/([\d.]+)/) ? ua.match(/Version\/([\d.]+)/)[1].split('.')[0] : "");
    }
    // 其他浏览器
    else {
        return "浏览器 " + (navigator.appVersion ? navigator.appVersion.split(" ")[0] : "");
    }
}

    $('#StateData').click(function() {
    clearInterval(window.getnet);
    clearInterval(window.info);

        function updateAll() {
            if ($('#StateDataPos').is('.open')) {
            state();
                $("#sys_times").html('<span class="badge badge-sm bg-dark">' + getNowFormatDate() + '</span>');
        }
        }

        // Call immediately after a short delay to allow dropdown to open
        setTimeout(updateAll, 50);

        window.getnet = setInterval(updateAll, 1000);

        if (!window.userInfoLoaded) {
    UserInfo();
            window.userInfoLoaded = true;
        }
});
</script>
<!-- 新追加的内容到此结束 -->

[/zd-plane]

4. 创建 serverInfo.php 文件

在网站根目录创建 serverInfo.php 文件,内容如下:

<?php

header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-cache, must-revalidate');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');

/**
 * Tries to get file contents, using file_get_contents and falling back to shell_exec('cat').
 * This can help in environments with restrictions like open_basedir.
 * @param string $file The path to the file.
 * @return string|false The file contents or false on failure.
 */
function GetContents($file)
{
    if (!@is_readable($file)) {
        return false;
    }
    $content = @file_get_contents($file);
    if ($content !== false) {
        return $content;
    }
    if (function_exists('shell_exec') && false === strpos(ini_get('disable_functions'), 'shell_exec')) {
        $output = shell_exec('cat ' . escapeshellarg($file));
        if ($output) {
            return $output;
        }
    }
    return false;
}

function size_format($bytes, $decimals = 2)
{
    $size = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
    $factor = floor((strlen($bytes) - 1) / 3);
    return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . $size[$factor];
}

function netSize($bytes)
{
    if ($bytes < 1024) return $bytes . ' B';
    if ($bytes < 1048576) return round($bytes / 1024, 2) . ' KB';
    if ($bytes < 1073741824) return round($bytes / 1048576, 2) . ' MB';
    return round($bytes / 1073741824, 2) . ' GB';
}

/**
 * 获取当前Unix时间戳
 * */
$unixTimestamp = time();
/**
 * 获取服务器总的运行时长
 * */
$serverUptime =  getUpTime();
/**
 * 获取服务器负载 以及CPU使用信息
 * */
$serverLoad = GetLoad();
$cpuUsage = GetCPUInfo();
/**
 * 获取服务器内存信息
 * */
$memoryInfo = GetMem();

// 处理IP信息请求
if (isset($_GET['action']) && $_GET['action'] == 'getip') {
    // 获取用户真实IP地址
    $ip = '';
    // 检查各种可能的代理头部
    $headers = [
        'HTTP_CF_CONNECTING_IP', // Cloudflare
        'HTTP_X_FORWARDED_FOR',  // 通用代理和CDN
        'HTTP_X_REAL_IP',        // Nginx代理
        'HTTP_CLIENT_IP',        // 客户端代理
        'HTTP_X_FORWARDED',      // 通用
        'HTTP_FORWARDED_FOR',    // 通用
        'HTTP_FORWARDED',        // 通用
        'HTTP_X_CLUSTER_CLIENT_IP', // 集群
        'HTTP_TRUE_CLIENT_IP',   // Akamai和Cloudflare
        'HTTP_X_CHAITIN_IP',     // 长亭雷池可能使用的头部
        'REMOTE_ADDR'            // 直接连接
    ];

    // 记录所有可能的IP来源(调试用)
    $debug_headers = [];
    foreach ($headers as $header) {
        if (!empty($_SERVER[$header])) {
            $debug_headers[$header] = $_SERVER[$header];
        }
    }

    // 尝试获取真实IP
    foreach ($headers as $header) {
        if (!empty($_SERVER[$header])) {
            $ip_array = explode(',', $_SERVER[$header]);
            $ip = trim($ip_array[0]); // 第一个IP通常是客户端真实IP
            break;
        }
    }

    // 简单过滤掉非法IP
    $ip = filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '未知';

    // 获取地理位置信息(只对公网IP有效)
    $location = '';
    // 检查是否是内网IP
    if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
        $location = '内网IP: ' . $ip; // 显示具体的内网IP地址
    } else {
        // 方法1:使用太平洋IP库(覆盖较全面,国内外都支持)
        $url = "http://whois.pconline.com.cn/ipJson.jsp?ip={$ip}&json=true";
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 3);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36');
        curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate');
        $response = curl_exec($ch);
        $errno = curl_errno($ch);
        curl_close($ch);

        if ($response && !$errno) {
            // 太平洋的接口返回的是GB2312编码,需要转换
            $response = mb_convert_encoding($response, 'UTF-8', 'GBK');
            $data = json_decode($response, true);
            if ($data && isset($data['pro']) && isset($data['city'])) {
                $region = '';
                if (!empty($data['pro'])) {
                    $region .= $data['pro'];
                }
                if (!empty($data['city']) && $data['city'] != $data['pro']) {
                    $region .= ' ' . $data['city'];
                }
                // 根据IP段判断运营商
                $carrier = judgeCarrier($ip);
                if ($carrier) {
                    $region .= ' ' . $carrier;
                }
                $location = $region ? $region : '未知区域';
            }
        }

        // 方法2:使用IpInfo API(国外IP支持较好)
        if (empty($location)) {
            $url = "https://ipinfo.io/{$ip}/json";
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 3);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36');
            $response = curl_exec($ch);
            curl_close($ch);

            if ($response) {
                $data = json_decode($response, true);
                if ($data && isset($data['region']) && isset($data['city'])) {
                    $region = '';
                    if (!empty($data['country'])) {
                        $region .= getCountryName($data['country']);
                    }
                    if (!empty($data['region'])) {
                        $region .= ' ' . $data['region'];
                    }
                    if (!empty($data['city'])) {
                        $region .= ' ' . $data['city'];
                    }
                    if (!empty($data['org'])) {
                        $region .= ' ' . preg_replace('/^AS\d+\s+/', '', $data['org']);
                    } else {
                        // 补充运营商信息
                        $carrier = judgeCarrier($ip);
                        if ($carrier) {
                            $region .= ' ' . $carrier;
                        }
                    }
                    $location = $region ? $region : '未知区域';
                }
            }
        }

        // 方法3:使用IPIP.net的免费接口(国内移动网络支持较好)
        if (empty($location)) {
            $url = "https://freeapi.ipip.net/{$ip}";
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 3);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36');
            $response = curl_exec($ch);
            curl_close($ch);

            if ($response && $response != '{}') {
                $data = json_decode($response, true);
                if (is_array($data) && count($data) >= 4) {
                    // IPIP.net返回格式:["中国","福建","厦门","","移动"]
                    $region = '';
                    if (!empty($data[1])) {
                        $region .= $data[1]; // 省份
                    }
                    if (!empty($data[2]) && $data[2] != $data[1]) {
                        $region .= ' ' . $data[2]; // 城市
                    }
                    if (!empty($data[4])) {
                        $region .= ' ' . $data[4]; // 运营商
                    } else {
                        // 补充运营商信息
                        $carrier = judgeCarrier($ip);
                        if ($carrier) {
                            $region .= ' ' . $carrier;
                        }
                    }
                    $location = $region ? $region : '未知区域';
                }
            }
        }

        // 方法4:使用淘宝IP库(作为备用)
        if (empty($location)) {
            $url = "https://ip.taobao.com/outGetIpInfo?ip=" . $ip . "&accessKey=alibaba-inc";
            $opts = array(
                'http' => array(
                    'method' => "GET",
                    'timeout' => 3,
                )
            );
            $context = stream_context_create($opts);
            $response = @file_get_contents($url, false, $context);

            if ($response !== false) {
                $data = json_decode($response, true);
                if (isset($data['data']) && $data['data']) {
                    $result = $data['data'];
                    $region = '';
                    if (!empty($result['region'])) {
                        $region .= $result['region'];
                    }
                    if (!empty($result['city']) && $result['city'] != $result['region']) {
                        $region .= ' ' . $result['city'];
                    }
                    if (!empty($result['isp'])) {
                        $region .= ' ' . $result['isp'];
                    } else {
                        // 补充运营商信息
                        $carrier = judgeCarrier($ip);
                        if ($carrier) {
                            $region .= ' ' . $carrier;
                        }
                    }
                    $location = $region ? $region : '未知区域';
                }
            }
        }

        // 方法5:极速数据网络IP库(移动网络特别友好)
        if (empty($location)) {
            $url = "http://ip.jisuapi.com/api/ip/geo?ip={$ip}";
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 3);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            $response = curl_exec($ch);
            curl_close($ch);

            if ($response) {
                $data = json_decode($response, true);
                if (isset($data['result']) && $data['result']) {
                    $result = $data['result'];
                    $region = '';
                    if (!empty($result['province'])) {
                        $region .= $result['province'];
                    }
                    if (!empty($result['city']) && $result['city'] != $result['province']) {
                        $region .= ' ' . $result['city'];
                    }
                    if (!empty($result['isp'])) {
                        $region .= ' ' . $result['isp'];
                    } else {
                        // 补充运营商信息
                        $carrier = judgeCarrier($ip);
                        if ($carrier) {
                            $region .= ' ' . $carrier;
                        }
                    }
                    $location = $region ? $region : '未知区域';
                }
            }
        }

        // 方法6:仅使用IP段进行精准匹配(最后保障)
        if (empty($location)) {
            // 根据IP段匹配位置信息
            $ipLocation = getIpLocation($ip);
            if ($ipLocation) {
                $location = $ipLocation;
            } else {
                // 至少返回运营商信息
                $carrier = judgeCarrier($ip);
                if ($carrier) {
                    $location = $carrier;
                } else {
                    $location = "公网IP";
                }
            }
        }
    }

    // 返回IP信息
    echo json_encode([
        'ip' => $ip,
        'location' => $location,
        'debug' => $debug_headers // 添加调试信息
    ]);
    exit;
}

/**
 * 根据IP地址判断归属运营商
 * @param string $ip IP地址
 * @return string 运营商名称
 */
function judgeCarrier($ip)
{
    // 特殊IP段映射
    $special_ip_carriers = [
        // 移动特殊段
        '39.128.' => '中国移动',
        '39.129.' => '中国移动',
        '39.130.' => '中国移动',
        '39.131.' => '中国移动',
        '39.132.' => '中国移动',
        '39.133.' => '中国移动',
        '39.134.' => '中国移动',
        '39.135.' => '中国移动',
        '39.136.' => '中国移动',
        '39.137.' => '中国移动',
        '39.138.' => '中国移动',
        '39.139.' => '中国移动',
        '39.140.' => '中国移动',
        '39.141.' => '中国移动',
        '39.142.' => '中国移动',
        '39.143.' => '中国移动',
        '39.144.' => '中国移动',
        '39.145.' => '中国移动',
        '39.146.' => '中国移动',
        '39.147.' => '中国移动',
        '39.148.' => '中国移动',
        '39.149.' => '中国移动',
        '39.150.' => '中国移动',
        '39.151.' => '中国移动',
        '39.152.' => '中国移动',
        '39.153.' => '中国移动',
        '39.154.' => '中国移动',
        '39.155.' => '中国移动',
        '39.156.' => '中国移动',
        '39.157.' => '中国移动',
        '39.158.' => '中国移动',
        '39.159.' => '中国移动',
        '39.176.' => '中国移动',
        '40.128.' => '中国移动',
        '40.129.' => '中国移动',
        '40.130.' => '中国移动',
        '41.128.' => '中国移动',
        '42.128.' => '中国移动',
        '43.128.' => '中国移动',

        // 联通特殊段
        '44.128.' => '中国联通',
        '45.128.' => '中国联通',
        '46.128.' => '中国联通',

        // 电信特殊段
        '48.128.' => '中国电信',
        '49.128.' => '中国电信',
        '50.128.' => '中国电信',
        '51.128.' => '中国电信',
    ];

    // 检查特殊IP段映射
    foreach ($special_ip_carriers as $prefix => $carrier) {
        if (strpos($ip, $prefix) === 0) {
            return $carrier;
        }
    }

    // 移动
    $china_mobile = [
        '39',
        '40',
        '41',
        '42',
        '43', // 新的移动段
        '47.0',
        '47.1',
        '47.2',
        '47.3', // 兼容部分联通和移动混用
        '178.',
        '180.',
        '182.',
        '183.',
        '184.',
        '157.',
        '158.',
        '159.',
        '165.',
        '172.',
        '120.204.',
        '120.205.',
        '120.206.',
        '120.207.',
        '120.208.',
        '120.209.',
        '120.210.',
        '120.211.',
        '120.212.',
        '120.213.',
        '120.214.',
        '120.215.',
        '221.130.',
        '221.131.',
        '221.132.',
        '221.133.',
        '221.134.',
        '221.135.',
        '221.136.',
        '221.137.',
        '221.138.',
        '221.139.',
        '211.103.',
        '211.137.',
    ];

    // 联通
    $china_unicom = [
        '44',
        '45',
        '46', // 新的联通段
        '47.4',
        '47.5',
        '47.6',
        '47.7',
        '47.8',
        '47.9', // 兼容部分联通和移动混用
        '130.',
        '131.',
        '132.',
        '155.',
        '156.',
        '186.',
        '145.',
        '146.',
        '166.',
        '175.',
        '171.',
        '175.',
        '176.',
        '185.',
        '186.',
        '166.',
        '120.64.',
        '120.65.',
        '120.66.',
        '120.67.',
        '120.68.',
        '120.69.',
        '120.70.',
        '120.71.',
        '120.72.',
        '120.73.',
        '120.74.',
        '120.75.',
        '120.76.',
        '120.77.',
        '120.78.',
        '120.79.',
        '120.80.',
        '120.81.',
        '121.76.',
        '121.77.',
        '121.78.',
        '121.79.',
        '121.80.',
        '121.81.',
        '218.100.',
        '218.104.',
        '218.108.',
        '211.90.',
        '211.91.',
        '211.92.',
        '211.93.',
        '211.94.',
        '211.95.',
        '211.96.',
        '211.97.',
        '211.98.',
        '211.99.',
        '211.100.',
        '211.101.',
        '211.102.',
    ];

    // 电信
    $china_telecom = [
        '48',
        '49',
        '50',
        '51', // 新的电信段
        '133.',
        '153.',
        '173.',
        '177.',
        '180.',
        '181.',
        '189.',
        '199.',
        '120.128.',
        '120.129.',
        '120.130.',
        '120.131.',
        '120.132.',
        '120.133.',
        '120.134.',
        '120.135.',
        '120.136.',
        '120.137.',
        '120.138.',
        '120.139.',
        '120.140.',
        '120.141.',
        '120.142.',
        '120.143.',
        '120.144.',
        '120.145.',
        '113.64.',
        '113.65.',
        '113.66.',
        '113.67.',
        '113.68.',
        '113.69.',
        '113.70.',
        '113.71.',
        '113.72.',
        '113.73.',
        '113.74.',
        '113.75.',
        '125.64.',
        '125.65.',
        '125.66.',
        '125.67.',
        '125.68.',
        '125.69.',
        '125.70.',
        '125.71.',
        '125.72.',
        '125.73.',
        '125.74.',
        '125.75.',
        '210.5.',
        '210.12.',
        '210.14.',
        '210.21.',
        '210.32.',
        '210.51.',
        '210.52.',
        '210.77.',
        '210.192.',
    ];

    // 铁通/广电/其他
    $china_other = [
        '36',
        '37',
        '38', // 其他杂段
        '1700',
        '1705',
        '1709', // 虚拟运营商
    ];

    // 特定省份IP段
    $province_ip_map = [
        // 河南移动IP段范围
        '39.144.25' => '河南 南阳 中国移动',
        '39.144.26' => '河南 南阳 中国移动',
        '39.144.27' => '河南 南阳 中国移动',

        // 福建电信IP段范围
        '120.32.2' => '福建 厦门 中国电信',
        '120.32.3' => '福建 厦门 中国电信',
        '120.32.4' => '福建 厦门 中国电信',
    ];

    // 检查特定省份IP段映射
    foreach ($province_ip_map as $prefix => $location) {
        if (strpos($ip, $prefix) === 0) {
            return $location;
        }
    }

    foreach ($china_mobile as $prefix) {
        if (strpos($ip, $prefix) === 0) {
            return '中国移动';
        }
    }

    foreach ($china_unicom as $prefix) {
        if (strpos($ip, $prefix) === 0) {
            return '中国联通';
        }
    }

    foreach ($china_telecom as $prefix) {
        if (strpos($ip, $prefix) === 0) {
            return '中国电信';
        }
    }

    foreach ($china_other as $prefix) {
        if (strpos($ip, $prefix) === 0) {
            return '其他运营商';
        }
    }

    return '';
}

/**
 * 根据IP前缀识别地理位置
 * @param string $ip IP地址
 * @return string 地理位置
 */
function getIpLocation($ip)
{
    // 特定地区IP段映射表
    $ip_location_map = [
        // 河南移动
        '39.144.2' => '河南 中国移动',
        '39.144.25' => '河南 南阳 中国移动',
        '39.144.26' => '河南 南阳 中国移动',

        // 福建电信
        '120.32.2' => '福建 厦门 中国电信',
        '120.32.3' => '福建 厦门 中国电信',

        // 北京联通
        '111.200.' => '北京 中国联通',
        '111.201.' => '北京 中国联通',

        // 上海电信
        '180.166.' => '上海 中国电信',
        '180.167.' => '上海 中国电信',

        // 广东移动
        '120.231.' => '广东 中国移动',
        '120.232.' => '广东 中国移动',

        // 浙江电信
        '115.192.' => '浙江 杭州 中国电信',
        '115.193.' => '浙江 杭州 中国电信',
    ];

    // 逐段检查,从最长前缀到最短
    $segments = explode('.', $ip);

    // 检查前三段 (如 192.168.1)
    if (count($segments) >= 3) {
        $prefix3 = $segments[0] . '.' . $segments[1] . '.' . $segments[2];
        if (isset($ip_location_map[$prefix3])) {
            return $ip_location_map[$prefix3];
        }
    }

    // 检查前两段 (如 192.168)
    if (count($segments) >= 2) {
        $prefix2 = $segments[0] . '.' . $segments[1];
        if (isset($ip_location_map[$prefix2])) {
            return $ip_location_map[$prefix2];
        }
    }

    // 检查第一段 (如 192)
    if (count($segments) >= 1) {
        $prefix1 = $segments[0];
        if (isset($ip_location_map[$prefix1])) {
            return $ip_location_map[$prefix1];
        }
    }

    return '';
}

/**
 * 获取国家名称(将ISO代码转为中文名称)
 * @param string $code 国家代码
 * @return string 国家名称
 */
function getCountryName($code)
{
    $countries = [
        'CN' => '中国',
        'US' => '美国',
        'JP' => '日本',
        'KR' => '韩国',
        'GB' => '英国',
        'DE' => '德国',
        'FR' => '法国',
        'RU' => '俄罗斯',
        'CA' => '加拿大',
        'AU' => '澳大利亚',
        'HK' => '香港',
        'TW' => '台湾',
    ];

    return isset($countries[$code]) ? $countries[$code] : $code;
}

/**
 * 获取详细的操作系统信息
 * @return string
 */
function getDetailedOsInfo()
{
    if (stristr(PHP_OS, 'WIN')) {
        return php_uname('s') . ' ' . php_uname('r');
    }
    if (@is_readable('/etc/os-release')) {
        $vars = parse_ini_file('/etc/os-release');
        if (!empty($vars['PRETTY_NAME'])) return $vars['PRETTY_NAME'];
        if (!empty($vars['NAME'])) return $vars['NAME'] . (isset($vars['VERSION']) ? ' ' . $vars['VERSION'] : '');
    }
    return php_uname('s') . ' ' . php_uname('r');
}

// 定义要输出的内容  
$serverInfo = array(
    'serverTime' => date('Y-m-d H:i:s', $unixTimestamp),
    'serverUptime' => array(
        'days' => $serverUptime['days'],
        'hours' => $serverUptime['hours'],
        'mins' => $serverUptime['mins'],
        'secs' => $serverUptime['secs']
    ),
    'serverUtcTime' => date('Y/m/d H:i:s', $unixTimestamp),
    'diskUsage' => array(
        'value' => disk_total_space(__FILE__) - disk_free_space(__FILE__),
        'max' => disk_total_space(__FILE__)
    ),
    'sysInfo' => php_uname('s') . ' ' . php_uname('r'),
    'osInfo' => getDetailedOsInfo(), // This is new
);

$serverStatus = array(
    'sysLoad' => array($serverLoad['1m'], $serverLoad['5m'], $serverLoad['15m']),
    'cpuUsage' => array(
        'user' => $cpuUsage['user'],
        'nice' => $cpuUsage['nice'],
        'sys' => $cpuUsage['sys'],
        'idle' => $cpuUsage['idle']
    ),
    'memRealUsage' => array(
        'value' => $memoryInfo['mRealUsed'],
        'max' => $memoryInfo['mTotal']
    ),
    'memBuffers' => array(
        'value' => $memoryInfo['mBuffers'],
        'max' => $memoryInfo['mTotal']
    ),
    'memCached' => array(
        'value' => $memoryInfo['mCached'],
        'max' => $memoryInfo['mTotal']
    ),
    'swapUsage' => array(
        'value' => $memoryInfo['swapUsed'],
        'max' => $memoryInfo['swapTotal']
    ),
    'swapCached' => array(
        'value' => $memoryInfo['swapCached'],
        'max' => $memoryInfo['swapTotal']
    )
);

$networkStats = array(
    'networks' => GetNetwork()
);
// 将以上内容合并为一个数组  
$output = array(
    'serverInfo' => $serverInfo,
    'serverStatus' => $serverStatus,
    'networkStats' => $networkStats
);

// 将数组转换为JSON字符串并输出  
echo json_encode($output);

/**
 * 获取系统运行时长
 *
 * @return array
 */
function getUpTime()
{
    $uptime_str = GetContents("/proc/uptime");
    if (false === $uptime_str) {
        return array('days' => 0, 'hours' => 0, 'mins' => 0, 'secs' => 0);
    }
    $uptime = (float)$uptime_str;
    $days = floor($uptime / 86400);
    $hours = floor(($uptime % 86400) / 3600);
    $minutes = floor((($uptime % 86400) % 3600) / 60);
    $seconds = floor(($uptime % 3600) % 60);
    return array(
        'days' => $days,
        'hours' => $hours,
        'mins' => $minutes,
        'secs' => $seconds
    );
}

/**
 * 获取系统负载
 *
 * @return array|false|string[]
 */
function GetLoad()
{
    $str = GetContents("/proc/loadavg");
    if (false === $str) {
        return ['1m' => 0, '5m' => 0, '15m' => 0];
    }
    $loads = explode(' ', $str);
    if (count($loads) >= 3) {
        return [
            '1m'  => $loads[0],
            '5m'  => $loads[1],
            '15m' => $loads[2],
        ];
    }
    return ['1m' => 0, '5m' => 0, '15m' => 0];
}

function GetCPUInfo()
{
    $load = sys_getloadavg();
    $user = $load[0];
    $nice = $load[1];
    $sys = $load[2];
    $idle = 100 - ($user + $nice + $sys);
    return [
        'user' => $user,
        'nice' => $nice,
        'sys' => $sys,
        'idle' => $idle,
    ];
}

/**
 * 内存信息
 *
 * @param bool $bFormat 格式化
 *
 * @return array
 */
function GetMem(bool $bFormat = false)
{
    $defaults = [
        'mTotal' => 0,
        'mFree' => 0,
        'mBuffers' => 0,
        'mCached' => 0,
        'mUsed' => 0,
        'mPercent' => 0,
        'mRealUsed' => 0,
        'mRealFree' => 0,
        'mRealPercent' => 0,
        'mCachedPercent' => 0,
        'swapTotal' => 0,
        'swapFree' => 0,
        'swapUsed' => 0,
        'swapPercent' => 0,
        'swapCached' => 0
    ];

    $str = GetContents("/proc/meminfo");
    if (false === $str) {
        return $defaults;
    }

    preg_match_all("/MemTotal\s{0,}\:+\s{0,}([\d\.]+).+?MemFree\s{0,}\:+\s{0,}([\d\.]+).+?Cached\s{0,}\:+\s{0,}([\d\.]+).+?SwapTotal\s{0,}\:+\s{0,}([\d\.]+).+?SwapFree\s{0,}\:+\s{0,}([\d\.]+)/s", $str, $mems);
    preg_match_all("/Buffers\s{0,}\:+\s{0,}([\d\.]+)/s", $str, $buffers);
    preg_match("/SwapCached\s{0,}\:+\s{0,}([\d\.]+)/s", $str, $swap_cached_match);

    if (!isset($mems[1][0], $mems[2][0], $mems[3][0], $mems[4][0], $mems[5][0], $buffers[1][0])) {
        return $defaults;
    }

    $mtotal    = $mems[1][0] * 1024;
    $mfree     = $mems[2][0] * 1024;
    $mbuffers  = $buffers[1][0] * 1024;
    $mcached   = $mems[3][0] * 1024;
    $stotal    = $mems[4][0] * 1024;
    $sfree     = $mems[5][0] * 1024;
    $swap_cached = !empty($swap_cached_match[1]) ? $swap_cached_match[1] * 1024 : 0;

    $mused     = $mtotal - $mfree;
    $sused     = $stotal - $sfree;
    $mrealused = $mtotal - $mfree - $mcached - $mbuffers;

    $rtn['mTotal']         = !$bFormat ? $mtotal : size_format($mtotal, 1);
    $rtn['mFree']          = !$bFormat ? $mfree : size_format($mfree, 1);
    $rtn['mBuffers']       = !$bFormat ? $mbuffers : size_format($mbuffers, 1);
    $rtn['mCached']        = !$bFormat ? $mcached : size_format($mcached, 1);
    $rtn['mUsed']          = !$bFormat ? ($mtotal - $mfree) : size_format($mtotal - $mfree, 1);
    $rtn['mPercent']       = (floatval($mtotal) != 0) ? round($mused / $mtotal * 100, 1) : 0;
    $rtn['mRealUsed']      = !$bFormat ? $mrealused : size_format($mrealused, 1);
    $rtn['mRealFree']      = !$bFormat ? ($mtotal - $mrealused) : size_format($mtotal - $mrealused, 1);
    $rtn['mRealPercent']   = (floatval($mtotal) != 0) ? round($mrealused / $mtotal * 100, 1) : 0;
    $rtn['mCachedPercent'] = (floatval($mcached) != 0) ? round($mcached / $mtotal * 100, 1) : 0;
    $rtn['swapTotal']      = !$bFormat ? $stotal : size_format($stotal, 1);
    $rtn['swapFree']       = !$bFormat ? $sfree : size_format($sfree, 1);
    $rtn['swapUsed']       = !$bFormat ? $sused : size_format($sused, 1);
    $rtn['swapPercent']    = (floatval($stotal) != 0) ? round($sused / $stotal * 100, 1) : 0;
    $rtn['swapCached']     = !$bFormat ? $swap_cached : size_format($swap_cached, 1);
    return $rtn;
}

/**
 * 获取网络数据
 *
 * @param bool $bFormat
 *
 * @return array
 */
function GetNetwork(bool $bFormat = false)
{
    $rtn     = [];
    $netstat = GetContents('/proc/net/dev');
    if (false === $netstat) {
        return [];
    }

    $bufe = preg_split("/\n/", $netstat, -1, PREG_SPLIT_NO_EMPTY);
    foreach ($bufe as $buf) {
        if (preg_match('/:/', $buf)) {
            list($dev_name, $stats_list) = preg_split('/:/', $buf, 2);
            $dev_name = trim($dev_name);

            $stats = preg_split('/\s+/', trim($stats_list));
            if (count($stats) < 10) {
                continue;
            }

            $rtn[$dev_name]['name']       = $dev_name;
            $rtn[$dev_name]['rx']    = !$bFormat ? $stats[0] : netSize($stats[0]);
            $rtn[$dev_name]['in_packets'] = $stats[1];
            $rtn[$dev_name]['in_errors']  = $stats[2];
            $rtn[$dev_name]['in_drop']    = $stats[3];

            $rtn[$dev_name]['tx'] = !$bFormat ? $stats[8] : netSize($stats[8]);
            $rtn[$dev_name]['out_packets'] = $stats[9];
            $rtn[$dev_name]['out_errors']  = $stats[10] ?? 0;
            $rtn[$dev_name]['out_drop']    = $stats[11] ?? 0;
        }
    }

    return $rtn;
}

使用宝塔面板配置说明

若使用宝塔面板环境,请修改网站根目录中的 .user.ini 文件,在 open_basedir 后面追加 :/proc/

open_basedir=/www/wwwroot/your.domain/:/tmp/:/proc/

PHP安全设置调整(可选)

在Debian系统上,需要确保PHP可以执行shell命令。修改php.ini文件:

  1. 找到 disable_functions 行,确保 shell_execexecsystem 未被禁用
  2. 如果在该行找到这些函数,请删除它们(用逗号分隔)

Apache配置(可选)

如果使用Apache服务器,可能需要修改配置允许执行系统命令:

  1. 编辑网站的虚拟主机配置文件或 .htaccess 文件
  2. 添加以下内容:

<Directory /var/www/html>
    Options +ExecCGI
</Directory>

常见问题排查

如果遇到问题,可以尝试以下排查步骤:

  1. 检查PHP是否有权限执行shell命令cd /path/to/your/website php -r "echo shell_exec('whoami');"如果返回用户名,说明PHP有权限执行shell命令
  2. 检查是否可以访问/proc目录php -r "var_dump(file_get_contents('/proc/uptime'));"如果返回内容而不是false或错误,说明可以访问/proc目录
  3. 检查必要的命令是否可用which top free ifconfig ip确保这些命令都可用
  4. CPU核心数默认是8核,可在headnav.php中修改
  5. 数据加载缓慢时尝试调整PHP版本为8.0并按照上述教程解禁函数,然后重启服务器

如有其他问题,欢迎在评论区留言

简介

自从iOS 26 的液态玻璃设计火了之后,就一直想把手底下的网站改一改样式,思来想去只有网盘最合适,刚好用的就是openlist。于是就修改为玻璃拟态主题,并加入了视频背景和自定义底部内容。

特点

  • 玻璃拟态效果:所有 UI 元素采用高级玻璃质感设计
  • 动态视频背景:支持响应式视频背景(桌面/移动端自适应)
  • SVG 滤镜特效:使用高级 SVG 滤镜实现液体玻璃效果
  • 暗色模式支持:完美适配 Hope UI 暗色模式
  • 响应式设计:全面适配桌面和移动设备
  • 优雅动画:平滑过渡和悬停效果

详细步骤

  1. 登录 OpenList/AList 管理后台
  2. 进入「设置」→「全局」→「自定义头部」将以下内容添加到对应位置

<style type="text/css">
body {
  /* 手机端背景图 */
  --mobile-background-image: url(https://csblog.chario.cn/usr/uploads/2026/03/哈妮克孜05-scaled-1.jpg;
  /* 电脑端背景图 */
  --desktop-background-image: url(https://csblog.chario.cn/usr/uploads/2026/03/哈妮克孜10-scaled-1.jpg;
}
.hope-ui-dark .markdown-body a{color:var(--hope-colors-loContrast) !important}html ::selection{background-color:var(--hope-colors-accent8);color:var(--hope-colors-loContrast)}body{background-image:var(--desktop-background-image) !important;background-repeat:no-repeat !important;background-size:cover !important;background-attachment:fixed !important;background-position-x:center !important}@media screen and (max-width: 960px){body{background-image:var(--mobile-background-image) !important}}.video-container{position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;overflow:hidden}.video-container video{min-width:100%;min-height:100%;width:auto;height:auto;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);object-fit:cover}.hope-c-PJLV-idaeksS-css,.hope-c-PJLV-ikaMhsQ-css{background:none !important}.hope-c-PJLV-idaeksS-css{border-radius:var(--hope-radii-full);overflow:hidden;padding:var(--hope-space-1) var(--hope-space-2)}.hope-c-khZXrc{display:inline-block !important}.hope-c-cPYwgm{text-overflow:ellipsis;display:-webkit-box !important;-webkit-line-clamp:1;-webkit-box-orient:vertical;white-space:normal;word-break:break-all}.hope-c-iIOWzi{padding:0 var(--hope-space-1) !important}.hope-c-PJLV-igScBhH-css,.hope-c-hOYTCS,.hope-c-PJLV-idaeksS-css,.hope-c-PJLV-ieTGfmR-css,.header-right.hope-stack>div,.hope-c-PJLV-ijgzmFG-css,.hope-c-PJLV-ikJQsXT-css,.hope-c-zbPwS,.hope-c-XJURY,.hope-c-ivMHWx-fbcPgu-cv,.hope-c-ivMHWx-dvmlqS-cv,.hope-c-ivMHWx-dMllzy-cv,.hope-c-cFbQhW,.hope-c-ivMHWx-knrFJ-cv,.hope-c-ivMHWx-gHYUvy-cv,.hope-c-PJLV-iigjoxS-css,.solid-contextmenu,.hope-c-PJLV-idusLCn-css,.hope-c-ivMHWx-eHkSxq-cv,.hope-c-kvTTWD-hYRNAb-variant-filled,.hope-c-ivMHWx:disabled,.hope-c-mHASU-byiOue-variant-filled,.hope-c-mHASU-kukbfD-variant-outline,.hope-c-PJLV-ihVEsOa-css,.hope-c-gIqhpo,.hope-c-PJLV-idMNcPR-css,.hope-c-PJLV-ieGWMbI-css,.hope-c-PJLV-iZZmce-css,.hope-c-PJLV-ibcBsNO-css .hope-c-PJLV-iubUra-css,.hope-c-PJLV-ibcBsNO-css .hope-c-PJLV-ifjOQLV-css,.hope-c-cIFneQ,.hope-c-PJLV-ikAHMBe-css{background-color:transparent !important;box-shadow:inset 1px 1px 0px 0px rgba(255,255,255,0.5),inset -1px -1px 0px 0px rgba(255,255,255,0.6) !important;cursor:grab;backdrop-filter:url(#liquid-glass-filter) blur(20px) saturate(1.4) !important;pointer-events:auto;border:none !important}.header-right.hope-stack>div{height:var(--hope-sizes-8);border:none !important;padding:var(--hope-space-1) var(--hope-space-2) !important}.hope-c-PJLV-ijSQbqe-css{background-color:var(--hope-colors-blackAlpha3) !important;border-color:rgba(234,234,234,0.5) !important}.hope-c-PJLV-iciJSBF-css{opacity:0.5}.markdown-body .highlight pre,.markdown-body pre{background-color:var(--hope-colors-blackAlpha3) !important;backdrop-filter:blur(var(--hope-space-3))}.hope-c-gUeVCo[data-active],.hope-c-kRwRnM-gSazcJ-colorScheme-neutral[data-active]{background-color:var(--hope-colors-blackAlpha3) !important}.hope-c-ctSAUo,.hope-c-bICGYT-dWksIc-scrollBehavior-outside{background-color:var(--hope-colors-blackAlpha6) !important;backdrop-filter:blur(8px)}.footer{opacity:0;transition:opacity .3s ease-in-out}.copyright{font-size:14px;text-align:center}.copyright .copyright-links{display:flex;align-items:center;justify-content:center;gap:var(--hope-space-2)}.copyright .copyright-desc{line-height:2}.copyright a{transition:color .3s ease-in-out}.copyright a:hover{color:var(--hope-colors-accent8)}.hope-c-PJLV-ihWgyFw-css,.hope-c-PJLV-ibiABng-css{display:none !important}
</style>

  1. 进入「设置」→「全局」→「自定义内容」将以下内容添加到对应位置

<!-- 视频背景 -->
<div class="video-container"><video id="bg-video" autoplay muted loop playsinline><source type="video/mp4"></video></div>
<!-- SVG -->
<svg style="display:none"><filter id="liquid-glass-filter" x="0%" y="0%" width="100%" height="100%" filterUnits="objectBoundingBox"><feTurbulence type="fractalNoise" baseFrequency="0.01 0.01" numOctaves="1" seed="5" result="turbulence"></feTurbulence><feComponentTransfer in="turbulence" result="mapped"><feFuncR type="gamma" amplitude="1" exponent="10" offset="0.5"></feFuncR><feFuncG type="gamma" amplitude="0" exponent="1" offset="0"></feFuncG><feFuncB type="gamma" amplitude="0" exponent="1" offset="0.5"></feFuncB></feComponentTransfer><feGaussianBlur in="turbulence" stdDeviation="3" result="softMap"></feGaussianBlur><feSpecularLighting in="softMap" surfaceScale="5" specularConstant="1" specularExponent="100" lighting-color="white" result="specLight"><fePointLight x="-200" y="-200" z="300"></fePointLight></feSpecularLighting><feComposite in="specLight" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="litImage"></feComposite><feDisplacementMap in="SourceGraphic" in2="softMap" scale="150" xChannelSelector="R" yChannelSelector="G"></feDisplacementMap></filter></svg>
<!-- JS -->
<script type="text/javascript">
// 配置项
const config = Object.freeze({
  video: Object.freeze({
    mobileSource: 'https://csblog.chario.cn/usr/uploads/2026/03/small.mp4',  // 手机端视频源地址
    desktopSource: 'https://csblog.chario.cn/usr/uploads/2026/03/large.mp4',  // 桌面端视频源地址
    elementId: 'bg-video', // 视频元素ID
    breakpoint: 768 // 响应式断点
  }),
  footer: Object.freeze({
    selector: '.footer', // 页脚选择器
    maxAttempts: 10, // 最大重试次数(默认10次)
    retryInterval: 1000, // 重试间隔(默认1000ms)
    blog: 'https://blog.chario.cn/',  // 博客地址,不填则不显示
    ziyuan: 'https://www.chario.cn/'  // 新增资源站地址
  })
});
const DeviceType=Object.freeze({MOBILE:"mobile",DESKTOP:"desktop"}),FILE_EXTENSION_REGEX=/\/[^/]+\.[a-zA-Z0-9]{1,10}(?:$|\/)/;function hasFileExtension(url){try{return FILE_EXTENSION_REGEX.test(new URL(url).pathname)}catch(error){return console.error("[URL] URL解析错误:",error),!1}}function debounce(func,wait=250){let timeout;return function(...args){clearTimeout(timeout),timeout=setTimeout(()=>func.apply(this,args),wait)}}class BackgroundVideoManager{constructor(){this.videoElement=document.getElementById(config.video.elementId),this.sourceElement=this.videoElement?.querySelector("source"),this.lastSource="",this.lastDeviceType=null,this.videoElement&&this.sourceElement?this.videoElement.addEventListener("loadeddata",this.handleVideoLoad.bind(this)):console.warn("[VideoManager] 视频元素未找到,视频功能将禁用")}init(){if(!this.videoElement)return;this.switchVideoSource();const resizeHandler=debounce(()=>this.switchVideoSource());window.addEventListener("resize",resizeHandler),document.addEventListener("visibilitychange",this.handleVisibilityChange.bind(this))}handleVideoLoad(){hasFileExtension(window.location.href)||this.playVideo().catch(e=>console.debug("[VideoManager] 视频自动播放失败:",e))}handleVisibilityChange(){"visible"!==document.visibilityState||hasFileExtension(window.location.href)||this.playVideo().catch(e=>console.debug("[VideoManager] 可见性恢复播放失败:",e))}getDeviceType(){return window.innerWidth<config.video.breakpoint||window.matchMedia("(pointer: coarse)").matches?DeviceType.MOBILE:DeviceType.DESKTOP}switchVideoSource(){const deviceType=this.getDeviceType();if(deviceType===this.lastDeviceType)return;this.lastDeviceType=deviceType;const newSource=deviceType===DeviceType.MOBILE?config.video.mobileSource:config.video.desktopSource;newSource!==this.lastSource&&(this.sourceElement.src=newSource,this.lastSource=newSource,this.videoElement.load())}playVideo(){return this.videoElement.play().catch(e=>{console.warn("[VideoManager] 视频播放被阻止:",e)})}pauseVideo(){this.videoElement.pause()}}class FooterManager{constructor(){this.currentYear=(new Date).getFullYear(),this.attemptCount=0,this.intervalId=null,this.extractedLinks=null}init(){this.tryRenderFooter()||(this.intervalId=setInterval(()=>{if(this.attemptCount++>=config.footer.maxAttempts)return this.cleanup(),void console.warn(`[FooterManager] 页脚元素未找到,已尝试 ${config.footer.maxAttempts} 次,停止尝试`);this.tryRenderFooter()&&this.cleanup()},config.footer.retryInterval))}tryRenderFooter(){const footerElement=document.querySelector(config.footer.selector);return!!footerElement&&(this.extractedLinks||(this.extractedLinks=this.extractLinksFromFooter(footerElement)),footerElement.innerHTML=this.getFooterHtml(),footerElement.style.opacity="1",!0)}extractLinksFromFooter(footerElement){try{const links=footerElement.querySelectorAll("a[href]");let loginUrl="",manageUrl="",isLoggedIn=!1;return links.forEach(link=>{const href=link.getAttribute("href"),text=link.textContent.trim().toLowerCase();text.includes("登录")||href?.includes("login")?loginUrl=href:(text.includes("管理")||href?.includes("manage"))&&(manageUrl=href)}),manageUrl?isLoggedIn=!0:loginUrl&&(isLoggedIn=!1),loginUrl||manageUrl?{loginUrl:loginUrl,manageUrl:manageUrl,isLoggedIn:isLoggedIn}:(console.warn("[FooterManager] 未从现有footer中提取到登录或管理链接"),null)}catch(error){return console.warn("[FooterManager] 提取footer链接失败:",error),null}}cleanup(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null)}getFooterHtml(){const links=this.extractedLinks||{loginUrl:"",manageUrl:"",isLoggedIn:!1},linkElements=[];config.footer.ziyuan&&config.footer.ziyuan.trim()&&linkElements.push(`<a href="${config.footer.ziyuan}" target="_blank">资源站</a>`),config.footer.blog&&config.footer.blog.trim()&&linkElements.push(`<a href="${config.footer.blog}" target="_blank">博客</a>`),links.isLoggedIn&&links.manageUrl?linkElements.push(`<a href="${links.manageUrl}">管理</a>`):!links.isLoggedIn&&links.loginUrl&&linkElements.push(`<a href="${links.loginUrl}">登录</a>`);const linksHtml=linkElements.length>0?linkElements.join(" | "):"";return`\n        <div class="copyright">\n          ${linksHtml?`<div class="copyright-links">${linksHtml}</div>`:""}\n          <div class="copyright-desc">\n            <p>免责声明:本站为个人网盘,网盘所发布的一切影视、源代码、注册信息及软件等资源仅限用于学习和研究目的</p>\n            <p>Copyright ©2024 - ${this.currentYear} All rights Reserved. | 由OpenList驱动</p>\n          </div>\n        </div>\n      `}}function createUrlChangeHandler(videoManager){return debounce(()=>{const hasExtension=hasFileExtension(window.location.href);document.body.classList.toggle("has-file-extension",hasExtension),videoManager&&videoManager.videoElement&&(hasExtension?videoManager.pauseVideo():videoManager.playVideo())},100)}function init(){const videoManager=new BackgroundVideoManager;videoManager.init();(new FooterManager).init();const urlChangeHandler=createUrlChangeHandler(videoManager);urlChangeHandler();["hashchange","popstate","pushState","replaceState"].forEach(event=>{if("pushState"===event||"replaceState"===event){const original=history[event];history[event]=function(){const result=original.apply(this,arguments);return urlChangeHandler(),result}}else window.addEventListener(event,urlChangeHandler)})}"loading"!==document.readyState?init():document.addEventListener("DOMContentLoaded",init);
</script>

  1. 保存设置并刷新页面查看效果

自定义配置

您可以根据需要修改以下配置:

  1. 视频源:
    • 修改 config.video.mobileSource 和 config.video.desktopSource 替换为您的视频链接

  2. 背景图片(防止视频失效):
    • 在 CSS 中修改 --mobile-background-image 和 --desktop-background-image 变量值

  3. 页脚信息:
    • 在 config.footer.blog 中修改博客链接
    • 在 getFooterHtml() 方法中自定义页脚 HTML

效果展示

  • 移动设备(小屏):
  • 桌面设备(大屏):

前言

typecho版本 : 1.2

主题 : handsome

我在使用handsome主题的时光机功能的时候,发现使用 img 标签显示的图片,全都无法显示。

cbe91770642110.png

de821770642135.png

我琢磨了半天,最后,在handsome主题文档中找到了解决办法。

解决方案

这个问题产生的原因是 Typecho默认 用户不能在评论中插入图片或html代码

需要在 '网站后台→网站设置→评论→允许使用的HTML标签和属性' 中添加以下标签

'''<img src=""><audio class="" src="" preload><video src=""></video>'''

在添加代码之后,评论(时光机)就可以显示图片了

typecho 默认的数据库不支持emoji,所以需要通过一下方式修改数据库的编码。

如果你的数据库不支持emoji,但是在文章编辑中或者主题设置中使用了emoji会导致你的内容丢失!

alter table typecho_comments convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_contents convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_fields convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_metas convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_options convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_relationships convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_users convert to character set utf8mb4 collate utf8mb4_unicode_ci;

执行上述sql语句来修改表的编码,如果用的是宝塔面板和MySQL数据库,一般有可视化的phpmyadmin界面,可以在phpmyadmin的控制台里面执行上面的语句,如果是服务器可以在服务器上连接mysql后执行。

最后将 Typecho 目录下的config.inc.php 配置文件中数据库定义参数中的 charset 为 utf8mb4

$db->addServer(array (
      'host'      =>  localhost, 
      'user'      =>  'root',
      'password'  =>  'root',
      'charset'   =>  'utf8mb4', //修改这一行
      'port'      =>  3306,
      'database'  =>  ''
  ), Typecho_Db::READ | Typecho_Db::WRITE);

如果还是不行,进入数据库管理软件phpmyadmin,选中要改的数据库,点击上方的操作,下滑找到排序规则,将选项改为utf8mb4_unicode_ci,记得选中下方选项框

效果预览

添加此功能后,您可以在博客侧边栏或页脚等位置实时显示当前网站的在线访客数量,如下图所示:

实现步骤

步骤一:添加统计函数代码

  • 默认主题:/usr/themes/default/functions.php
  • Handsome主题:/usr/themes/handsome/functions.php

在文件末尾添加以下代码:

/**
 * 在线人数统计函数
 */
function online_users() {
    // 基本配置参数
    $filename = 'online.txt';        // 数据文件路径,默认在网站根目录
    $cookiename = 'Typecho_Online';  // Cookie名称,用于标识访客
    $onlinetime = 30;                // 在线有效时间(秒),超过此时间未活动则视为离线
  
    // 读取在线用户数据文件
    $online = file_exists($filename) ? file($filename) : array();
    $nowtime = $_SERVER['REQUEST_TIME']; 
    $nowonline = array(); 
  
    // 清理过期的在线记录
    foreach($online as $line) { 
        $row = explode('|', $line); 
        $sesstime = trim($row[1]); 
        // 仅保留有效期内的记录
        if(($nowtime - $sesstime) <= $onlinetime) {
            $nowonline[$row[0]] = $sesstime;
        } 
    } 
  
    // 处理当前访客的Cookie
    if(isset($_COOKIE[$cookiename])) {
        // 已有Cookie,获取访客ID
        $uid = $_COOKIE[$cookiename]; 
    } else {
        // 无Cookie,生成新的唯一访客ID
        $vid = 0;
        do {
            $vid++; 
            $uid = 'U'.$vid; 
        } while(array_key_exists($uid, $nowonline)); 
  
        // 设置Cookie,默认会话期有效
        setcookie($cookiename, $uid); 
    } 
  
    // 更新当前访客的在线时间
    $nowonline[$uid] = $nowtime;
  
    // 计算在线总人数
    $total_online = count($nowonline); 
  
    // 将更新后的数据写回文件
    if($fp = @fopen($filename, 'w')) { 
        if(flock($fp, LOCK_EX)) { // 获取独占锁
            rewind($fp); 
            foreach($nowonline as $fuid => $ftime) { 
                $fline = $fuid.'|'.$ftime."\n"; 
                @fputs($fp, $fline); 
            } 
            flock($fp, LOCK_UN); // 释放锁
        } 
        fclose($fp);
    } 
  
    return $total_online; 
}

步骤二:在模板中显示在线人数

通用方法(适用于任何位置)

在需要显示在线人数的模板文件中,添加以下代码:

<div class="online-users">
    当前在线: <?php echo online_users(); ?> 人
</div>

Handsome主题侧边栏添加方法

  1. 编辑文件:/usr/themes/handsome/component/sidebar.php
  2. 在适当位置(如"统计信息"部分)添加:

<li class="list-group-item"><div class="text-second">
<span class="blog-info-icons"> 
<i data-feather="user"></i></span>
<span class="badge pull-right"><?php echo online_users() ?>
</span><?php _me("在线人数") ?></li>

页脚添加方法

编辑主题的footer.php文件,在合适位置添加:

<div class="site-info">
    <!-- 其他页脚信息 -->
    <span class="online-count">当前在线: <?php echo online_users(); ?> 人</span>
</div>

转载自Xuan's blog

文章标题:Typecho添加实时在线人数统计功能 本文地址:https://blog.ybyq.wang/archives/331.html