mirror of
https://github.com/woodchen-ink/random-api-go.git
synced 2025-07-18 13:52:02 +08:00
- Changed timestamp logging in the HandleAPIRequest function to use Unix milliseconds for improved precision. - Updated the recent requests display in the HTML to correctly parse and format timestamps using the new millisecond format, enhancing readability and consistency in metrics presentation.
360 lines
14 KiB
HTML
360 lines
14 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 recentRequestsHtml = metrics.recent_requests.map(req => `
|
||
<tr>
|
||
<td>${new Date(req.time).toLocaleString()}</td>
|
||
<td>${req.path}</td>
|
||
<td>${req.method}</td>
|
||
<td>${req.status_code}</td>
|
||
<td>${formatLatency(req.latency)}</td>
|
||
</tr>
|
||
`).join('');
|
||
|
||
// 热门引用来源
|
||
const topReferersHtml = 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('');
|
||
|
||
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="recent-requests">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>时间</th>
|
||
<th>路径</th>
|
||
<th>方法</th>
|
||
<th>状态</th>
|
||
<th>延迟</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
${recentRequestsHtml}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="metrics-section">
|
||
<h3>热门引用来源</h3>
|
||
<div class="top-referers">
|
||
${topReferersHtml}
|
||
</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> |