mirror of
https://github.com/woodchen-ink/random-api-go.git
synced 2025-07-18 05:42:01 +08:00
feat(public): enhance system metrics display and user interface
- Added a main title to the index.html for better context. - Introduced a new stats container to organize summary and detail statistics. - Implemented a refresh animation for loading stats, improving user experience. - Updated the updateStats function to streamline data handling and display. - Enhanced the metrics section with improved formatting and error handling. - Refined CSS styles for better visual presentation and responsiveness. - Ensured a cleaner layout for system metrics and top referers, contributing to a more intuitive interface.
This commit is contained in:
parent
454fe7380f
commit
6a0df0bdc7
@ -3,9 +3,22 @@ body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
font-weight: 300;
|
||||
background: transparent;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url(https://random-api.czl.net/pic/all);
|
||||
background-size: cover;
|
||||
overflow: auto;
|
||||
background-position: center;
|
||||
z-index: -1;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
@ -14,7 +27,7 @@ body {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
z-index: 2;
|
||||
overflow-y: auto;
|
||||
}
|
||||
@ -45,29 +58,27 @@ img {
|
||||
}
|
||||
|
||||
.stats-summary {
|
||||
background-color: #44444423;
|
||||
padding: 15px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin: 20px 0;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
/* 两列布局 */
|
||||
gap: 10px;
|
||||
/* 项目之间的间距 */
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.stats-item {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
/* 确保在小屏幕上切换为单列 */
|
||||
@media (max-width: 600px) {
|
||||
.stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
padding: 12px 15px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.95em;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.stats-header {
|
||||
@ -80,6 +91,7 @@ img {
|
||||
.stats-header h2 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.refresh-icon {
|
||||
@ -113,21 +125,16 @@ table {
|
||||
} */
|
||||
|
||||
.endpoint-link {
|
||||
/* color: #666; 默认字体颜色改为深灰色 */
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
padding: 2px 8px; /* 稍微增加内边距 */
|
||||
color: #2196f3;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s ease; /* 平滑过渡效果 */
|
||||
display: inline-block; /* 使padding生效 */
|
||||
font-weight: 500; /* 稍微加粗 */
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.endpoint-link:hover {
|
||||
background-color: #2196f3; /* 鼠标悬停时的背景色改为蓝色 */
|
||||
color: white; /* 鼠标悬停时文字变为白色 */
|
||||
transform: translateY(-1px); /* 轻微上浮效果 */
|
||||
box-shadow: 0 2px 4px rgba(33, 150, 243, 0.2); /* 添加阴影效果 */
|
||||
background: rgba(33, 150, 243, 0.1);
|
||||
color: #2196f3;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* 点击时的效果 */
|
||||
@ -176,78 +183,185 @@ table {
|
||||
|
||||
/* 系统监控样式 */
|
||||
.metrics-container {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.metrics-section {
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.metrics-section h3 {
|
||||
color: #2196f3;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.1em;
|
||||
border-bottom: 1px solid rgba(33, 150, 243, 0.2);
|
||||
padding-bottom: 5px;
|
||||
color: #2196f3;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.1em;
|
||||
border-bottom: 1px solid rgba(33, 150, 243, 0.2);
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.metrics-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.metric-item {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9em;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.status-codes {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.status-code-item {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.recent-requests table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.9em;
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.recent-requests th,
|
||||
.recent-requests td {
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.recent-requests th {
|
||||
color: #2196f3;
|
||||
font-weight: 500;
|
||||
color: #2196f3;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.top-referers {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.referer-item {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 8px 12px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.referer {
|
||||
max-width: 70%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.count {
|
||||
color: #2196f3;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 更新表格样式 */
|
||||
.stats-table {
|
||||
margin-top: 20px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 12px 15px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
color: #999;
|
||||
}
|
||||
|
||||
th {
|
||||
background: rgba(33, 150, 243, 0.1);
|
||||
font-weight: 500;
|
||||
color: #2196f3;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
/* 操作按钮样式 */
|
||||
td a {
|
||||
color: #2196f3;
|
||||
text-decoration: none;
|
||||
margin-right: 8px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
td a:hover {
|
||||
background: rgba(33, 150, 243, 0.1);
|
||||
}
|
||||
|
||||
/* 响应式优化 */
|
||||
@media (max-width: 768px) {
|
||||
.stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
.stats-table {
|
||||
margin: 10px -15px;
|
||||
width: calc(100% + 30px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 系统指标样式 */
|
||||
.metrics-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
color: #999;
|
||||
font-size: 0.9em;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: 1.1em;
|
||||
font-weight: 500;
|
||||
color: #2196f3;
|
||||
}
|
||||
|
||||
.referers-list {
|
||||
margin-top: 15px;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.referer-item {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
padding: 10px 15px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@ -255,6 +369,7 @@ table {
|
||||
}
|
||||
|
||||
.referer {
|
||||
color: #999;
|
||||
max-width: 70%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@ -264,4 +379,64 @@ table {
|
||||
.count {
|
||||
color: #2196f3;
|
||||
font-weight: 500;
|
||||
padding: 2px 8px;
|
||||
background: rgba(33, 150, 243, 0.1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: rgba(255, 0, 0, 0.1);
|
||||
color: #ff4444;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
margin: 10px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 确保系统指标和统计数据之间有适当间距 */
|
||||
#system-metrics {
|
||||
max-width: 800px;
|
||||
margin: 0 auto 30px auto;
|
||||
}
|
||||
|
||||
/* 优化移动端显示 */
|
||||
@media (max-width: 768px) {
|
||||
.stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.referer {
|
||||
max-width: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
.main-title {
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
margin: 20px 0;
|
||||
font-size: 2em;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
/* 修改统计数据容器的宽度限制 */
|
||||
.stats-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 移动端适配 */
|
||||
@media (max-width: 768px) {
|
||||
.stats-container {
|
||||
padding: 0 15px;
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,14 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 class="main-title">Random-Api 随机文件API</h1>
|
||||
<div class="overlay">
|
||||
<main>
|
||||
<div id="system-metrics"></div>
|
||||
<div class="stats-container">
|
||||
<div id="stats-summary"></div>
|
||||
<div id="stats-detail"></div>
|
||||
</div>
|
||||
<div id="markdown-content" class="prose prose-dark">
|
||||
</div>
|
||||
</main>
|
||||
@ -58,6 +63,18 @@
|
||||
// 加载统计数据
|
||||
async function loadStats() {
|
||||
try {
|
||||
// 添加刷新动画
|
||||
const refreshIcon = document.querySelector('.refresh-icon');
|
||||
const summaryElement = document.getElementById('stats-summary');
|
||||
const detailElement = document.getElementById('stats-detail');
|
||||
|
||||
if (refreshIcon) {
|
||||
refreshIcon.classList.add('spinning');
|
||||
}
|
||||
if (summaryElement) summaryElement.classList.add('fade');
|
||||
if (detailElement) detailElement.classList.add('fade');
|
||||
|
||||
// 获取数据
|
||||
const [statsResponse, urlStatsResponse, endpointConfig] = await Promise.all([
|
||||
fetch('/stats'),
|
||||
fetch('/urlstats'),
|
||||
@ -67,30 +84,25 @@
|
||||
const stats = await statsResponse.json();
|
||||
const urlStats = await urlStatsResponse.json();
|
||||
|
||||
// 只显示 endpoint.json 中配置的路径
|
||||
const filteredPaths = {};
|
||||
for (const [category, types] of Object.entries(endpointConfig)) {
|
||||
if (urlStats.paths[category]) {
|
||||
filteredPaths[category] = {};
|
||||
for (const [type, desc] of Object.entries(types)) {
|
||||
if (urlStats.paths[category][type]) {
|
||||
filteredPaths[category][type] = {
|
||||
path: urlStats.paths[category][type],
|
||||
description: desc
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 更新统计
|
||||
await updateStats(stats, urlStats);
|
||||
|
||||
// 移除动画
|
||||
setTimeout(() => {
|
||||
if (refreshIcon) {
|
||||
refreshIcon.classList.remove('spinning');
|
||||
}
|
||||
if (summaryElement) summaryElement.classList.remove('fade');
|
||||
if (detailElement) detailElement.classList.remove('fade');
|
||||
}, 300);
|
||||
|
||||
await updateStats(stats, filteredPaths);
|
||||
} catch (error) {
|
||||
console.error('Error loading stats:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新统计显示
|
||||
async function updateStats(stats, paths) {
|
||||
async function updateStats(stats, urlStats) {
|
||||
const startDate = new Date('2024-11-1');
|
||||
const today = new Date();
|
||||
const daysSinceStart = Math.ceil((today - startDate) / (1000 * 60 * 60 * 24));
|
||||
@ -106,6 +118,9 @@
|
||||
|
||||
const avgCallsPerDay = Math.round(totalCalls / daysSinceStart);
|
||||
|
||||
// 获取 endpoint 配置
|
||||
const endpointConfig = await loadEndpointConfig();
|
||||
|
||||
// 更新总览统计
|
||||
const summaryHtml = `
|
||||
<div class="stats-summary">
|
||||
@ -119,13 +134,6 @@
|
||||
<div class="stats-item">统计开始日期:2024-11-1</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 获取 endpoint 配置
|
||||
const endpointConfig = await loadEndpointConfig();
|
||||
|
||||
// 生成详细统计表格
|
||||
let detailHtml = `
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@ -137,48 +145,40 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
// 按 order 排序并生成表格行
|
||||
const endpoints = Object.entries(endpointConfig)
|
||||
.sort(([, a], [, b]) => a.order - b.order);
|
||||
|
||||
for (const [endpoint, config] of endpoints) {
|
||||
const stat = stats[endpoint] || { today_calls: 0, total_calls: 0 };
|
||||
const urlCount = urlStats[endpoint]?.total_urls || 0;
|
||||
|
||||
detailHtml += `
|
||||
<tr>
|
||||
<td>
|
||||
<a href="javascript:void(0)"
|
||||
onclick="copyToClipboard('${endpoint}')"
|
||||
class="endpoint-link"
|
||||
title="点击复制链接">
|
||||
${config.name}
|
||||
</a>
|
||||
</td>
|
||||
<td>${stat.today_calls}</td>
|
||||
<td>${stat.total_calls}</td>
|
||||
<td>${urlCount}</td>
|
||||
<td>
|
||||
<a href="/${endpoint}" target="_blank" rel="noopener noreferrer" title="测试接口">👀</a>
|
||||
<a href="javascript:void(0)" onclick="copyToClipboard('${endpoint}')" title="复制链接">📋</a>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
|
||||
detailHtml += `
|
||||
${Object.entries(endpointConfig)
|
||||
.sort(([, a], [, b]) => (a.order || 0) - (b.order || 0))
|
||||
.map(([endpoint, config]) => {
|
||||
const stat = stats[endpoint] || { today_calls: 0, total_calls: 0 };
|
||||
const urlCount = urlStats[endpoint]?.total_urls || 0;
|
||||
return `
|
||||
<tr>
|
||||
<td>
|
||||
<a href="javascript:void(0)"
|
||||
onclick="copyToClipboard('${endpoint}')"
|
||||
class="endpoint-link"
|
||||
title="点击复制链接">
|
||||
${config.name}
|
||||
</a>
|
||||
</td>
|
||||
<td>${stat.today_calls}</td>
|
||||
<td>${stat.total_calls}</td>
|
||||
<td>${urlCount}</td>
|
||||
<td>
|
||||
<a href="/${endpoint}" target="_blank" rel="noopener noreferrer" title="测试接口">👀</a>
|
||||
<a href="javascript:void(0)" onclick="copyToClipboard('${endpoint}')" title="复制链接">📋</a>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
|
||||
// 更新 DOM
|
||||
const summaryElement = document.getElementById('stats-summary');
|
||||
const detailElement = document.getElementById('stats-detail');
|
||||
|
||||
if (summaryElement) summaryElement.innerHTML = summaryHtml;
|
||||
if (detailElement) detailElement.innerHTML = detailHtml;
|
||||
const container = document.querySelector('.stats-container');
|
||||
if (container) {
|
||||
container.innerHTML = summaryHtml;
|
||||
}
|
||||
}
|
||||
|
||||
// 复制链接功能
|
||||
@ -213,43 +213,113 @@
|
||||
const response = await fetch('/metrics');
|
||||
const data = await response.json();
|
||||
|
||||
// 添加数据验证
|
||||
if (!data || typeof data !== 'object') {
|
||||
throw new Error('Invalid metrics data received');
|
||||
}
|
||||
|
||||
// 格式化函数
|
||||
const formatUptime = (ns) => {
|
||||
const seconds = Math.floor(ns / 1e9);
|
||||
const days = Math.floor(seconds / 86400);
|
||||
const hours = Math.floor((seconds % 86400) / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
return `${days}天 ${hours}小时 ${minutes}分钟`;
|
||||
};
|
||||
|
||||
const formatBytes = (bytes) => {
|
||||
const units = ['B', 'KB', 'MB', 'GB'];
|
||||
let size = bytes;
|
||||
let unitIndex = 0;
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
||||
};
|
||||
|
||||
const formatDate = (dateStr) => {
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
};
|
||||
|
||||
updateMetricsDisplay(data);
|
||||
const metricsHtml = `
|
||||
<div class="metrics-section">
|
||||
<div class="stats-summary">
|
||||
<div class="stats-header">
|
||||
<h2>💻 系统状态</h2>
|
||||
</div>
|
||||
<div class="stats-grid">
|
||||
<div class="stats-item">
|
||||
<div class="metric-label">运行时间</div>
|
||||
<div class="metric-value">${formatUptime(data.uptime)}</div>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<div class="metric-label">启动时间</div>
|
||||
<div class="metric-value">${formatDate(data.start_time)}</div>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<div class="metric-label">CPU核心数</div>
|
||||
<div class="metric-value">${data.num_cpu} 核</div>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<div class="metric-label">Goroutine数量</div>
|
||||
<div class="metric-value">${data.num_goroutine}</div>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<div class="metric-label">平均延迟</div>
|
||||
<div class="metric-value">${data.average_latency.toFixed(2)} ms</div>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<div class="metric-label">堆内存分配</div>
|
||||
<div class="metric-value">${formatBytes(data.memory_stats.heap_alloc)}</div>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<div class="metric-label">系统内存</div>
|
||||
<div class="metric-value">${formatBytes(data.memory_stats.heap_sys)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${Object.keys(data.top_referers).length > 0 ? `
|
||||
<div class="stats-summary">
|
||||
<div class="stats-header">
|
||||
<h2>🔗 访问来源</h2>
|
||||
</div>
|
||||
<div class="referers-list">
|
||||
${Object.entries(data.top_referers)
|
||||
.sort(([,a], [,b]) => b - a)
|
||||
.map(([referer, count]) => `
|
||||
<div class="referer-item">
|
||||
<span class="referer">${referer || '直接访问'}</span>
|
||||
<span class="count">${count}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
const container = document.getElementById('system-metrics');
|
||||
if (container) {
|
||||
container.innerHTML = metricsHtml;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading metrics:', error);
|
||||
document.getElementById('metrics-container').innerHTML =
|
||||
'<p class="error">加载指标数据时出错,请稍后重试</p>';
|
||||
const container = document.getElementById('system-metrics');
|
||||
if (container) {
|
||||
container.innerHTML = '<div class="error-message">加载系统指标失败</div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateMetricsDisplay(metricsData) {
|
||||
// 确保 metricsData 是有效对象
|
||||
if (!metricsData || typeof metricsData !== 'object') {
|
||||
console.error('Invalid metrics data');
|
||||
return;
|
||||
}
|
||||
|
||||
const container = document.getElementById('metrics-container');
|
||||
container.innerHTML = ''; // 清空现有内容
|
||||
|
||||
// 使用 Object.entries 之前进行安全检查
|
||||
const entries = Object.entries(metricsData).filter(([key, value]) => key && value !== undefined);
|
||||
|
||||
entries.forEach(([key, value]) => {
|
||||
const metricDiv = document.createElement('div');
|
||||
metricDiv.className = 'metric-item';
|
||||
metricDiv.innerHTML = `
|
||||
<h3>${escapeHtml(key)}</h3>
|
||||
<p>${escapeHtml(String(value))}</p>
|
||||
`;
|
||||
container.appendChild(metricDiv);
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(unsafe) {
|
||||
return unsafe
|
||||
.replace(/&/g, "&")
|
||||
|
@ -1,15 +1,12 @@
|
||||
# Random-Api 随机文件API
|
||||
<div id="system-metrics"></div>
|
||||
|
||||
<div class="stats-container">
|
||||
<div id="stats-summary"></div>
|
||||
<div id="stats-detail"></div>
|
||||
</div>
|
||||
|
||||
<div id="system-metrics"></div>
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 部署和原理
|
||||
|
||||
请见我的帖子:[https://q58.org/t/topic/127](https://q58.org/t/topic/127)
|
||||
|
Loading…
x
Reference in New Issue
Block a user