mirror of
https://github.com/woodchen-ink/random-api-go.git
synced 2025-07-18 05:42:01 +08:00
- Removed the RecentRequests field from SystemMetrics and its associated logic in LogRequest to streamline metrics collection. - Updated the HTML to eliminate the recent requests section, focusing on the top referers display for improved performance and clarity. - Enhanced the overall metrics presentation by ensuring only relevant data is displayed, contributing to a cleaner user interface.
326 lines
13 KiB
HTML
326 lines
13 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-cmn-Hans">
|
||
|
||
<head>
|
||
<title>随机文件api</title>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<meta name="description" content="随机图API, 随机视频等 ">
|
||
<link rel="shortcut icon" size="32x32" href="https://i.czl.net/r2/2023/06/20/649168ebc2b5d.png">
|
||
<link rel="stylesheet" href="https://i.czl.net/g-f/frame/czlfonts/slice/font.css" media="all">
|
||
<link rel="stylesheet" href="https://i.czl.net/g-f/frame/prose.css" media="all">
|
||
<link rel="stylesheet" href="./css/main.css" media="all">
|
||
<style>
|
||
|
||
</style>
|
||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/markdown-it/12.3.2/markdown-it.min.js"></script>
|
||
<script defer src="https://analytics.czl.net/script.js"
|
||
data-website-id="67f71dbc-799c-46b6-9c23-b1b012daef65"></script>
|
||
</head>
|
||
|
||
<body>
|
||
<div class="overlay">
|
||
<main>
|
||
<div id="markdown-content" class="prose prose-dark">
|
||
</div>
|
||
</main>
|
||
</div>
|
||
<!-- 渲染markdown -->
|
||
<script>
|
||
// 创建带有配置的 markdown-it 实例
|
||
var md = window.markdownit({
|
||
html: true
|
||
});
|
||
|
||
// 用于存储配置的全局变量
|
||
let cachedEndpointConfig = null;
|
||
|
||
// 加载配置的函数
|
||
async function loadEndpointConfig() {
|
||
if (cachedEndpointConfig) {
|
||
return cachedEndpointConfig;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch('/config/endpoint.json');
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
cachedEndpointConfig = await response.json();
|
||
return cachedEndpointConfig;
|
||
} catch (error) {
|
||
console.error('加载endpoint配置失败:', error);
|
||
return {};
|
||
}
|
||
}
|
||
|
||
// 加载统计数据
|
||
async function loadStats() {
|
||
try {
|
||
const [statsResponse, urlStatsResponse, endpointConfig] = await Promise.all([
|
||
fetch('/stats'),
|
||
fetch('/urlstats'),
|
||
loadEndpointConfig()
|
||
]);
|
||
|
||
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, filteredPaths);
|
||
} catch (error) {
|
||
console.error('Error loading stats:', error);
|
||
}
|
||
}
|
||
|
||
// 更新统计显示
|
||
async function updateStats(stats, paths) {
|
||
const startDate = new Date('2024-11-1');
|
||
const today = new Date();
|
||
const daysSinceStart = Math.ceil((today - startDate) / (1000 * 60 * 60 * 24));
|
||
|
||
let totalCalls = 0;
|
||
let todayCalls = 0;
|
||
|
||
// 计算总调用次数
|
||
Object.entries(stats).forEach(([endpoint, stat]) => {
|
||
totalCalls += stat.total_calls;
|
||
todayCalls += stat.today_calls;
|
||
});
|
||
|
||
const avgCallsPerDay = Math.round(totalCalls / daysSinceStart);
|
||
|
||
// 更新总览统计
|
||
const summaryHtml = `
|
||
<div class="stats-summary">
|
||
<div class="stats-header">
|
||
<h2>📊 接口调用次数 <span class="refresh-icon">🔄</span></h2>
|
||
</div>
|
||
<div class="stats-grid">
|
||
<div class="stats-item">今日总调用:${todayCalls} 次</div>
|
||
<div class="stats-item">平均每天调用:${avgCallsPerDay} 次</div>
|
||
<div class="stats-item">总调用次数:${totalCalls} 次</div>
|
||
<div class="stats-item">统计开始日期:2024-11-1</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// 获取 endpoint 配置
|
||
const endpointConfig = await loadEndpointConfig();
|
||
|
||
// 生成详细统计表格
|
||
let detailHtml = `
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>接口名称</th>
|
||
<th>今日调用</th>
|
||
<th>总调用</th>
|
||
<th>URL数量</th>
|
||
<th>操作</th>
|
||
</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 += `
|
||
</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;
|
||
}
|
||
|
||
// 复制链接功能
|
||
function copyToClipboard(endpoint) {
|
||
const url = `${window.location.protocol}//${window.location.host}/${endpoint}`;
|
||
navigator.clipboard.writeText(url).then(() => {
|
||
const toast = document.createElement('div');
|
||
toast.className = 'toast';
|
||
toast.textContent = '链接已复制到剪贴板!';
|
||
document.body.appendChild(toast);
|
||
setTimeout(() => toast.remove(), 2000);
|
||
}).catch(err => {
|
||
console.error('复制失败:', err);
|
||
});
|
||
}
|
||
|
||
// 先加载 markdown 内容
|
||
fetch('./index.md')
|
||
.then(response => response.text())
|
||
.then(markdownText => {
|
||
document.getElementById('markdown-content').innerHTML = md.render(markdownText);
|
||
// markdown 加载完成后等待一小段时间再加载统计数据
|
||
setTimeout(loadStats, 100);
|
||
})
|
||
.catch(error => console.error('Error loading index.md:', error));
|
||
|
||
// 定期更新统计数据
|
||
setInterval(loadStats, 5 * 1000);
|
||
|
||
async function loadMetrics() {
|
||
try {
|
||
const response = await fetch('/metrics');
|
||
const metrics = await response.json();
|
||
updateMetricsDisplay(metrics);
|
||
} catch (error) {
|
||
console.error('Error loading metrics:', error);
|
||
}
|
||
}
|
||
|
||
function updateMetricsDisplay(metrics) {
|
||
// 格式化延迟显示
|
||
function formatLatency(microseconds) {
|
||
if (microseconds < 1000) {
|
||
return `${microseconds.toFixed(3)}µs`;
|
||
}
|
||
if (microseconds < 1000000) {
|
||
return `${(microseconds/1000).toFixed(3)}ms`;
|
||
}
|
||
return `${(microseconds/1000000).toFixed(3)}s`;
|
||
}
|
||
|
||
const metricsHtml = `
|
||
<div class="metrics-container">
|
||
<div class="metrics-section">
|
||
<h3>基础指标</h3>
|
||
<div class="metrics-grid">
|
||
<div class="metric-item">运行时间:${formatDuration(metrics.uptime)}</div>
|
||
<div class="metric-item">启动时间:${new Date(metrics.start_time).toLocaleString()}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="metrics-section">
|
||
<h3>系统指标</h3>
|
||
<div class="metrics-grid">
|
||
<div class="metric-item">CPU核心数:${metrics.num_cpu}</div>
|
||
<div class="metric-item">Goroutine数:${metrics.num_goroutine}</div>
|
||
<div class="metric-item">内存使用:${formatBytes(metrics.memory_stats.heap_alloc)}</div>
|
||
<div class="metric-item">系统内存:${formatBytes(metrics.memory_stats.heap_sys)}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="metrics-section">
|
||
<h3>性能指标</h3>
|
||
<div class="metrics-grid">
|
||
<div class="metric-item">总请求数:${metrics.request_count}</div>
|
||
<div class="metric-item">平均延迟:${formatLatency(metrics.average_latency)}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="metrics-section">
|
||
<h3>状态码统计</h3>
|
||
<div class="status-codes">
|
||
${Object.entries(metrics.status_codes)
|
||
.map(([code, count]) => `
|
||
<div class="status-code-item">
|
||
<span class="code">${code}</span>
|
||
<span class="count">${count}</span>
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="metrics-section">
|
||
<h3>热门引用来源</h3>
|
||
<div class="top-referers">
|
||
${Object.entries(metrics.top_referers)
|
||
.sort(([, a], [, b]) => b - a)
|
||
.slice(0, 10)
|
||
.map(([referer, count]) => `
|
||
<div class="referer-item">
|
||
<span class="referer">${referer}</span>
|
||
<span class="count">${count}</span>
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
const metricsElement = document.getElementById('system-metrics');
|
||
if (metricsElement) {
|
||
metricsElement.innerHTML = metricsHtml;
|
||
}
|
||
}
|
||
|
||
function 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]}`;
|
||
}
|
||
|
||
function formatDuration(ns) {
|
||
const duration = ns / 1e9; // 转换为秒
|
||
const days = Math.floor(duration / 86400);
|
||
const hours = Math.floor((duration % 86400) / 3600);
|
||
const minutes = Math.floor((duration % 3600) / 60);
|
||
const seconds = Math.floor(duration % 60);
|
||
return `${days}天 ${hours}时 ${minutes}分 ${seconds}秒`;
|
||
}
|
||
|
||
// 定期更新监控数据
|
||
setInterval(loadMetrics, 5000);
|
||
|
||
// 初始加载
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
loadMetrics();
|
||
});
|
||
</script>
|
||
</body>
|
||
|
||
</html> |