Files
JieXi/templates/admin_apis.html
2025-11-30 19:49:25 +08:00

286 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>接口管理 - 管理后台</title>
<link rel="stylesheet" href="/static/css/ui-components.css?v=3">
<link rel="stylesheet" href="/static/css/admin.css">
</head>
<body class="admin-layout">
<header class="admin-header">
<div class="header-container">
<a href="/admin/dashboard" class="brand">
<span style="font-size: 1.5rem;"></span> JieXi Admin
</a>
<nav class="nav-links">
<a href="/admin/dashboard" class="nav-item">仪表板</a>
<a href="/admin/users" class="nav-item">用户管理</a>
<a href="/admin/apis" class="nav-item active">接口管理</a>
<a href="/admin/redeem-codes" class="nav-item">兑换码</a>
<a href="/admin/config" class="nav-item">系统配置</a>
<a href="/admin/logs" class="nav-item">日志审计</a>
</nav>
<div class="user-actions">
<a href="/admin/profile" class="ui-btn ui-btn-secondary ui-btn-sm">账号设置</a>
<button class="ui-btn ui-btn-secondary ui-btn-sm" onclick="logout()">退出登录</button>
</div>
</div>
</header>
<main class="main-container">
<div class="page-header">
<h1 class="page-title">接口管理</h1>
<div class="actions">
<button class="ui-btn ui-btn-primary" onclick="showAddModal()">
<span style="margin-right: 4px;">+</span> 添加接口
</button>
</div>
</div>
<div class="ui-card">
<div id="tableContainer">
<div class="loading">加载中...</div>
</div>
</div>
</main>
<!-- API Modal -->
<div id="apiModal" class="ui-modal">
<div class="ui-modal-overlay" onclick="closeModal()"></div>
<div class="ui-modal-content">
<h3 id="modalTitle">添加接口</h3>
<form id="apiForm">
<input type="hidden" id="apiId">
<div class="form-group">
<label>接口名称 *</label>
<input type="text" id="apiName" class="ui-input" required>
</div>
<div class="form-group">
<label>平台类型 *</label>
<select id="apiPlatform" class="ui-input" required>
<option value="douyin">抖音</option>
<option value="tiktok">TikTok</option>
<option value="bilibili">哔哩哔哩</option>
<option value="kuaishou">快手</option>
<option value="pipixia">皮皮虾</option>
<option value="weibo">微博</option>
</select>
</div>
<div class="form-group">
<label>API地址 *</label>
<input type="url" id="apiUrl" class="ui-input" required placeholder="https://api.example.com">
</div>
<div class="form-group">
<label>API密钥</label>
<input type="text" id="apiKey" class="ui-input" placeholder="可选">
</div>
<div class="form-group">
<label>权重</label>
<input type="number" id="apiWeight" class="ui-input" value="1" min="0" max="100">
</div>
<div class="form-group">
<label>是否启用</label>
<select id="apiEnabled" class="ui-input">
<option value="true">启用</option>
<option value="false">禁用</option>
</select>
</div>
<div class="ui-modal-actions">
<button type="button" class="ui-btn ui-btn-secondary" onclick="closeModal()">取消</button>
<button type="submit" class="ui-btn ui-btn-primary">保存</button>
</div>
</form>
</div>
</div>
<script src="/static/js/ui-components.js"></script>
<script>
let apisData = [];
async function loadApis() {
try {
const response = await fetch('/admin/api/apis');
const result = await response.json();
if (result.success) {
apisData = result.data;
renderTable();
} else {
UI.notify('加载失败: ' + result.message, 'error');
}
} catch (error) {
UI.notify('加载失败: ' + error.message, 'error');
}
}
function renderTable() {
const container = document.getElementById('tableContainer');
if (apisData.length === 0) {
container.innerHTML = '<div style="text-align: center; padding: 2rem; color: var(--text-muted);">暂无接口数据</div>';
return;
}
container.innerHTML = `
<div class="table-container">
<table>
<thead>
<tr>
<th>ID</th>
<th>名称</th>
<th>平台</th>
<th>API地址</th>
<th>权重</th>
<th>状态</th>
<th>健康</th>
<th>调用统计</th>
<th>响应时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
${apisData.map(a => `
<tr>
<td>#${a.id}</td>
<td><span class="font-medium">${a.name}</span></td>
<td><span class="badge badge-neutral">${a.platform}</span></td>
<td style="max-width: 200px; overflow: hidden; text-overflow: ellipsis;" title="${a.api_url}" class="text-muted text-sm">${a.api_url}</td>
<td>${a.weight}</td>
<td><span class="badge ${a.is_enabled ? 'badge-info' : 'badge-neutral'}">${a.is_enabled ? '启用' : '禁用'}</span></td>
<td><span class="badge ${a.health_status ? 'badge-success' : 'badge-error'}">${a.health_status ? '正常' : '异常'}</span></td>
<td class="text-sm">${a.success_calls}/${a.total_calls}</td>
<td class="text-sm text-muted">${(a.avg_response_time / 1000).toFixed(2)}s</td>
<td>
<div class="flex gap-2">
<button class="ui-btn ui-btn-secondary ui-btn-sm" onclick='editApi(${JSON.stringify(a)})'>编辑</button>
<button class="ui-btn ui-btn-secondary ui-btn-sm" onclick="testApi(${a.id}, '${a.platform}')">测试</button>
<button class="ui-btn ui-btn-danger ui-btn-sm" onclick="deleteApi(${a.id})">删除</button>
</div>
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
`;
}
function showAddModal() {
document.getElementById('modalTitle').textContent = '添加接口';
document.getElementById('apiForm').reset();
document.getElementById('apiId').value = '';
document.getElementById('apiModal').classList.add('show');
}
function editApi(api) {
document.getElementById('modalTitle').textContent = '编辑接口';
document.getElementById('apiId').value = api.id;
document.getElementById('apiName').value = api.name;
document.getElementById('apiPlatform').value = api.platform;
document.getElementById('apiUrl').value = api.api_url;
document.getElementById('apiKey').value = api.api_key || '';
document.getElementById('apiWeight').value = api.weight;
document.getElementById('apiEnabled').value = api.is_enabled;
document.getElementById('apiModal').classList.add('show');
}
function closeModal() {
document.getElementById('apiModal').classList.remove('show');
}
async function deleteApi(id) {
UI.confirm('确认删除', '确定要删除此接口吗?', async () => {
try {
const response = await fetch(`/admin/api/apis/${id}`, { method: 'DELETE' });
const result = await response.json();
if (result.success) {
UI.notify('删除成功', 'success');
loadApis();
} else {
UI.notify('删除失败: ' + result.message, 'error');
}
} catch (error) {
UI.notify('删除失败: ' + error.message, 'error');
}
});
}
document.getElementById('apiForm').addEventListener('submit', async (e) => {
e.preventDefault();
const apiId = document.getElementById('apiId').value;
const data = {
name: document.getElementById('apiName').value,
platform: document.getElementById('apiPlatform').value,
api_url: document.getElementById('apiUrl').value,
api_key: document.getElementById('apiKey').value || null,
weight: parseInt(document.getElementById('apiWeight').value),
is_enabled: document.getElementById('apiEnabled').value === 'true'
};
try {
const url = apiId ? `/admin/api/apis/${apiId}` : '/admin/api/apis';
const method = apiId ? 'PUT' : 'POST';
const response = await fetch(url, {
method: method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
UI.notify(apiId ? '更新成功' : '创建成功', 'success');
closeModal();
loadApis();
} else {
UI.notify('操作失败: ' + result.message, 'error');
}
} catch (error) {
UI.notify('操作失败: ' + error.message, 'error');
}
});
async function testApi(apiId, platform) {
const testUrls = {
'douyin': 'https://v.douyin.com/iRNBho6u/',
'tiktok': 'https://www.tiktok.com/@username/video/1234567890',
'bilibili': 'https://www.bilibili.com/video/BV1vrU6B4ELQ/?share_source=copy_web&vd_source=8977adbddf938cc18f327c3c21c5120c'
};
UI.prompt('测试接口', `请输入测试链接 (${platform})`, testUrls[platform] || '', async (testUrl) => {
try {
const response = await fetch(`/admin/api/apis/${apiId}/test`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ test_url: testUrl })
});
const result = await response.json();
if (result.success) {
UI.notify(`测试成功!响应时间: ${result.response_time}ms`, 'success');
loadApis();
} else {
UI.notify('测试失败: ' + result.message, 'error');
}
} catch (error) {
UI.notify('测试失败: ' + error.message, 'error');
}
});
}
async function logout() {
try {
await fetch('/admin/logout', { method: 'POST' });
window.location.href = '/admin/login';
} catch (error) {
UI.notify('退出失败', 'error');
}
}
loadApis();
</script>
</body>
</html>