mirror of
https://github.com/cnlimiter/codex-register.git
synced 2026-09-04 23:19:20 +08:00
3
This commit is contained in:
+275
-243
@@ -1,41 +1,38 @@
|
||||
/**
|
||||
* 设置页面 JavaScript
|
||||
* 使用 utils.js 中的工具库
|
||||
*/
|
||||
|
||||
// API 基础路径
|
||||
const API_BASE = '/api';
|
||||
|
||||
// DOM 元素
|
||||
const tabBtns = document.querySelectorAll('.tab-btn');
|
||||
const tabContents = document.querySelectorAll('.tab-content');
|
||||
const proxyForm = document.getElementById('proxy-form');
|
||||
const registrationForm = document.getElementById('registration-form');
|
||||
const testProxyBtn = document.getElementById('test-proxy-btn');
|
||||
const backupBtn = document.getElementById('backup-btn');
|
||||
const cleanupBtn = document.getElementById('cleanup-btn');
|
||||
const addEmailServiceBtn = document.getElementById('add-email-service-btn');
|
||||
const addServiceModal = document.getElementById('add-service-modal');
|
||||
const addServiceForm = document.getElementById('add-service-form');
|
||||
const closeServiceModalBtn = document.getElementById('close-service-modal');
|
||||
const cancelAddServiceBtn = document.getElementById('cancel-add-service');
|
||||
const serviceTypeSelect = document.getElementById('service-type');
|
||||
const serviceConfigFields = document.getElementById('service-config-fields');
|
||||
const emailServicesTable = document.getElementById('email-services-table');
|
||||
|
||||
// Outlook 批量导入相关
|
||||
const toggleImportBtn = document.getElementById('toggle-import-btn');
|
||||
const outlookImportBody = document.getElementById('outlook-import-body');
|
||||
const outlookImportBtn = document.getElementById('outlook-import-btn');
|
||||
const clearImportBtn = document.getElementById('clear-import-btn');
|
||||
const outlookImportData = document.getElementById('outlook-import-data');
|
||||
const importResult = document.getElementById('import-result');
|
||||
|
||||
// 批量操作
|
||||
const batchDeleteBtn = document.getElementById('batch-delete-btn');
|
||||
const selectAllCheckbox = document.getElementById('select-all-services');
|
||||
const elements = {
|
||||
tabs: document.querySelectorAll('.tab-btn'),
|
||||
tabContents: document.querySelectorAll('.tab-content'),
|
||||
proxyForm: document.getElementById('proxy-form'),
|
||||
registrationForm: document.getElementById('registration-settings-form'),
|
||||
testProxyBtn: document.getElementById('test-proxy-btn'),
|
||||
backupBtn: document.getElementById('backup-btn'),
|
||||
cleanupBtn: document.getElementById('cleanup-btn'),
|
||||
addEmailServiceBtn: document.getElementById('add-email-service-btn'),
|
||||
addServiceModal: document.getElementById('add-service-modal'),
|
||||
addServiceForm: document.getElementById('add-service-form'),
|
||||
closeServiceModal: document.getElementById('close-service-modal'),
|
||||
cancelAddService: document.getElementById('cancel-add-service'),
|
||||
serviceType: document.getElementById('service-type'),
|
||||
serviceConfigFields: document.getElementById('service-config-fields'),
|
||||
emailServicesTable: document.getElementById('email-services-table'),
|
||||
// Outlook 导入
|
||||
toggleImportBtn: document.getElementById('toggle-import-btn'),
|
||||
outlookImportBody: document.getElementById('outlook-import-body'),
|
||||
outlookImportBtn: document.getElementById('outlook-import-btn'),
|
||||
clearImportBtn: document.getElementById('clear-import-btn'),
|
||||
outlookImportData: document.getElementById('outlook-import-data'),
|
||||
importResult: document.getElementById('import-result'),
|
||||
// 批量操作
|
||||
selectAllServices: document.getElementById('select-all-services')
|
||||
};
|
||||
|
||||
// 选中的服务 ID
|
||||
let selectedServiceIds = [];
|
||||
let selectedServiceIds = new Set();
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
@@ -48,12 +45,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// 初始化标签页
|
||||
function initTabs() {
|
||||
tabBtns.forEach(btn => {
|
||||
elements.tabs.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const tab = btn.dataset.tab;
|
||||
|
||||
tabBtns.forEach(b => b.classList.remove('active'));
|
||||
tabContents.forEach(c => c.classList.remove('active'));
|
||||
elements.tabs.forEach(b => b.classList.remove('active'));
|
||||
elements.tabContents.forEach(c => c.classList.remove('active'));
|
||||
|
||||
btn.classList.add('active');
|
||||
document.getElementById(`${tab}-tab`).classList.add('active');
|
||||
@@ -64,90 +61,84 @@ function initTabs() {
|
||||
// 事件监听
|
||||
function initEventListeners() {
|
||||
// 代理表单
|
||||
proxyForm.addEventListener('submit', handleSaveProxy);
|
||||
elements.proxyForm.addEventListener('submit', handleSaveProxy);
|
||||
|
||||
// 测试代理
|
||||
testProxyBtn.addEventListener('click', handleTestProxy);
|
||||
elements.testProxyBtn.addEventListener('click', handleTestProxy);
|
||||
|
||||
// 注册配置表单
|
||||
registrationForm.addEventListener('submit', handleSaveRegistration);
|
||||
elements.registrationForm.addEventListener('submit', handleSaveRegistration);
|
||||
|
||||
// 备份数据库
|
||||
backupBtn.addEventListener('click', handleBackup);
|
||||
elements.backupBtn.addEventListener('click', handleBackup);
|
||||
|
||||
// 清理数据
|
||||
cleanupBtn.addEventListener('click', handleCleanup);
|
||||
elements.cleanupBtn.addEventListener('click', handleCleanup);
|
||||
|
||||
// 添加邮箱服务
|
||||
addEmailServiceBtn.addEventListener('click', () => {
|
||||
addServiceModal.classList.add('active');
|
||||
loadServiceConfigFields(serviceTypeSelect.value);
|
||||
elements.addEmailServiceBtn.addEventListener('click', () => {
|
||||
elements.addServiceModal.classList.add('active');
|
||||
loadServiceConfigFields(elements.serviceType.value);
|
||||
});
|
||||
|
||||
closeServiceModalBtn.addEventListener('click', () => {
|
||||
addServiceModal.classList.remove('active');
|
||||
elements.closeServiceModal.addEventListener('click', () => {
|
||||
elements.addServiceModal.classList.remove('active');
|
||||
});
|
||||
|
||||
cancelAddServiceBtn.addEventListener('click', () => {
|
||||
addServiceModal.classList.remove('active');
|
||||
elements.cancelAddService.addEventListener('click', () => {
|
||||
elements.addServiceModal.classList.remove('active');
|
||||
});
|
||||
|
||||
addServiceModal.addEventListener('click', (e) => {
|
||||
if (e.target === addServiceModal) {
|
||||
addServiceModal.classList.remove('active');
|
||||
elements.addServiceModal.addEventListener('click', (e) => {
|
||||
if (e.target === elements.addServiceModal) {
|
||||
elements.addServiceModal.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// 服务类型切换
|
||||
serviceTypeSelect.addEventListener('change', (e) => {
|
||||
elements.serviceType.addEventListener('change', (e) => {
|
||||
loadServiceConfigFields(e.target.value);
|
||||
});
|
||||
|
||||
// 添加服务表单
|
||||
addServiceForm.addEventListener('submit', handleAddService);
|
||||
elements.addServiceForm.addEventListener('submit', handleAddService);
|
||||
|
||||
// Outlook 批量导入展开/折叠
|
||||
if (toggleImportBtn) {
|
||||
toggleImportBtn.addEventListener('click', () => {
|
||||
const isHidden = outlookImportBody.style.display === 'none';
|
||||
outlookImportBody.style.display = isHidden ? 'block' : 'none';
|
||||
toggleImportBtn.textContent = isHidden ? '收起' : '展开';
|
||||
if (elements.toggleImportBtn) {
|
||||
elements.toggleImportBtn.addEventListener('click', () => {
|
||||
const isHidden = elements.outlookImportBody.style.display === 'none';
|
||||
elements.outlookImportBody.style.display = isHidden ? 'block' : 'none';
|
||||
elements.toggleImportBtn.textContent = isHidden ? '收起' : '展开';
|
||||
});
|
||||
}
|
||||
|
||||
// Outlook 批量导入
|
||||
if (outlookImportBtn) {
|
||||
outlookImportBtn.addEventListener('click', handleOutlookBatchImport);
|
||||
if (elements.outlookImportBtn) {
|
||||
elements.outlookImportBtn.addEventListener('click', handleOutlookBatchImport);
|
||||
}
|
||||
|
||||
// 清空导入数据
|
||||
if (clearImportBtn) {
|
||||
clearImportBtn.addEventListener('click', () => {
|
||||
outlookImportData.value = '';
|
||||
importResult.style.display = 'none';
|
||||
if (elements.clearImportBtn) {
|
||||
elements.clearImportBtn.addEventListener('click', () => {
|
||||
elements.outlookImportData.value = '';
|
||||
elements.importResult.style.display = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
// 全选/取消全选
|
||||
if (selectAllCheckbox) {
|
||||
selectAllCheckbox.addEventListener('change', (e) => {
|
||||
if (elements.selectAllServices) {
|
||||
elements.selectAllServices.addEventListener('change', (e) => {
|
||||
const checkboxes = document.querySelectorAll('.service-checkbox');
|
||||
checkboxes.forEach(cb => cb.checked = e.target.checked);
|
||||
updateSelectedServices();
|
||||
});
|
||||
}
|
||||
|
||||
// 批量删除
|
||||
if (batchDeleteBtn) {
|
||||
batchDeleteBtn.addEventListener('click', handleBatchDelete);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载设置
|
||||
async function loadSettings() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/settings`);
|
||||
const data = await response.json();
|
||||
const data = await api.get('/settings');
|
||||
|
||||
// 代理设置
|
||||
document.getElementById('proxy-enabled').checked = data.proxy?.enabled || false;
|
||||
@@ -165,61 +156,88 @@ async function loadSettings() {
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载设置失败:', error);
|
||||
toast.error('加载设置失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 加载邮箱服务
|
||||
async function loadEmailServices() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/email-services`);
|
||||
const data = await response.json();
|
||||
|
||||
const data = await api.get('/email-services');
|
||||
renderEmailServices(data.services);
|
||||
} catch (error) {
|
||||
console.error('加载邮箱服务失败:', error);
|
||||
elements.emailServicesTable.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-icon">❌</div>
|
||||
<div class="empty-state-title">加载失败</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染邮箱服务
|
||||
function renderEmailServices(services) {
|
||||
if (services.length === 0) {
|
||||
emailServicesTable.innerHTML = '<tr><td colspan="7" style="text-align: center;">暂无配置</td></tr>';
|
||||
batchDeleteBtn.style.display = 'none';
|
||||
elements.emailServicesTable.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-icon">📭</div>
|
||||
<div class="empty-state-title">暂无配置</div>
|
||||
<div class="empty-state-description">点击上方"添加服务"按钮添加邮箱服务</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
emailServicesTable.innerHTML = services.map(service => `
|
||||
elements.emailServicesTable.innerHTML = services.map(service => `
|
||||
<tr data-service-id="${service.id}">
|
||||
<td><input type="checkbox" class="service-checkbox" data-id="${service.id}" onchange="updateSelectedServices()"></td>
|
||||
<td>
|
||||
<input type="checkbox" class="service-checkbox" data-id="${service.id}"
|
||||
onchange="updateSelectedServices()">
|
||||
</td>
|
||||
<td>${escapeHtml(service.name)}</td>
|
||||
<td>${getServiceTypeText(service.service_type)}</td>
|
||||
<td><span class="status-badge ${service.enabled ? 'completed' : ''}">${service.enabled ? '已启用' : '已禁用'}</span></td>
|
||||
<td>${service.priority}</td>
|
||||
<td>${formatDate(service.last_used)}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-secondary" onclick="testService(${service.id})">测试</button>
|
||||
<button class="btn btn-sm ${service.enabled ? 'btn-warning' : 'btn-primary'}" onclick="toggleService(${service.id}, ${!service.enabled})">
|
||||
${service.enabled ? '禁用' : '启用'}
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger" onclick="deleteService(${service.id})">删除</button>
|
||||
<span class="status-badge ${service.enabled ? 'active' : 'disabled'}">
|
||||
${service.enabled ? '已启用' : '已禁用'}
|
||||
</span>
|
||||
</td>
|
||||
<td>${service.priority}</td>
|
||||
<td>${format.date(service.last_used)}</td>
|
||||
<td>
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-ghost btn-sm" onclick="testService(${service.id})" title="测试">
|
||||
🔌
|
||||
</button>
|
||||
<button class="btn btn-ghost btn-sm" onclick="toggleService(${service.id}, ${!service.enabled})" title="${service.enabled ? '禁用' : '启用'}">
|
||||
${service.enabled ? '🔒' : '🔓'}
|
||||
</button>
|
||||
<button class="btn btn-ghost btn-sm" onclick="deleteService(${service.id})" title="删除">
|
||||
🗑️
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
|
||||
// 更新批量删除按钮状态
|
||||
updateSelectedServices();
|
||||
}
|
||||
|
||||
// 加载数据库信息
|
||||
async function loadDatabaseInfo() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/settings/database`);
|
||||
const data = await response.json();
|
||||
const data = await api.get('/settings/database');
|
||||
|
||||
document.getElementById('db-size').textContent = `${data.database_size_mb} MB`;
|
||||
document.getElementById('db-accounts').textContent = data.accounts_count;
|
||||
document.getElementById('db-services').textContent = data.email_services_count;
|
||||
document.getElementById('db-tasks').textContent = data.tasks_count;
|
||||
document.getElementById('db-accounts').textContent = format.number(data.accounts_count);
|
||||
document.getElementById('db-services').textContent = format.number(data.email_services_count);
|
||||
document.getElementById('db-tasks').textContent = format.number(data.tasks_count);
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载数据库信息失败:', error);
|
||||
@@ -240,38 +258,25 @@ async function handleSaveProxy(e) {
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/settings/proxy`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert('代理设置已保存');
|
||||
} else {
|
||||
const result = await response.json();
|
||||
alert('保存失败: ' + (result.detail || '未知错误'));
|
||||
}
|
||||
await api.post('/settings/proxy', data);
|
||||
toast.success('代理设置已保存');
|
||||
} catch (error) {
|
||||
alert('保存失败: ' + error.message);
|
||||
toast.error('保存失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 测试代理
|
||||
async function handleTestProxy() {
|
||||
testProxyBtn.disabled = true;
|
||||
testProxyBtn.textContent = '测试中...';
|
||||
elements.testProxyBtn.disabled = true;
|
||||
elements.testProxyBtn.innerHTML = '<span class="loading-spinner"></span> 测试中...';
|
||||
|
||||
try {
|
||||
// 这里应该调用一个测试代理的 API
|
||||
// 暂时模拟
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
alert('代理测试功能待实现');
|
||||
// TODO: 实现代理测试 API
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
toast.info('代理测试功能待实现');
|
||||
} finally {
|
||||
testProxyBtn.disabled = false;
|
||||
testProxyBtn.textContent = '测试连接';
|
||||
elements.testProxyBtn.disabled = false;
|
||||
elements.testProxyBtn.textContent = '🔌 测试连接';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,96 +293,68 @@ async function handleSaveRegistration(e) {
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/settings/registration`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert('注册配置已保存');
|
||||
} else {
|
||||
const result = await response.json();
|
||||
alert('保存失败: ' + (result.detail || '未知错误'));
|
||||
}
|
||||
await api.post('/settings/registration', data);
|
||||
toast.success('注册配置已保存');
|
||||
} catch (error) {
|
||||
alert('保存失败: ' + error.message);
|
||||
toast.error('保存失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 备份数据库
|
||||
async function handleBackup() {
|
||||
backupBtn.disabled = true;
|
||||
backupBtn.textContent = '备份中...';
|
||||
elements.backupBtn.disabled = true;
|
||||
elements.backupBtn.innerHTML = '<span class="loading-spinner"></span> 备份中...';
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/settings/database/backup`, {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
alert(`备份成功: ${data.backup_path}`);
|
||||
} else {
|
||||
alert('备份失败: ' + (data.detail || '未知错误'));
|
||||
}
|
||||
const data = await api.post('/settings/database/backup');
|
||||
toast.success(`备份成功: ${data.backup_path}`);
|
||||
} catch (error) {
|
||||
alert('备份失败: ' + error.message);
|
||||
toast.error('备份失败: ' + error.message);
|
||||
} finally {
|
||||
backupBtn.disabled = false;
|
||||
backupBtn.textContent = '备份数据库';
|
||||
elements.backupBtn.disabled = false;
|
||||
elements.backupBtn.textContent = '💾 备份数据库';
|
||||
}
|
||||
}
|
||||
|
||||
// 清理数据
|
||||
async function handleCleanup() {
|
||||
if (!confirm('确定要清理过期数据吗?此操作不可恢复。')) {
|
||||
return;
|
||||
}
|
||||
const confirmed = await confirm('确定要清理过期数据吗?此操作不可恢复。');
|
||||
if (!confirmed) return;
|
||||
|
||||
cleanupBtn.disabled = true;
|
||||
cleanupBtn.textContent = '清理中...';
|
||||
elements.cleanupBtn.disabled = true;
|
||||
elements.cleanupBtn.innerHTML = '<span class="loading-spinner"></span> 清理中...';
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/settings/database/cleanup?days=30`, {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
alert(data.message);
|
||||
loadDatabaseInfo();
|
||||
} else {
|
||||
alert('清理失败: ' + (data.detail || '未知错误'));
|
||||
}
|
||||
const data = await api.post('/settings/database/cleanup?days=30');
|
||||
toast.success(data.message);
|
||||
loadDatabaseInfo();
|
||||
} catch (error) {
|
||||
alert('清理失败: ' + error.message);
|
||||
toast.error('清理失败: ' + error.message);
|
||||
} finally {
|
||||
cleanupBtn.disabled = false;
|
||||
cleanupBtn.textContent = '清理过期数据';
|
||||
elements.cleanupBtn.disabled = false;
|
||||
elements.cleanupBtn.textContent = '🧹 清理过期数据';
|
||||
}
|
||||
}
|
||||
|
||||
// 加载服务配置字段
|
||||
async function loadServiceConfigFields(serviceType) {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/email-services/types`);
|
||||
const data = await response.json();
|
||||
|
||||
const data = await api.get('/email-services/types');
|
||||
const typeInfo = data.types.find(t => t.value === serviceType);
|
||||
if (!typeInfo) return;
|
||||
|
||||
serviceConfigFields.innerHTML = typeInfo.config_fields.map(field => `
|
||||
if (!typeInfo) {
|
||||
elements.serviceConfigFields.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
elements.serviceConfigFields.innerHTML = typeInfo.config_fields.map(field => `
|
||||
<div class="form-group">
|
||||
<label for="config-${field.name}">${field.label}</label>
|
||||
<input type="${field.name.includes('password') || field.name.includes('token') ? 'password' : 'text'}"
|
||||
id="config-${field.name}"
|
||||
name="${field.name}"
|
||||
value="${field.default || ''}"
|
||||
placeholder="${field.label}"
|
||||
${field.required ? 'required' : ''}>
|
||||
</div>
|
||||
`).join('');
|
||||
@@ -391,10 +368,10 @@ async function loadServiceConfigFields(serviceType) {
|
||||
async function handleAddService(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(addServiceForm);
|
||||
const formData = new FormData(elements.addServiceForm);
|
||||
const config = {};
|
||||
|
||||
serviceConfigFields.querySelectorAll('input').forEach(input => {
|
||||
elements.serviceConfigFields.querySelectorAll('input').forEach(input => {
|
||||
config[input.name] = input.value;
|
||||
});
|
||||
|
||||
@@ -407,44 +384,27 @@ async function handleAddService(e) {
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/email-services`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
addServiceModal.classList.remove('active');
|
||||
addServiceForm.reset();
|
||||
loadEmailServices();
|
||||
alert('邮箱服务已添加');
|
||||
} else {
|
||||
const result = await response.json();
|
||||
alert('添加失败: ' + (result.detail || '未知错误'));
|
||||
}
|
||||
await api.post('/email-services', data);
|
||||
toast.success('邮箱服务已添加');
|
||||
elements.addServiceModal.classList.remove('active');
|
||||
elements.addServiceForm.reset();
|
||||
loadEmailServices();
|
||||
} catch (error) {
|
||||
alert('添加失败: ' + error.message);
|
||||
toast.error('添加失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 测试服务
|
||||
async function testService(id) {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/email-services/${id}/test`, {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const data = await api.post(`/email-services/${id}/test`);
|
||||
if (data.success) {
|
||||
alert('服务连接正常');
|
||||
toast.success('服务连接正常');
|
||||
} else {
|
||||
alert('服务连接失败: ' + data.message);
|
||||
toast.warning('服务连接失败: ' + data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
alert('测试失败: ' + error.message);
|
||||
toast.error('测试失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,62 +412,134 @@ async function testService(id) {
|
||||
async function toggleService(id, enabled) {
|
||||
try {
|
||||
const endpoint = enabled ? 'enable' : 'disable';
|
||||
const response = await fetch(`${API_BASE}/email-services/${id}/${endpoint}`, {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
loadEmailServices();
|
||||
} else {
|
||||
const data = await response.json();
|
||||
alert('操作失败: ' + (data.detail || '未知错误'));
|
||||
}
|
||||
await api.post(`/email-services/${id}/${endpoint}`);
|
||||
toast.success(enabled ? '服务已启用' : '服务已禁用');
|
||||
loadEmailServices();
|
||||
} catch (error) {
|
||||
alert('操作失败: ' + error.message);
|
||||
toast.error('操作失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 删除服务
|
||||
async function deleteService(id) {
|
||||
if (!confirm('确定要删除此邮箱服务配置吗?')) {
|
||||
return;
|
||||
}
|
||||
const confirmed = await confirm('确定要删除此邮箱服务配置吗?');
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/email-services/${id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
loadEmailServices();
|
||||
} else {
|
||||
const data = await response.json();
|
||||
alert('删除失败: ' + (data.detail || '未知错误'));
|
||||
}
|
||||
await api.delete(`/email-services/${id}`);
|
||||
toast.success('服务已删除');
|
||||
loadEmailServices();
|
||||
} catch (error) {
|
||||
alert('删除失败: ' + error.message);
|
||||
toast.error('删除失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
// 更新选中的服务
|
||||
function updateSelectedServices() {
|
||||
selectedServiceIds.clear();
|
||||
document.querySelectorAll('.service-checkbox:checked').forEach(cb => {
|
||||
selectedServiceIds.add(parseInt(cb.dataset.id));
|
||||
});
|
||||
}
|
||||
|
||||
// Outlook 批量导入
|
||||
async function handleOutlookBatchImport() {
|
||||
const data = elements.outlookImportData.value.trim();
|
||||
if (!data) {
|
||||
toast.warning('请输入要导入的数据');
|
||||
return;
|
||||
}
|
||||
|
||||
const enabled = document.getElementById('outlook-import-enabled').checked;
|
||||
const priority = parseInt(document.getElementById('outlook-import-priority').value) || 0;
|
||||
|
||||
// 解析数据
|
||||
const lines = data.split('\n').filter(line => line.trim() && !line.trim().startsWith('#'));
|
||||
const accounts = [];
|
||||
const errors = [];
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
const parts = line.split('----').map(p => p.trim());
|
||||
if (parts.length < 2) {
|
||||
errors.push(`第 ${index + 1} 行格式错误`);
|
||||
return;
|
||||
}
|
||||
|
||||
const account = {
|
||||
email: parts[0],
|
||||
password: parts[1],
|
||||
client_id: parts[2] || null,
|
||||
refresh_token: parts[3] || null,
|
||||
enabled: enabled,
|
||||
priority: priority
|
||||
};
|
||||
|
||||
if (!account.email.includes('@')) {
|
||||
errors.push(`第 ${index + 1} 行邮箱格式错误: ${account.email}`);
|
||||
return;
|
||||
}
|
||||
|
||||
accounts.push(account);
|
||||
});
|
||||
|
||||
if (errors.length > 0) {
|
||||
elements.importResult.style.display = 'block';
|
||||
elements.importResult.innerHTML = `
|
||||
<div class="import-errors">${errors.map(e => `<div>${e}</div>`).join('')}</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
elements.outlookImportBtn.disabled = true;
|
||||
elements.outlookImportBtn.innerHTML = '<span class="loading-spinner"></span> 导入中...';
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
try {
|
||||
for (const account of accounts) {
|
||||
try {
|
||||
await api.post('/email-services', {
|
||||
service_type: 'outlook',
|
||||
name: account.email,
|
||||
config: {
|
||||
email: account.email,
|
||||
password: account.password,
|
||||
client_id: account.client_id,
|
||||
refresh_token: account.refresh_token
|
||||
},
|
||||
enabled: account.enabled,
|
||||
priority: account.priority
|
||||
});
|
||||
successCount++;
|
||||
} catch {
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
elements.importResult.style.display = 'block';
|
||||
elements.importResult.innerHTML = `
|
||||
<div class="import-stats">
|
||||
<span>✅ 成功: ${successCount}</span>
|
||||
<span>❌ 失败: ${failCount}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
toast.success(`导入完成,成功 ${successCount} 个`);
|
||||
loadEmailServices();
|
||||
|
||||
} catch (error) {
|
||||
toast.error('导入失败: ' + error.message);
|
||||
} finally {
|
||||
elements.outlookImportBtn.disabled = false;
|
||||
elements.outlookImportBtn.textContent = '📥 开始导入';
|
||||
}
|
||||
}
|
||||
|
||||
// HTML 转义
|
||||
function escapeHtml(text) {
|
||||
if (!text) return '';
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function getServiceTypeText(type) {
|
||||
const typeMap = {
|
||||
'tempmail': 'Tempmail.lol',
|
||||
'outlook': 'Outlook',
|
||||
'custom_domain': '自定义域名',
|
||||
};
|
||||
return typeMap[type] || type;
|
||||
}
|
||||
|
||||
function formatDate(dateStr) {
|
||||
if (!dateStr) return '-';
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleString('zh-CN');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user