aws-mt5/templates/mapping.html
2026-01-05 15:33:08 +08:00

186 lines
7.6 KiB
HTML
Raw Permalink 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-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;
}
body { margin:0; min-height:100vh; background:var(--bg); color:var(--text); font-family:"Segoe UI","Helvetica Neue",Arial,sans-serif; padding:24px; }
.shell { max-width: 820px; 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; }
label { display:block; font-weight:600; color:#cbd5e1; margin:8px 0 4px; }
input { width:100%; padding:10px 12px; border-radius:10px; border:1px solid rgba(255,255,255,0.08); background:rgba(255,255,255,0.04); color:var(--text); }
button { padding:10px 14px; border:none; border-radius:10px; cursor:pointer; background:linear-gradient(135deg,var(--accent),var(--accent-2)); color:var(--text); font-weight:700; }
table { width:100%; border-collapse:collapse; margin-top:16px; }
th, td { padding:10px; border-bottom:1px solid rgba(255,255,255,0.08); text-align:left; }
.muted { color:var(--muted); }
.status { margin-top:12px; padding:10px; border-radius:10px; background:rgba(255,255,255,0.04); border:1px solid rgba(255,255,255,0.08); }
.error { color: var(--danger); }
.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;">操作需输入额外密码ZHIYUN_PASS。通过校验后可增删改。</p>
<div class="status" id="auth-box">
<div class="row">
<div>
<label for="auth-pass">校验密码</label>
<input id="auth-pass" type="password" placeholder="输入 ZHIYUN_PASS">
</div>
<div style="align-self:flex-end;">
<button id="auth-btn" style="width:100%;">校验</button>
</div>
</div>
<div id="auth-msg" class="muted" style="margin-top:6px;"></div>
</div>
<div id="form-area" style="display:none; margin-top:16px;">
<div class="row">
<div>
<label for="ip">IP 地址</label>
<input id="ip" placeholder="例如54.12.34.56">
</div>
<div>
<label for="account">账户名</label>
<input id="account" placeholder="与数据库中 AWS 账户的 name 对应">
</div>
</div>
<div style="margin-top:10px; display:flex; gap:10px;">
<button id="save-btn">新增/更新</button>
<button id="delete-btn" style="background:#ef4444;">删除</button>
</div>
</div>
<div id="msg" class="status" style="display:none;"></div>
<table id="table" style="display:none;">
<thead>
<tr><th>IP</th><th>账户</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
<script>
const authBtn = document.getElementById('auth-btn');
const authPass = document.getElementById('auth-pass');
const authMsg = document.getElementById('auth-msg');
const formArea = document.getElementById('form-area');
const msg = document.getElementById('msg');
const table = document.getElementById('table');
const tbody = table.querySelector('tbody');
const saveBtn = document.getElementById('save-btn');
const delBtn = document.getElementById('delete-btn');
const ipInput = document.getElementById('ip');
const accountInput = document.getElementById('account');
function showMsg(text, isError=false) {
msg.style.display = 'block';
msg.className = 'status' + (isError ? ' error' : '');
msg.textContent = text;
}
async function auth() {
authBtn.disabled = true;
authMsg.textContent = '校验中...';
try {
const formData = new FormData();
formData.append('password', authPass.value);
const resp = await fetch('/mapping/auth', { method:'POST', body: formData });
const data = await resp.json();
if (!resp.ok) throw new Error(data.error || '校验失败');
authMsg.textContent = '校验成功,可进行操作';
formArea.style.display = 'block';
await loadList();
} catch (err) {
authMsg.textContent = err.message;
formArea.style.display = 'none';
table.style.display = 'none';
} finally {
authBtn.disabled = false;
}
}
async function loadList() {
try {
const resp = await fetch('/mapping/list');
const data = await resp.json();
if (!resp.ok) throw new Error(data.error || '获取失败');
const items = data.items || [];
tbody.innerHTML = items.map(item => `
<tr>
<td>${item.ip_address}</td>
<td>${item.account_name}</td>
</tr>
`).join('');
table.style.display = 'table';
} catch (err) {
showMsg(err.message, true);
}
}
async function save() {
const ip = ipInput.value.trim();
const acc = accountInput.value.trim();
if (!ip || !acc) {
showMsg('IP 和账户名不能为空', true);
return;
}
saveBtn.disabled = true;
try {
const formData = new FormData();
formData.append('ip', ip);
formData.append('account', acc);
const resp = await fetch('/mapping/upsert', { method:'POST', body: formData });
const data = await resp.json();
if (!resp.ok) throw new Error(data.error || '保存失败');
showMsg('保存成功');
await loadList();
} catch (err) {
showMsg(err.message, true);
} finally {
saveBtn.disabled = false;
}
}
async function remove() {
const ip = ipInput.value.trim();
if (!ip) {
showMsg('删除前请填写 IP', true);
return;
}
delBtn.disabled = true;
try {
const formData = new FormData();
formData.append('ip', ip);
const resp = await fetch('/mapping/delete', { method:'POST', body: formData });
const data = await resp.json();
if (!resp.ok) throw new Error(data.error || '删除失败');
showMsg('删除成功');
await loadList();
} catch (err) {
showMsg(err.message, true);
} finally {
delBtn.disabled = false;
}
}
authBtn.addEventListener('click', auth);
saveBtn.addEventListener('click', save);
delBtn.addEventListener('click', remove);
</script>
</body>
</html>