This commit is contained in:
2025-11-28 21:20:40 +08:00
commit f940b95b67
73 changed files with 15721 additions and 0 deletions

335
templates/admin_smtp.html Normal file
View File

@@ -0,0 +1,335 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SMTP配置 - 管理后台</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">接口管理</a>
<a href="/admin/config" class="nav-item active">系统配置</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">SMTP配置</h1>
<div class="actions">
<a href="/admin/config" class="ui-btn ui-btn-secondary">返回配置</a>
<button class="ui-btn ui-btn-primary" onclick="showAddModal()">+ 添加SMTP</button>
</div>
</div>
<div class="ui-card">
<div class="table-container">
<table id="smtpTable">
<thead>
<tr>
<th>ID</th>
<th>名称</th>
<th>服务器</th>
<th>端口</th>
<th>用户名</th>
<th>发件邮箱</th>
<th>权重</th>
<th>状态</th>
<th>统计</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="10" style="text-align: center; color: var(--text-muted);">加载中...</td>
</tr>
</tbody>
</table>
</div>
</div>
</main>
<!-- SMTP Modal -->
<div id="smtpModal" class="ui-modal">
<div class="ui-modal-overlay" onclick="closeModal()"></div>
<div class="ui-modal-content">
<h3 id="modalTitle">添加SMTP配置</h3>
<form id="smtpForm">
<input type="hidden" id="smtpId">
<div class="form-group">
<label>配置名称 *</label>
<input type="text" id="smtpName" class="ui-input" required placeholder="例如:阿里云邮件">
</div>
<div class="form-group">
<label>SMTP服务器 *</label>
<input type="text" id="smtpHost" class="ui-input" required placeholder="smtp.example.com">
</div>
<div class="form-group">
<label>端口 *</label>
<input type="number" id="smtpPort" class="ui-input" required value="587" min="1" max="65535">
<small class="text-muted text-sm">常用端口25 (不加密), 587 (TLS), 465 (SSL)</small>
</div>
<div class="form-group">
<label>用户名 *</label>
<input type="text" id="smtpUsername" class="ui-input" required>
</div>
<div class="form-group">
<label>密码 *</label>
<input type="password" id="smtpPassword" class="ui-input" required placeholder="SMTP授权码/密码">
<small class="text-muted text-sm" id="smtpPasswordHint">创建时必填,编辑时留空表示不修改</small>
</div>
<div class="form-group">
<label>发件邮箱 *</label>
<input type="email" id="smtpFromEmail" class="ui-input" required placeholder="noreply@example.com">
</div>
<div class="form-group">
<label>发件人名称</label>
<input type="text" id="smtpFromName" class="ui-input" placeholder="短视频解析平台">
</div>
<div class="form-group">
<label>使用TLS</label>
<select id="smtpUseTls" class="ui-input">
<option value="true"></option>
<option value="false"></option>
</select>
</div>
<div class="form-group">
<label>权重</label>
<input type="number" id="smtpWeight" class="ui-input" value="1" min="0" max="100">
</div>
<div class="form-group">
<label>是否启用</label>
<select id="smtpEnabled" 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>
<!-- Test Modal -->
<div id="testModal" class="ui-modal">
<div class="ui-modal-overlay" onclick="closeTestModal()"></div>
<div class="ui-modal-content">
<h3>测试SMTP配置</h3>
<form id="testForm">
<div class="form-group">
<label>测试邮箱地址</label>
<input type="email" id="testEmail" class="ui-input" required placeholder="test@example.com">
</div>
<div class="ui-modal-actions">
<button type="button" class="ui-btn ui-btn-secondary" onclick="closeTestModal()">取消</button>
<button type="submit" class="ui-btn ui-btn-success">发送测试邮件</button>
</div>
</form>
</div>
</div>
<script src="/static/js/ui-components.js"></script>
<script>
async function loadSmtp() {
try {
const response = await fetch('/admin/api/smtp');
const result = await response.json();
if (result.success) {
const tbody = document.querySelector('#smtpTable tbody');
if (result.data.length === 0) {
tbody.innerHTML = '<tr><td colspan="10" style="text-align: center; padding: 2rem; color: var(--text-muted);">暂无SMTP配置</td></tr>';
return;
}
tbody.innerHTML = result.data.map(s => `
<tr>
<td>${s.id}</td>
<td><span class="font-medium">${s.name}</span> ${s.is_default ? '<span class="badge badge-info">默认</span>' : ''}</td>
<td class="text-sm">${s.host}</td>
<td>${s.port}</td>
<td class="text-sm text-muted">${s.username}</td>
<td class="text-sm">${s.from_email}</td>
<td>${s.weight}</td>
<td><span class="badge ${s.is_enabled ? 'badge-success' : 'badge-neutral'}">${s.is_enabled ? '启用' : '禁用'}</span></td>
<td class="text-sm">${s.send_count} / <span class="text-error">${s.fail_count}失败</span></td>
<td>
<div class="flex gap-2">
<button class="ui-btn ui-btn-secondary ui-btn-sm" onclick='editSmtp(${JSON.stringify(s)})'>编辑</button>
<button class="ui-btn ui-btn-secondary ui-btn-sm" onclick="testSmtp()">测试</button>
<button class="ui-btn ui-btn-danger ui-btn-sm" onclick="deleteSmtp(${s.id})">删除</button>
</div>
</td>
</tr>
`).join('');
}
} catch (error) {
UI.notify('加载失败: ' + error.message, 'error');
}
}
function showAddModal() {
const passwordInput = document.getElementById('smtpPassword');
const passwordHint = document.getElementById('smtpPasswordHint');
document.getElementById('modalTitle').textContent = '添加SMTP配置';
document.getElementById('smtpForm').reset();
document.getElementById('smtpId').value = '';
document.getElementById('smtpPort').value = '587';
document.getElementById('smtpWeight').value = '1';
passwordInput.value = '';
passwordInput.required = true;
passwordHint.textContent = '创建时必填,编辑时留空表示不修改';
document.getElementById('smtpModal').classList.add('show');
}
function editSmtp(smtp) {
const passwordInput = document.getElementById('smtpPassword');
const passwordHint = document.getElementById('smtpPasswordHint');
document.getElementById('modalTitle').textContent = '编辑SMTP配置';
document.getElementById('smtpId').value = smtp.id;
document.getElementById('smtpName').value = smtp.name;
document.getElementById('smtpHost').value = smtp.host;
document.getElementById('smtpPort').value = smtp.port;
document.getElementById('smtpUsername').value = smtp.username;
passwordInput.value = '';
passwordInput.required = false;
passwordHint.textContent = '留空表示保留原密码/授权码';
document.getElementById('smtpFromEmail').value = smtp.from_email;
document.getElementById('smtpFromName').value = smtp.from_name || '';
document.getElementById('smtpUseTls').value = smtp.use_tls;
document.getElementById('smtpWeight').value = smtp.weight;
document.getElementById('smtpEnabled').value = smtp.is_enabled;
document.getElementById('smtpModal').classList.add('show');
}
function closeModal() {
document.getElementById('smtpModal').classList.remove('show');
}
function testSmtp() {
document.getElementById('testModal').classList.add('show');
}
function closeTestModal() {
document.getElementById('testModal').classList.remove('show');
}
async function deleteSmtp(id) {
UI.confirm('确认删除', '确定要删除此SMTP配置吗', async () => {
try {
const response = await fetch(`/admin/api/smtp/${id}`, { method: 'DELETE' });
const result = await response.json();
if (result.success) {
UI.notify('删除成功', 'success');
loadSmtp();
} else {
UI.notify('删除失败: ' + result.message, 'error');
}
} catch (error) {
UI.notify('删除失败: ' + error.message, 'error');
}
});
}
document.getElementById('smtpForm').addEventListener('submit', async (e) => {
e.preventDefault();
const smtpId = document.getElementById('smtpId').value;
const passwordInput = document.getElementById('smtpPassword');
const password = passwordInput.value.trim();
const data = {
name: document.getElementById('smtpName').value.trim(),
host: document.getElementById('smtpHost').value.trim(),
port: parseInt(document.getElementById('smtpPort').value, 10),
username: document.getElementById('smtpUsername').value.trim(),
from_email: document.getElementById('smtpFromEmail').value.trim(),
from_name: document.getElementById('smtpFromName').value.trim(),
use_tls: document.getElementById('smtpUseTls').value === 'true',
weight: parseInt(document.getElementById('smtpWeight').value, 10),
is_enabled: document.getElementById('smtpEnabled').value === 'true'
};
if (password) {
data.password = password;
} else if (!smtpId) {
UI.notify('请输入SMTP密码/授权码', 'error');
passwordInput.focus();
return;
}
try {
const url = smtpId ? `/admin/api/smtp/${smtpId}` : '/admin/api/smtp';
const method = smtpId ? '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(smtpId ? '更新成功' : '创建成功', 'success');
closeModal();
loadSmtp();
} else {
UI.notify('操作失败: ' + result.message, 'error');
}
} catch (error) {
UI.notify('操作失败: ' + error.message, 'error');
}
});
document.getElementById('testForm').addEventListener('submit', async (e) => {
e.preventDefault();
const email = document.getElementById('testEmail').value;
try {
const response = await fetch('/admin/api/smtp/test', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
const result = await response.json();
if (result.success) {
UI.notify('测试邮件已发送,请检查收件箱', 'success');
closeTestModal();
} 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');
}
}
loadSmtp();
</script>
</body>
</html>