aws-mt5/templates/history.html

132 lines
5.8 KiB
HTML
Raw Normal View History

2026-01-05 11:07:55 +08:00
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>IP 替换历史</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root {
--bg: linear-gradient(135deg, #0f172a, #1e293b);
--card: #0b1224;
--accent: #22d3ee;
--accent-2: #a855f7;
--text: #e2e8f0;
--muted: #94a3b8;
--danger: #f87171;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
background: var(--bg);
color: var(--text);
padding: 28px;
}
.shell {
max-width: 900px;
margin: 0 auto;
background: var(--card);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 14px;
padding: 24px;
box-shadow: 0 25px 60px rgba(0, 0, 0, 0.45);
}
h1 { margin: 0 0 10px; letter-spacing: 0.6px; }
label { font-weight: 600; color: #cbd5e1; display: block; margin-bottom: 6px; }
input { width: 100%; padding: 11px 12px; border-radius: 10px; border: 1px solid rgba(255,255,255,0.08); background: rgba(255,255,255,0.04); color: var(--text); }
button { margin-top: 12px; padding: 10px 14px; border-radius: 10px; border: none; cursor: pointer; background: linear-gradient(135deg, var(--accent), var(--accent-2)); color: var(--text); font-weight: 700; }
.muted { color: var(--muted); }
.status { margin-top: 12px; padding: 12px; border-radius: 10px; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); }
.history-item { padding: 10px 0; border-bottom: 1px solid rgba(255,255,255,0.06); }
.history-item:last-child { border-bottom: none; }
.badge { display: inline-block; padding: 2px 8px; background: rgba(255,255,255,0.08); border-radius: 999px; margin-right: 6px; font-size: 12px; }
.mono { font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; }
.row { display: flex; gap: 10px; }
.row > div { flex: 1; }
</style>
</head>
<body>
<div class="shell">
<h1>IP 替换历史</h1>
<p class="muted" style="margin-top:0;">按 IP 或 group_id 查看完整链路a→b→c。group_id 默认继承上一跳,否则用旧 IP。</p>
<div class="row">
<div>
<label for="ip">IP旧/新任意一个)</label>
<input id="ip" placeholder="例如18.133.222.207">
</div>
<div>
<label for="group">group_id可选</label>
<input id="group" placeholder="默认使用旧 IP 作为 group_id">
</div>
</div>
<button id="search-btn">搜索</button>
<div id="result" class="status" style="display:none;"></div>
<div id="list"></div>
</div>
<script>
const ipInput = document.getElementById('ip');
const groupInput = document.getElementById('group');
const searchBtn = document.getElementById('search-btn');
const result = document.getElementById('result');
const list = document.getElementById('list');
function showMsg(msg, isError=false) {
result.style.display = 'block';
result.className = 'status' + (isError ? ' error' : '');
result.textContent = msg;
}
function render(items) {
if (!items || !items.length) {
list.innerHTML = '<div class="muted" style="margin-top:10px;">暂无记录</div>';
return;
}
list.innerHTML = items.map(group => {
const chainStr = (group.chain || []).join(' ➜ ');
const detail = (group.items || []).map(item => `
<div class="history-item">
<div><span class="badge">旧 IP</span><span class="mono">${item.old_ip}</span></div>
<div><span class="badge">新 IP</span><span class="mono">${item.new_ip}</span></div>
<div class="muted" style="margin-top:4px;">${item.account_name} ${item.created_at} 流量(30天): ${item.terminated_network_out_mb ?? '-' } MB</div>
</div>
`).join('');
return `
<div class="status" style="margin-top:12px;">
<div><strong>Group</strong>: <span class="mono">${group.group_id || '-'}</span></div>
<div class="muted" style="margin:4px 0;">首台开机时间:${group.first_ip_start || '-'}</div>
<div class="muted" style="margin:6px 0;">链路:${chainStr}</div>
<div>${detail}</div>
</div>
`;
}).join('');
}
async function loadChains() {
const ip = ipInput.value.trim();
const group = groupInput.value.trim();
searchBtn.disabled = true;
showMsg('查询中...');
try {
const params = new URLSearchParams();
if (ip) params.append('ip', ip);
if (group) params.append('group', group);
const resp = await fetch('/history/chains?' + params.toString());
const data = await resp.json();
if (!resp.ok) throw new Error(data.error || '查询失败');
result.style.display = 'none';
render(data.items || []);
} catch (err) {
showMsg(err.message, true);
} finally {
searchBtn.disabled = false;
}
}
searchBtn.addEventListener('click', loadChains);
loadChains();
</script>
</body>
</html>