random-api-go/public/index.html
wood chen d3933374af refactor(middleware, monitoring, public): streamline metrics handling and remove top referers display
- Simplified MetricsMiddleware by removing unnecessary comments and focusing on essential request logging.
- Updated RequestLog structure to exclude the Referer field from JSON serialization, enhancing data privacy.
- Removed top referers tracking and associated HTML/CSS elements to declutter the user interface and improve performance.
- Cleaned up CSS styles related to referers for a more streamlined design.
2024-12-02 05:35:24 +08:00

324 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>
<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>
</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 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'),
loadEndpointConfig()
]);
const stats = await statsResponse.json();
const urlStats = await urlStatsResponse.json();
// 更新统计
await updateStats(stats, urlStats);
// 移除动画
setTimeout(() => {
if (refreshIcon) {
refreshIcon.classList.remove('spinning');
}
if (summaryElement) summaryElement.classList.remove('fade');
if (detailElement) detailElement.classList.remove('fade');
}, 300);
} catch (error) {
console.error('Error loading stats:', error);
}
}
// 更新统计显示
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));
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);
// 获取 endpoint 配置
const endpointConfig = await loadEndpointConfig();
// 更新总览统计
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>
<table>
<thead>
<tr>
<th>接口名称</th>
<th>今日调用</th>
<th>总调用</th>
<th>URL数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
${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 container = document.querySelector('.stats-container');
if (container) {
container.innerHTML = summaryHtml;
}
}
// 复制链接功能
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 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'
});
};
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>
</div>
`;
const container = document.getElementById('system-metrics');
if (container) {
container.innerHTML = metricsHtml;
}
} catch (error) {
console.error('Error loading metrics:', error);
const container = document.getElementById('system-metrics');
if (container) {
container.innerHTML = '<div class="error-message">加载系统指标失败</div>';
}
}
}
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
// 定期更新监控数据
setInterval(loadMetrics, 5000);
// 初始加载
document.addEventListener('DOMContentLoaded', () => {
loadMetrics();
});
</script>
</body>
</html>