Files
codex-register/static/js/app.js
cnlimiter 845e712226 feat(registration): 新增Outlook批量注册功能
- 前端界面添加Outlook批量注册选项和账户选择面板
- 后端API新增/registration/outlook-accounts和/registration/outlook-batch端点
- 支持批量选择Outlook账户、自动跳过已注册邮箱、随机间隔控制
- 更新requirements.txt依赖版本
2026-03-15 01:56:55 +08:00

823 lines
29 KiB
JavaScript
Raw 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.
/**
* 注册页面 JavaScript
* 使用 utils.js 中的工具库
*/
// 状态
let currentTask = null;
let currentBatch = null;
let logPollingInterval = null;
let batchPollingInterval = null;
let accountsPollingInterval = null;
let isBatchMode = false;
let isOutlookBatchMode = false;
let outlookAccounts = [];
let availableServices = {
tempmail: { available: true, services: [] },
outlook: { available: false, services: [] },
custom_domain: { available: false, services: [] }
};
// DOM 元素
const elements = {
form: document.getElementById('registration-form'),
emailService: document.getElementById('email-service'),
regMode: document.getElementById('reg-mode'),
regModeGroup: document.getElementById('reg-mode-group'),
batchCountGroup: document.getElementById('batch-count-group'),
batchCount: document.getElementById('batch-count'),
batchOptions: document.getElementById('batch-options'),
intervalMin: document.getElementById('interval-min'),
intervalMax: document.getElementById('interval-max'),
startBtn: document.getElementById('start-btn'),
cancelBtn: document.getElementById('cancel-btn'),
taskStatusRow: document.getElementById('task-status-row'),
batchProgressSection: document.getElementById('batch-progress-section'),
consoleLog: document.getElementById('console-log'),
clearLogBtn: document.getElementById('clear-log-btn'),
// 任务状态
taskId: document.getElementById('task-id'),
taskEmail: document.getElementById('task-email'),
taskStatus: document.getElementById('task-status'),
taskService: document.getElementById('task-service'),
taskStatusBadge: document.getElementById('task-status-badge'),
// 批量状态
batchProgressText: document.getElementById('batch-progress-text'),
batchProgressPercent: document.getElementById('batch-progress-percent'),
progressBar: document.getElementById('progress-bar'),
batchSuccess: document.getElementById('batch-success'),
batchFailed: document.getElementById('batch-failed'),
batchRemaining: document.getElementById('batch-remaining'),
// 已注册账号
recentAccountsTable: document.getElementById('recent-accounts-table'),
refreshAccountsBtn: document.getElementById('refresh-accounts-btn'),
// Outlook 批量注册
outlookBatchSection: document.getElementById('outlook-batch-section'),
outlookAccountsContainer: document.getElementById('outlook-accounts-container'),
outlookIntervalMin: document.getElementById('outlook-interval-min'),
outlookIntervalMax: document.getElementById('outlook-interval-max'),
outlookSkipRegistered: document.getElementById('outlook-skip-registered')
};
// 初始化
document.addEventListener('DOMContentLoaded', () => {
initEventListeners();
loadAvailableServices();
loadRecentAccounts();
startAccountsPolling();
});
// 事件监听
function initEventListeners() {
// 注册表单提交
elements.form.addEventListener('submit', handleStartRegistration);
// 注册模式切换
elements.regMode.addEventListener('change', handleModeChange);
// 邮箱服务切换
elements.emailService.addEventListener('change', handleServiceChange);
// 取消按钮
elements.cancelBtn.addEventListener('click', handleCancelTask);
// 清空日志
elements.clearLogBtn.addEventListener('click', () => {
elements.consoleLog.innerHTML = '<div class="log-line info">[系统] 日志已清空</div>';
});
// 刷新账号列表
elements.refreshAccountsBtn.addEventListener('click', () => {
loadRecentAccounts();
toast.info('已刷新');
});
}
// 加载可用的邮箱服务
async function loadAvailableServices() {
try {
const data = await api.get('/registration/available-services');
availableServices = data;
// 更新邮箱服务选择框
updateEmailServiceOptions();
addLog('info', '[系统] 邮箱服务列表已加载');
} catch (error) {
console.error('加载邮箱服务列表失败:', error);
addLog('warning', '[警告] 加载邮箱服务列表失败');
}
}
// 更新邮箱服务选择框
function updateEmailServiceOptions() {
const select = elements.emailService;
select.innerHTML = '';
// Tempmail
if (availableServices.tempmail.available) {
const optgroup = document.createElement('optgroup');
optgroup.label = '🌐 临时邮箱';
availableServices.tempmail.services.forEach(service => {
const option = document.createElement('option');
option.value = `tempmail:${service.id || 'default'}`;
option.textContent = service.name;
option.dataset.type = 'tempmail';
optgroup.appendChild(option);
});
select.appendChild(optgroup);
}
// Outlook
if (availableServices.outlook.available) {
const optgroup = document.createElement('optgroup');
optgroup.label = `📧 Outlook (${availableServices.outlook.count} 个账户)`;
availableServices.outlook.services.forEach(service => {
const option = document.createElement('option');
option.value = `outlook:${service.id}`;
option.textContent = service.name + (service.has_oauth ? ' (OAuth)' : '');
option.dataset.type = 'outlook';
option.dataset.serviceId = service.id;
optgroup.appendChild(option);
});
select.appendChild(optgroup);
// Outlook 批量注册选项
const batchOption = document.createElement('option');
batchOption.value = 'outlook_batch:all';
batchOption.textContent = `📋 Outlook 批量注册 (${availableServices.outlook.count} 个账户)`;
batchOption.dataset.type = 'outlook_batch';
optgroup.appendChild(batchOption);
} else {
const optgroup = document.createElement('optgroup');
optgroup.label = '📧 Outlook (未配置)';
const option = document.createElement('option');
option.value = '';
option.textContent = '请先在邮箱服务页面导入账户';
option.disabled = true;
optgroup.appendChild(option);
select.appendChild(optgroup);
}
// 自定义域名
if (availableServices.custom_domain.available) {
const optgroup = document.createElement('optgroup');
optgroup.label = `🔗 自定义域名 (${availableServices.custom_domain.count} 个服务)`;
availableServices.custom_domain.services.forEach(service => {
const option = document.createElement('option');
option.value = `custom_domain:${service.id || 'default'}`;
option.textContent = service.name;
option.dataset.type = 'custom_domain';
if (service.id) {
option.dataset.serviceId = service.id;
}
optgroup.appendChild(option);
});
select.appendChild(optgroup);
} else {
const optgroup = document.createElement('optgroup');
optgroup.label = '🔗 自定义域名 (未配置)';
const option = document.createElement('option');
option.value = '';
option.textContent = '请先在邮箱服务页面添加服务';
option.disabled = true;
optgroup.appendChild(option);
select.appendChild(optgroup);
}
}
// 处理邮箱服务切换
function handleServiceChange(e) {
const value = e.target.value;
if (!value) return;
const [type, id] = value.split(':');
const selectedOption = e.target.options[e.target.selectedIndex];
// 处理 Outlook 批量注册模式
if (type === 'outlook_batch') {
isOutlookBatchMode = true;
elements.outlookBatchSection.style.display = 'block';
elements.regModeGroup.style.display = 'none';
elements.batchCountGroup.style.display = 'none';
elements.batchOptions.style.display = 'none';
loadOutlookAccounts();
addLog('info', '[系统] 已切换到 Outlook 批量注册模式');
return;
} else {
isOutlookBatchMode = false;
elements.outlookBatchSection.style.display = 'none';
elements.regModeGroup.style.display = 'block';
}
// 显示服务信息
if (type === 'outlook') {
const service = availableServices.outlook.services.find(s => s.id == id);
if (service) {
addLog('info', `[系统] 已选择 Outlook 账户: ${service.name}`);
}
} else if (type === 'custom_domain') {
const service = availableServices.custom_domain.services.find(s => s.id == id);
if (service) {
addLog('info', `[系统] 已选择自定义域名服务: ${service.name}`);
}
}
}
// 模式切换
function handleModeChange(e) {
const mode = e.target.value;
isBatchMode = mode === 'batch';
elements.batchCountGroup.style.display = isBatchMode ? 'block' : 'none';
elements.batchOptions.style.display = isBatchMode ? 'block' : 'none';
}
// 开始注册
async function handleStartRegistration(e) {
e.preventDefault();
const selectedValue = elements.emailService.value;
if (!selectedValue) {
toast.error('请选择一个邮箱服务');
return;
}
// 处理 Outlook 批量注册模式
if (isOutlookBatchMode) {
await handleOutlookBatchRegistration();
return;
}
const [emailServiceType, serviceId] = selectedValue.split(':');
// 禁用开始按钮
elements.startBtn.disabled = true;
elements.cancelBtn.disabled = false;
// 清空日志
elements.consoleLog.innerHTML = '';
// 构建请求数据(代理从设置中自动获取)
const requestData = {
email_service_type: emailServiceType
};
// 如果选择了数据库中的服务,传递 service_id
if (serviceId && serviceId !== 'default') {
requestData.email_service_id = parseInt(serviceId);
}
if (isBatchMode) {
await handleBatchRegistration(requestData);
} else {
await handleSingleRegistration(requestData);
}
}
// 单次注册
async function handleSingleRegistration(requestData) {
addLog('info', '[系统] 正在启动注册任务...');
try {
const data = await api.post('/registration/start', requestData);
currentTask = data;
addLog('info', `[系统] 任务已创建: ${data.task_uuid}`);
showTaskStatus(data);
updateTaskStatus('running');
// 开始轮询日志
startLogPolling(data.task_uuid);
} catch (error) {
addLog('error', `[错误] 启动失败: ${error.message}`);
toast.error(error.message);
resetButtons();
}
}
// 批量注册
async function handleBatchRegistration(requestData) {
const count = parseInt(elements.batchCount.value) || 5;
const intervalMin = parseInt(elements.intervalMin.value) || 5;
const intervalMax = parseInt(elements.intervalMax.value) || 30;
requestData.count = count;
requestData.interval_min = intervalMin;
requestData.interval_max = intervalMax;
addLog('info', `[系统] 正在启动批量注册任务 (数量: ${count})...`);
try {
const data = await api.post('/registration/batch', requestData);
currentBatch = data;
addLog('info', `[系统] 批量任务已创建: ${data.batch_id}`);
addLog('info', `[系统] 共 ${data.count} 个任务已加入队列`);
showBatchStatus(data);
// 开始轮询批量状态
startBatchPolling(data.batch_id);
} catch (error) {
addLog('error', `[错误] 启动失败: ${error.message}`);
toast.error(error.message);
resetButtons();
}
}
// 取消任务
async function handleCancelTask() {
if (isBatchMode && currentBatch) {
try {
await api.post(`/registration/batch/${currentBatch.batch_id}/cancel`);
addLog('warning', '[警告] 批量任务取消请求已提交');
toast.info('任务取消请求已提交');
stopBatchPolling();
resetButtons();
} catch (error) {
addLog('error', `[错误] 取消失败: ${error.message}`);
toast.error(error.message);
}
} else if (currentTask) {
try {
await api.post(`/registration/tasks/${currentTask.task_uuid}/cancel`);
addLog('warning', '[警告] 任务已取消');
toast.info('任务已取消');
stopLogPolling();
resetButtons();
} catch (error) {
addLog('error', `[错误] 取消失败: ${error.message}`);
toast.error(error.message);
}
}
}
// 开始轮询日志
function startLogPolling(taskUuid) {
let lastLogIndex = 0;
logPollingInterval = setInterval(async () => {
try {
const data = await api.get(`/registration/tasks/${taskUuid}/logs`);
// 更新任务状态
updateTaskStatus(data.status);
// 更新邮箱信息
if (data.email) {
elements.taskEmail.textContent = data.email;
}
if (data.email_service) {
elements.taskService.textContent = getServiceTypeText(data.email_service);
}
// 添加新日志
const logs = data.logs || [];
for (let i = lastLogIndex; i < logs.length; i++) {
const log = logs[i];
const logType = getLogType(log);
addLog(logType, log);
}
lastLogIndex = logs.length;
// 检查任务是否完成
if (['completed', 'failed', 'cancelled'].includes(data.status)) {
stopLogPolling();
resetButtons();
if (data.status === 'completed') {
addLog('success', '[成功] 注册成功!');
toast.success('注册成功!');
// 刷新账号列表
loadRecentAccounts();
} else if (data.status === 'failed') {
addLog('error', '[错误] 注册失败');
toast.error('注册失败');
} else if (data.status === 'cancelled') {
addLog('warning', '[警告] 任务已取消');
}
}
} catch (error) {
console.error('轮询日志失败:', error);
}
}, 1000);
}
// 停止轮询日志
function stopLogPolling() {
if (logPollingInterval) {
clearInterval(logPollingInterval);
logPollingInterval = null;
}
}
// 开始轮询批量状态
function startBatchPolling(batchId) {
batchPollingInterval = setInterval(async () => {
try {
const data = await api.get(`/registration/batch/${batchId}`);
updateBatchProgress(data);
// 检查是否完成
if (data.finished) {
stopBatchPolling();
resetButtons();
addLog('info', `[完成] 批量任务完成!成功: ${data.success}, 失败: ${data.failed}`);
if (data.success > 0) {
toast.success(`批量注册完成,成功 ${data.success}`);
// 刷新账号列表
loadRecentAccounts();
} else {
toast.warning('批量注册完成,但没有成功注册任何账号');
}
}
} catch (error) {
console.error('轮询批量状态失败:', error);
}
}, 2000);
}
// 停止轮询批量状态
function stopBatchPolling() {
if (batchPollingInterval) {
clearInterval(batchPollingInterval);
batchPollingInterval = null;
}
}
// 显示任务状态
function showTaskStatus(task) {
elements.taskStatusRow.style.display = 'grid';
elements.batchProgressSection.style.display = 'none';
elements.taskStatusBadge.style.display = 'inline-flex';
elements.taskId.textContent = task.task_uuid.substring(0, 8) + '...';
elements.taskEmail.textContent = '-';
elements.taskService.textContent = '-';
}
// 更新任务状态
function updateTaskStatus(status) {
const statusInfo = {
pending: { text: '等待中', class: 'pending' },
running: { text: '运行中', class: 'running' },
completed: { text: '已完成', class: 'completed' },
failed: { text: '失败', class: 'failed' },
cancelled: { text: '已取消', class: 'disabled' }
};
const info = statusInfo[status] || { text: status, class: '' };
elements.taskStatusBadge.textContent = info.text;
elements.taskStatusBadge.className = `status-badge ${info.class}`;
elements.taskStatus.textContent = info.text;
}
// 显示批量状态
function showBatchStatus(batch) {
elements.batchProgressSection.style.display = 'block';
elements.taskStatusRow.style.display = 'none';
elements.taskStatusBadge.style.display = 'none';
elements.batchProgressText.textContent = `0/${batch.count}`;
elements.batchProgressPercent.textContent = '0%';
elements.progressBar.style.width = '0%';
elements.batchSuccess.textContent = '0';
elements.batchFailed.textContent = '0';
elements.batchRemaining.textContent = batch.count;
// 重置计数器
elements.batchSuccess.dataset.last = '0';
elements.batchFailed.dataset.last = '0';
}
// 更新批量进度
function updateBatchProgress(data) {
const progress = ((data.completed / data.total) * 100).toFixed(0);
elements.batchProgressText.textContent = `${data.completed}/${data.total}`;
elements.batchProgressPercent.textContent = `${progress}%`;
elements.progressBar.style.width = `${progress}%`;
elements.batchSuccess.textContent = data.success;
elements.batchFailed.textContent = data.failed;
elements.batchRemaining.textContent = data.total - data.completed;
// 记录日志(避免重复)
if (data.completed > 0) {
const lastSuccess = parseInt(elements.batchSuccess.dataset.last || '0');
const lastFailed = parseInt(elements.batchFailed.dataset.last || '0');
if (data.success > lastSuccess) {
addLog('success', `[成功] 第 ${data.success} 个账号注册成功`);
}
if (data.failed > lastFailed) {
addLog('error', `[失败] 第 ${data.failed} 个账号注册失败`);
}
elements.batchSuccess.dataset.last = data.success;
elements.batchFailed.dataset.last = data.failed;
}
}
// 加载最近注册的账号
async function loadRecentAccounts() {
try {
const data = await api.get('/accounts?page=1&page_size=10');
if (data.accounts.length === 0) {
elements.recentAccountsTable.innerHTML = `
<tr>
<td colspan="5">
<div class="empty-state" style="padding: var(--spacing-md);">
<div class="empty-state-icon">📭</div>
<div class="empty-state-title">暂无已注册账号</div>
</div>
</td>
</tr>
`;
return;
}
elements.recentAccountsTable.innerHTML = data.accounts.map(account => `
<tr data-id="${account.id}">
<td>${account.id}</td>
<td>
<span title="${escapeHtml(account.email)}">${escapeHtml(account.email)}</span>
</td>
<td class="password-cell">
${account.password ? `<span class="password-hidden" title="点击查看">${escapeHtml(account.password.substring(0, 8))}...</span>` : '-'}
</td>
<td>
<span class="status-badge ${getStatusClass('account', account.status)}" style="font-size: 0.7rem;">
${getStatusText('account', account.status)}
</span>
</td>
<td>
<div class="action-buttons">
<button class="btn btn-ghost btn-sm" onclick="copyToClipboard('${escapeHtml(account.email)}')" title="复制邮箱">
📋
</button>
${account.password ? `<button class="btn btn-ghost btn-sm" onclick="copyToClipboard('${escapeHtml(account.password)}')" title="复制密码">🔑</button>` : ''}
</div>
</td>
</tr>
`).join('');
} catch (error) {
console.error('加载账号列表失败:', error);
}
}
// 开始账号列表轮询
function startAccountsPolling() {
// 每30秒刷新一次账号列表
accountsPollingInterval = setInterval(() => {
loadRecentAccounts();
}, 30000);
}
// 添加日志
function addLog(type, message) {
const line = document.createElement('div');
line.className = `log-line ${type}`;
// 添加时间戳
const timestamp = new Date().toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
line.innerHTML = `<span class="timestamp">[${timestamp}]</span>${escapeHtml(message)}`;
elements.consoleLog.appendChild(line);
// 自动滚动到底部
elements.consoleLog.scrollTop = elements.consoleLog.scrollHeight;
// 限制日志行数
const lines = elements.consoleLog.querySelectorAll('.log-line');
if (lines.length > 500) {
lines[0].remove();
}
}
// 获取日志类型
function getLogType(log) {
if (typeof log !== 'string') return 'info';
const lowerLog = log.toLowerCase();
if (lowerLog.includes('error') || lowerLog.includes('失败') || lowerLog.includes('错误')) {
return 'error';
}
if (lowerLog.includes('warning') || lowerLog.includes('警告')) {
return 'warning';
}
if (lowerLog.includes('success') || lowerLog.includes('成功') || lowerLog.includes('完成')) {
return 'success';
}
return 'info';
}
// 重置按钮状态
function resetButtons() {
elements.startBtn.disabled = false;
elements.cancelBtn.disabled = true;
currentTask = null;
currentBatch = null;
isBatchMode = false;
// 注意:不重置 isOutlookBatchMode因为用户可能想继续使用 Outlook 批量模式
}
// HTML 转义
function escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// ============== Outlook 批量注册功能 ==============
// 加载 Outlook 账户列表
async function loadOutlookAccounts() {
try {
elements.outlookAccountsContainer.innerHTML = '<div class="loading-placeholder" style="text-align: center; padding: var(--spacing-md); color: var(--text-muted);">加载中...</div>';
const data = await api.get('/registration/outlook-accounts');
outlookAccounts = data.accounts || [];
renderOutlookAccountsList();
addLog('info', `[系统] 已加载 ${data.total} 个 Outlook 账户 (已注册: ${data.registered_count}, 未注册: ${data.unregistered_count})`);
} catch (error) {
console.error('加载 Outlook 账户列表失败:', error);
elements.outlookAccountsContainer.innerHTML = `<div style="text-align: center; padding: var(--spacing-md); color: var(--text-muted);">加载失败: ${error.message}</div>`;
addLog('error', `[错误] 加载 Outlook 账户列表失败: ${error.message}`);
}
}
// 渲染 Outlook 账户列表
function renderOutlookAccountsList() {
if (outlookAccounts.length === 0) {
elements.outlookAccountsContainer.innerHTML = '<div style="text-align: center; padding: var(--spacing-md); color: var(--text-muted);">没有可用的 Outlook 账户</div>';
return;
}
const html = outlookAccounts.map(account => `
<label class="outlook-account-item" style="display: flex; align-items: center; padding: var(--spacing-sm); border-bottom: 1px solid var(--border-light); cursor: pointer; ${account.is_registered ? 'opacity: 0.6;' : ''}" data-id="${account.id}" data-registered="${account.is_registered}">
<input type="checkbox" class="outlook-account-checkbox" value="${account.id}" ${account.is_registered ? '' : 'checked'} style="margin-right: var(--spacing-sm);">
<div style="flex: 1;">
<div style="font-weight: 500;">${escapeHtml(account.email)}</div>
<div style="font-size: 0.75rem; color: var(--text-muted);">
${account.is_registered
? `<span style="color: var(--success-color);">✓ 已注册</span>`
: '<span style="color: var(--primary-color);">未注册</span>'
}
${account.has_oauth ? ' | OAuth' : ''}
</div>
</div>
</label>
`).join('');
elements.outlookAccountsContainer.innerHTML = html;
}
// 全选
function selectAllOutlookAccounts() {
const checkboxes = document.querySelectorAll('.outlook-account-checkbox');
checkboxes.forEach(cb => cb.checked = true);
}
// 只选未注册
function selectUnregisteredOutlook() {
const items = document.querySelectorAll('.outlook-account-item');
items.forEach(item => {
const checkbox = item.querySelector('.outlook-account-checkbox');
const isRegistered = item.dataset.registered === 'true';
checkbox.checked = !isRegistered;
});
}
// 取消全选
function deselectAllOutlookAccounts() {
const checkboxes = document.querySelectorAll('.outlook-account-checkbox');
checkboxes.forEach(cb => cb.checked = false);
}
// 处理 Outlook 批量注册
async function handleOutlookBatchRegistration() {
// 获取选中的账户
const selectedIds = [];
document.querySelectorAll('.outlook-account-checkbox:checked').forEach(cb => {
selectedIds.push(parseInt(cb.value));
});
if (selectedIds.length === 0) {
toast.error('请选择至少一个 Outlook 账户');
return;
}
const intervalMin = parseInt(elements.outlookIntervalMin.value) || 5;
const intervalMax = parseInt(elements.outlookIntervalMax.value) || 30;
const skipRegistered = elements.outlookSkipRegistered.checked;
// 禁用开始按钮
elements.startBtn.disabled = true;
elements.cancelBtn.disabled = false;
// 清空日志
elements.consoleLog.innerHTML = '';
const requestData = {
service_ids: selectedIds,
skip_registered: skipRegistered,
interval_min: intervalMin,
interval_max: intervalMax
};
addLog('info', `[系统] 正在启动 Outlook 批量注册 (${selectedIds.length} 个账户)...`);
try {
const data = await api.post('/registration/outlook-batch', requestData);
if (data.to_register === 0) {
addLog('warning', '[警告] 所有选中的邮箱都已注册,无需重复注册');
toast.warning('所有选中的邮箱都已注册');
resetButtons();
return;
}
currentBatch = { batch_id: data.batch_id, ...data };
addLog('info', `[系统] 批量任务已创建: ${data.batch_id}`);
addLog('info', `[系统] 总数: ${data.total}, 跳过已注册: ${data.skipped}, 待注册: ${data.to_register}`);
// 初始化批量状态显示
showBatchStatus({ count: data.to_register });
// 开始轮询批量状态
startOutlookBatchPolling(data.batch_id);
} catch (error) {
addLog('error', `[错误] 启动失败: ${error.message}`);
toast.error(error.message);
resetButtons();
}
}
// 开始轮询 Outlook 批量状态
function startOutlookBatchPolling(batchId) {
batchPollingInterval = setInterval(async () => {
try {
const data = await api.get(`/registration/outlook-batch/${batchId}`);
// 更新进度
updateBatchProgress({
total: data.total,
completed: data.completed,
success: data.success,
failed: data.failed
});
// 输出日志
if (data.logs && data.logs.length > 0) {
const lastLogIndex = batchPollingInterval.lastLogIndex || 0;
for (let i = lastLogIndex; i < data.logs.length; i++) {
const log = data.logs[i];
const logType = getLogType(log);
addLog(logType, log);
}
batchPollingInterval.lastLogIndex = data.logs.length;
}
// 检查是否完成
if (data.finished) {
stopBatchPolling();
resetButtons();
addLog('info', `[完成] Outlook 批量任务完成!成功: ${data.success}, 失败: ${data.failed}, 跳过: ${data.skipped || 0}`);
if (data.success > 0) {
toast.success(`Outlook 批量注册完成,成功 ${data.success}`);
loadRecentAccounts();
} else {
toast.warning('Outlook 批量注册完成,但没有成功注册任何账号');
}
}
} catch (error) {
console.error('轮询 Outlook 批量状态失败:', error);
}
}, 2000);
batchPollingInterval.lastLogIndex = 0;
}