背景说明

这个版本和之前的做了以下升级:
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">

添加代码:

展开查看代码

<!-- 引入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>

3. 修改 footer.php 文件

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

在找到如下代码位置:

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

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

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

添加如下JavaScript代码:

<!-- 这里开始是新追加的内容 -->
<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>
<!-- 新追加的内容到此结束 -->

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_exec, exec, system 未被禁用
  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并按照上述教程解禁函数,然后重启服务器

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

最后修改:2025 年 12 月 27 日
如果觉得我的文章对你有用,请随意赞赏
本文作者: 文章标题:handsome主题添加服务器状态栏最新版(Linux通用版)
本文地址:https://blog.chario.cn/jsfx/36.html
版权说明:若无注明,本文皆枝语风笺原创,转载请保留文章出处。