mirror of
https://github.com/cnlimiter/codex-register.git
synced 2026-05-11 18:10:53 +08:00
2
This commit is contained in:
360
static/js/app.js
Normal file
360
static/js/app.js
Normal file
@@ -0,0 +1,360 @@
|
||||
/**
|
||||
* 注册页面 JavaScript
|
||||
*/
|
||||
|
||||
// API 基础路径
|
||||
const API_BASE = '/api';
|
||||
|
||||
// 状态
|
||||
let currentTask = null;
|
||||
let currentBatch = null;
|
||||
let logPollingInterval = null;
|
||||
let batchPollingInterval = null;
|
||||
let isBatchMode = false;
|
||||
|
||||
// DOM 元素
|
||||
const registrationForm = document.getElementById('registration-form');
|
||||
const emailServiceSelect = document.getElementById('email-service');
|
||||
const proxyInput = document.getElementById('proxy');
|
||||
const regModeSelect = document.getElementById('reg-mode');
|
||||
const batchCountGroup = document.getElementById('batch-count-group');
|
||||
const batchCountInput = document.getElementById('batch-count');
|
||||
const batchOptions = document.getElementById('batch-options');
|
||||
const intervalMinInput = document.getElementById('interval-min');
|
||||
const intervalMaxInput = document.getElementById('interval-max');
|
||||
const startBtn = document.getElementById('start-btn');
|
||||
const cancelBtn = document.getElementById('cancel-btn');
|
||||
const taskStatusCard = document.getElementById('task-status-card');
|
||||
const batchStatusCard = document.getElementById('batch-status-card');
|
||||
const consoleLog = document.getElementById('console-log');
|
||||
const clearLogBtn = document.getElementById('clear-log-btn');
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initEventListeners();
|
||||
});
|
||||
|
||||
// 事件监听
|
||||
function initEventListeners() {
|
||||
// 注册表单提交
|
||||
registrationForm.addEventListener('submit', handleStartRegistration);
|
||||
|
||||
// 注册模式切换
|
||||
regModeSelect.addEventListener('change', handleModeChange);
|
||||
|
||||
// 取消按钮
|
||||
cancelBtn.addEventListener('click', handleCancelTask);
|
||||
|
||||
// 清空日志
|
||||
clearLogBtn.addEventListener('click', () => {
|
||||
consoleLog.innerHTML = '<div class="log-line info">[*] 日志已清空</div>';
|
||||
});
|
||||
}
|
||||
|
||||
// 模式切换
|
||||
function handleModeChange(e) {
|
||||
const mode = e.target.value;
|
||||
isBatchMode = mode === 'batch';
|
||||
|
||||
batchCountGroup.style.display = isBatchMode ? 'block' : 'none';
|
||||
batchOptions.style.display = isBatchMode ? 'block' : 'none';
|
||||
}
|
||||
|
||||
// 开始注册
|
||||
async function handleStartRegistration(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const emailService = emailServiceSelect.value;
|
||||
const proxy = proxyInput.value.trim() || null;
|
||||
|
||||
// 禁用开始按钮
|
||||
startBtn.disabled = true;
|
||||
cancelBtn.disabled = false;
|
||||
|
||||
// 清空日志
|
||||
consoleLog.innerHTML = '';
|
||||
|
||||
if (isBatchMode) {
|
||||
await handleBatchRegistration(emailService, proxy);
|
||||
} else {
|
||||
await handleSingleRegistration(emailService, proxy);
|
||||
}
|
||||
}
|
||||
|
||||
// 单次注册
|
||||
async function handleSingleRegistration(emailService, proxy) {
|
||||
addLog('info', '[*] 正在启动注册任务...');
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/registration/start`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email_service_type: emailService,
|
||||
proxy: proxy,
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
currentTask = data;
|
||||
addLog('info', `[*] 任务已创建: ${data.task_uuid}`);
|
||||
showTaskStatus(data);
|
||||
|
||||
// 开始轮询日志
|
||||
startLogPolling(data.task_uuid);
|
||||
} else {
|
||||
addLog('error', `[Error] 启动失败: ${data.detail || '未知错误'}`);
|
||||
resetButtons();
|
||||
}
|
||||
} catch (error) {
|
||||
addLog('error', `[Error] 网络错误: ${error.message}`);
|
||||
resetButtons();
|
||||
}
|
||||
}
|
||||
|
||||
// 批量注册
|
||||
async function handleBatchRegistration(emailService, proxy) {
|
||||
const count = parseInt(batchCountInput.value) || 5;
|
||||
const intervalMin = parseInt(intervalMinInput.value) || 5;
|
||||
const intervalMax = parseInt(intervalMaxInput.value) || 30;
|
||||
|
||||
addLog('info', `[*] 正在启动批量注册任务 (数量: ${count})...`);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/registration/batch`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
count: count,
|
||||
email_service_type: emailService,
|
||||
proxy: proxy,
|
||||
interval_min: intervalMin,
|
||||
interval_max: intervalMax,
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
currentBatch = data;
|
||||
addLog('info', `[*] 批量任务已创建: ${data.batch_id}`);
|
||||
addLog('info', `[*] 共 ${data.count} 个任务已加入队列`);
|
||||
showBatchStatus(data);
|
||||
|
||||
// 开始轮询批量状态
|
||||
startBatchPolling(data.batch_id);
|
||||
} else {
|
||||
addLog('error', `[Error] 启动失败: ${data.detail || '未知错误'}`);
|
||||
resetButtons();
|
||||
}
|
||||
} catch (error) {
|
||||
addLog('error', `[Error] 网络错误: ${error.message}`);
|
||||
resetButtons();
|
||||
}
|
||||
}
|
||||
|
||||
// 取消任务
|
||||
async function handleCancelTask() {
|
||||
if (isBatchMode && currentBatch) {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/registration/batch/${currentBatch.batch_id}/cancel`, {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
addLog('warning', '[!] 批量任务取消请求已提交');
|
||||
stopBatchPolling();
|
||||
resetButtons();
|
||||
}
|
||||
} catch (error) {
|
||||
addLog('error', `[Error] 取消失败: ${error.message}`);
|
||||
}
|
||||
} else if (currentTask) {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/registration/tasks/${currentTask.task_uuid}/cancel`, {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
addLog('warning', '[!] 任务已取消');
|
||||
stopLogPolling();
|
||||
resetButtons();
|
||||
}
|
||||
} catch (error) {
|
||||
addLog('error', `[Error] 取消失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 开始轮询日志
|
||||
function startLogPolling(taskUuid) {
|
||||
let lastLogLine = '';
|
||||
|
||||
logPollingInterval = setInterval(async () => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/registration/tasks/${taskUuid}/logs`);
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
// 更新任务状态
|
||||
updateTaskStatus(data.status);
|
||||
|
||||
// 添加新日志
|
||||
const logs = data.logs || [];
|
||||
logs.forEach(log => {
|
||||
if (log !== lastLogLine) {
|
||||
const logType = getLogType(log);
|
||||
addLog(logType, log);
|
||||
lastLogLine = log;
|
||||
}
|
||||
});
|
||||
|
||||
// 检查任务是否完成
|
||||
if (['completed', 'failed', 'cancelled'].includes(data.status)) {
|
||||
stopLogPolling();
|
||||
resetButtons();
|
||||
|
||||
if (data.status === 'completed') {
|
||||
addLog('success', '[*] 注册成功!');
|
||||
} else if (data.status === 'failed') {
|
||||
addLog('error', '[Error] 注册失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('轮询日志失败:', error);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 停止轮询日志
|
||||
function stopLogPolling() {
|
||||
if (logPollingInterval) {
|
||||
clearInterval(logPollingInterval);
|
||||
logPollingInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 开始轮询批量状态
|
||||
function startBatchPolling(batchId) {
|
||||
batchPollingInterval = setInterval(async () => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/registration/batch/${batchId}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
updateBatchProgress(data);
|
||||
|
||||
// 检查是否完成
|
||||
if (data.finished) {
|
||||
stopBatchPolling();
|
||||
resetButtons();
|
||||
|
||||
addLog('info', `[*] 批量任务完成!成功: ${data.success}, 失败: ${data.failed}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('轮询批量状态失败:', error);
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// 停止轮询批量状态
|
||||
function stopBatchPolling() {
|
||||
if (batchPollingInterval) {
|
||||
clearInterval(batchPollingInterval);
|
||||
batchPollingInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示任务状态
|
||||
function showTaskStatus(task) {
|
||||
taskStatusCard.style.display = 'block';
|
||||
batchStatusCard.style.display = 'none';
|
||||
document.getElementById('task-id').textContent = task.task_uuid;
|
||||
updateTaskStatus(task.status);
|
||||
}
|
||||
|
||||
// 更新任务状态
|
||||
function updateTaskStatus(status) {
|
||||
const statusBadge = document.getElementById('task-status-badge');
|
||||
const statusText = document.getElementById('task-status');
|
||||
|
||||
const statusMap = {
|
||||
'pending': { text: '等待中', class: '' },
|
||||
'running': { text: '运行中', class: 'running' },
|
||||
'completed': { text: '已完成', class: 'completed' },
|
||||
'failed': { text: '失败', class: 'failed' },
|
||||
'cancelled': { text: '已取消', class: '' },
|
||||
};
|
||||
|
||||
const info = statusMap[status] || { text: status, class: '' };
|
||||
statusBadge.textContent = info.text;
|
||||
statusBadge.className = 'status-badge ' + info.class;
|
||||
statusText.textContent = info.text;
|
||||
}
|
||||
|
||||
// 显示批量状态
|
||||
function showBatchStatus(batch) {
|
||||
batchStatusCard.style.display = 'block';
|
||||
taskStatusCard.style.display = 'none';
|
||||
document.getElementById('batch-progress').textContent = `0/${batch.count}`;
|
||||
document.getElementById('progress-bar').style.width = '0%';
|
||||
document.getElementById('batch-success').textContent = '0';
|
||||
document.getElementById('batch-failed').textContent = '0';
|
||||
document.getElementById('batch-remaining').textContent = batch.count;
|
||||
}
|
||||
|
||||
// 更新批量进度
|
||||
function updateBatchProgress(data) {
|
||||
const progress = data.completed / data.total * 100;
|
||||
document.getElementById('batch-progress').textContent = data.progress;
|
||||
document.getElementById('progress-bar').style.width = `${progress}%`;
|
||||
document.getElementById('batch-success').textContent = data.success;
|
||||
document.getElementById('batch-failed').textContent = data.failed;
|
||||
document.getElementById('batch-remaining').textContent = data.total - data.completed;
|
||||
|
||||
// 记录日志
|
||||
if (data.completed > 0) {
|
||||
addLog('info', `[*] 进度: ${data.progress}, 成功: ${data.success}, 失败: ${data.failed}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加日志
|
||||
function addLog(type, message) {
|
||||
const line = document.createElement('div');
|
||||
line.className = `log-line ${type}`;
|
||||
line.textContent = message;
|
||||
consoleLog.appendChild(line);
|
||||
|
||||
// 自动滚动到底部
|
||||
consoleLog.scrollTop = consoleLog.scrollHeight;
|
||||
}
|
||||
|
||||
// 获取日志类型
|
||||
function getLogType(log) {
|
||||
if (log.includes('[Error]') || log.includes('失败') || log.includes('错误')) {
|
||||
return 'error';
|
||||
}
|
||||
if (log.includes('[!]') || log.includes('警告')) {
|
||||
return 'warning';
|
||||
}
|
||||
if (log.includes('成功') || log.includes('完成')) {
|
||||
return 'success';
|
||||
}
|
||||
return 'info';
|
||||
}
|
||||
|
||||
// 重置按钮状态
|
||||
function resetButtons() {
|
||||
startBtn.disabled = false;
|
||||
cancelBtn.disabled = true;
|
||||
currentTask = null;
|
||||
currentBatch = null;
|
||||
}
|
||||
Reference in New Issue
Block a user