feat: 添加自定义邮箱注册场景支持,优化验证码处理逻辑。新增清理待处理验证码请求的接口,改进前端提示信息,确保在自动获取验证码失败时转为手动输入模式。

This commit is contained in:
real-jacket
2025-04-01 21:49:03 +08:00
parent c721b2bb26
commit d155a2bcd7
5 changed files with 369 additions and 68 deletions

87
api.py
View File

@@ -30,6 +30,9 @@ from dotenv import load_dotenv
import json
import time
import uuid
import aiosqlite
import sqlite3
import logging
# 从get_email_code.py导入验证码请求字典
from get_email_code import EmailVerificationHandler
@@ -711,6 +714,12 @@ async def stop_registration():
background_tasks["registration_task"] = None
registration_status["is_running"] = False
registration_status["last_status"] = "manually stopped"
# 清理所有待处理的验证码请求
for email_id, request_info in list(get_email_code.pending_verification_codes.items()):
if request_info["status"] == "pending":
get_email_code.pending_verification_codes.pop(email_id, None)
info(f"已清理待处理的验证码请求: {email_id}")
return {"success": True, "message": "Registration task stopped successfully"}
except Exception as e:
@@ -1475,23 +1484,48 @@ async def register_with_custom_email(request: CustomRegistrationRequest):
# 调用注册函数,传入自定义邮箱
try:
success = await asyncio.get_event_loop().run_in_executor(
result = await asyncio.get_event_loop().run_in_executor(
None, lambda: register_account(custom_email=request.email)
)
if success:
# 处理布尔值情况 - True表示成功False表示失败
if result is True:
return JSONResponse(
content={
"success": True,
"message": f"使用邮箱 {request.email} 注册成功"
}
)
elif result is False:
return JSONResponse(
status_code=500,
content={
"success": False,
"message": f"使用邮箱 {request.email} 注册失败,请检查日志获取详细信息"
}
)
# 字符串状态码处理
elif result == "SUCCESS":
return JSONResponse(
content={
"success": True,
"message": f"使用邮箱 {request.email} 注册成功"
}
)
elif result == "EMAIL_VERIFICATION_FAILED":
return JSONResponse(
status_code=400,
content={
"success": False,
"message": "验证码获取失败,请检查邮箱是否正确或尝试其他邮箱"
}
)
else:
return JSONResponse(
status_code=500,
content={
"success": False,
"message": f"使用邮箱 {request.email} 注册失败"
"message": f"使用邮箱 {request.email} 注册失败: {result}"
}
)
except SystemExit:
@@ -1518,15 +1552,30 @@ async def register_with_custom_email(request: CustomRegistrationRequest):
@app.get("/verification/pending")
async def check_pending_verification():
"""检查是否有等待验证码输入的请求"""
# 返回所有等待验证的邮箱ID及邮箱地址
result = []
for email_id, email_info in get_email_code.pending_verification_codes.items():
if email_info.get("status") == "pending":
if email_info["status"] == "pending":
request_data = {
"id": email_id,
"email": email_info["email"],
"created_at": email_info["created_at"]
}
# 如果存在auto_failure标记添加到结果中
if "auto_failure" in email_info:
request_data["auto_failure"] = email_info["auto_failure"]
result.append(request_data)
elif email_info["status"] == "failed":
# 返回失败状态的请求,让前端知道验证失败
result.append({
"id": email_id,
"email": email_info.get("email"),
"created_at": email_info.get("created_at")
"email": email_info["email"],
"created_at": email_info["created_at"],
"status": "failed",
"message": email_info.get("message", "验证码获取失败")
})
# 返回后从字典中删除,避免重复通知
get_email_code.pending_verification_codes.pop(email_id, None)
return {
"success": True,
@@ -1560,6 +1609,30 @@ async def submit_verification_code(code_data: Dict):
"message": "验证码已提交"
}
@app.get("/verification/clear", tags=["Verification"])
async def clear_verification_requests():
"""清理所有待处理的验证码请求"""
try:
count = 0
for email_id, request_info in list(get_email_code.pending_verification_codes.items()):
if request_info["status"] == "pending":
get_email_code.pending_verification_codes.pop(email_id, None)
count += 1
info(f"已清理 {count} 个待处理的验证码请求")
return {
"success": True,
"message": f"已清理 {count} 个待处理的验证码请求"
}
except Exception as e:
error(f"清理验证码请求失败: {str(e)}")
error(traceback.format_exc())
return {
"success": False,
"message": f"清理验证码请求失败: {str(e)}"
}
if __name__ == "__main__":
# 设置Web模式环境变量

View File

@@ -225,8 +225,13 @@ def sign_up_account(browser, tab, account_info):
info("注册限制")
return "SIGNUP_RESTRICTED"
# 创建邮件处理器,传入自定义邮箱
email_handler = EmailVerificationHandler(custom_email=account_info["email"])
# 判断是否为自定义邮箱注册场景
is_custom_email_registration = account_info.get("is_custom_registration", False)
# 创建邮件处理器,只在自定义邮箱注册场景下传入邮箱参数
email_handler = EmailVerificationHandler(
custom_email=account_info["email"] if is_custom_email_registration else None
)
i = 0
while i < 5:
try:
@@ -241,15 +246,20 @@ def sign_up_account(browser, tab, account_info):
global EMAIL_CODE_TYPE
# 获取当前的EMAIL_CODE_TYPE值
local_email_code_type = EMAIL_CODE_TYPE
if account_info["email"] != f"{EMAIL_USERNAME}@{EMAIL_DOMAIN}":
info(f"检测到使用自定义邮箱 {account_info['email']},确保使用手动输入验证码")
# 使用自定义邮箱时确保使用手动输入
if EMAIL_CODE_TYPE != "INPUT":
EMAIL_CODE_TYPE = "INPUT"
# 切换到邮箱标签页获取验证码
if is_custom_email_registration:
info(f"检测到自定义邮箱注册场景,使用手动输入验证码模式")
# 自定义邮箱注册场景确保使用手动输入
if EMAIL_CODE_TYPE != "INPUT":
# 临时切换为手动输入模式,在获取验证码完成后会恢复原值
EMAIL_CODE_TYPE = "INPUT"
else:
info(f"检测到任务注册场景,使用配置的验证码获取方式: {EMAIL_CODE_TYPE}")
# 获取验证码 - 对于自定义邮箱EmailVerificationHandler会直接进入手动输入模式
# 不会尝试从临时邮箱服务获取验证码
code = email_handler.get_verification_code(
source_email=account_info["email"]
source_email=account_info["email"] if is_custom_email_registration else None
)
# 如果临时修改了EMAIL_CODE_TYPE恢复原值
@@ -258,7 +268,14 @@ def sign_up_account(browser, tab, account_info):
if code is None:
info("未获取到验证码...系统异常,正在退出....")
return "EMAIL_GET_CODE_FAILED"
return "EMAIL_VERIFICATION_FAILED"
# 在自动获取验证码失败时可能已转为手动输入模式
if not is_custom_email_registration and EMAIL_CODE_TYPE != "INPUT":
info(f"成功获取到验证码: {code},继续注册流程")
else:
info(f"通过手动输入获取到验证码: {code},继续注册流程")
info(f"输入验证码: {code}")
i = 0
for digit in code:
@@ -447,15 +464,17 @@ def main(custom_email=None):
try:
# 如果使用自定义邮箱,切换验证码获取模式为手动输入
if custom_email:
info(f"使用自定义邮箱 {custom_email},切换为手动输入验证码模式")
info(f"使用自定义邮箱注册场景 {custom_email},切换为手动输入验证码模式")
EMAIL_CODE_TYPE = "INPUT"
else:
info(f"使用任务注册场景,使用配置的验证码获取方式: {EMAIL_CODE_TYPE}")
# 初始化邮箱验证处理器时传入自定义邮箱
email_handler = EmailVerificationHandler(custom_email=custom_email)
if email_handler.check():
info('邮箱服务连接正常,开始注册!')
else:
if EMAIL_CODE_TYPE == "API":
if EMAIL_CODE_TYPE == "API" and not custom_email:
error('邮箱服务连接失败并且验证码为API获取结束注册!')
return False
else:
@@ -468,8 +487,15 @@ def main(custom_email=None):
try:
# 使用自定义邮箱或生成新邮箱
account_info = email_generator.get_account_info(email=custom_email)
# 标记是否为自定义邮箱注册场景
if custom_email:
account_info["is_custom_registration"] = True
else:
account_info["is_custom_registration"] = False
info(
f"初始化账号信息成功 => 邮箱: {account_info['email']}, 用户名: {account_info['first_name']}, 密码: {account_info['password']}"
f"初始化账号信息成功 => 邮箱: {account_info['email']}, 用户名: {account_info['first_name']}, 密码: {account_info['password']}, 场景: {'自定义邮箱' if account_info['is_custom_registration'] else '任务注册'}"
)
signup_tab = browser.new_tab(LOGIN_URL)

View File

@@ -63,6 +63,11 @@ class EmailVerificationHandler:
info(f"已启用代理: {EMAIL_PROXY_ADDRESS}")
def check(self):
# 如果是自定义邮箱场景,跳过邮箱连接检查,直接返回成功
if self.custom_email is not None:
info(f"自定义邮箱注册场景 {self.custom_email},跳过临时邮箱连接检查")
return True
# 如果有自定义邮箱,优先使用它
username = self.username
domain = self.domain
@@ -119,19 +124,18 @@ class EmailVerificationHandler:
# 记录当前的验证码获取模式和邮箱信息
info(f"验证码获取模式: {EMAIL_CODE_TYPE}, 使用邮箱: {source_email}")
# 首先检查是否自定义邮箱(非系统配置的邮箱)
if source_email and '@' in source_email:
username, domain = source_email.split('@', 1)
if username != EMAIL_USERNAME or domain != EMAIL_DOMAIN:
info(f"检测到使用非系统配置邮箱: {source_email},强制使用手动输入模式")
return self.prompt_manual_code(source_email)
# 通过self.custom_email判断是否自定义邮箱注册场景而不是比较source_email
is_custom_email_scenario = self.custom_email is not None
# 如果是INPUT模式始终直接进入手动输入
if EMAIL_CODE_TYPE == "INPUT":
info("EMAIL_CODE_TYPE设为INPUT跳过自动获取直接手动输入")
# 如果是INPUT模式或者是自定义邮箱场景,直接进入手动输入
if EMAIL_CODE_TYPE == "INPUT" or is_custom_email_scenario:
if is_custom_email_scenario:
info("检测到自定义邮箱注册场景,直接使用手动输入验证码模式")
else:
info("EMAIL_CODE_TYPE设为INPUT跳过自动获取直接手动输入")
return self.prompt_manual_code(source_email)
# 以下是自动获取验证码的逻辑只有在EMAIL_CODE_TYPE不是INPUT且不是自定义邮箱时才会执行
# 以下是自动获取验证码的逻辑只有在EMAIL_CODE_TYPE不是INPUT且不是自定义邮箱场景时才会执行
max_retries = max_retries or EMAIL_VERIFICATION_RETRIES
wait_time = wait_time or EMAIL_VERIFICATION_WAIT
info(f"开始获取邮箱验证码=>最大重试次数:{max_retries}, 等待时间:{wait_time}")
@@ -169,7 +173,25 @@ class EmailVerificationHandler:
else:
error(f"已达到最大重试次数({max_retries}),获取验证码失败")
# 所有自动尝试都失败后,询问是否手动输入
# Web模式下更新等待验证码请求状态为失败
web_mode = os.environ.get("CURSOR_AUTO_REGISTER_WEB", "").lower() == "true"
if web_mode and source_email:
# 查找该邮箱的待处理验证码请求
global pending_verification_codes
for email_id, request_info in list(pending_verification_codes.items()):
if request_info.get("email") == source_email and request_info.get("status") == "pending":
# 更新状态为失败
pending_verification_codes[email_id]["status"] = "failed"
pending_verification_codes[email_id]["message"] = f"验证码获取失败,已尝试{max_retries}"
info(f"已更新验证码请求 {email_id} 状态为失败")
break
# 在Web模式下自动获取失败后转为前端手动输入而不直接返回失败
if web_mode:
info("自动获取验证码失败,转为前端手动输入模式")
return self.prompt_manual_code(source_email)
# 命令行模式下询问手动输入
response = input("自动获取验证码失败,是否手动输入? (y/n): ").lower()
if response == 'y':
return self.prompt_manual_code(source_email)
@@ -201,16 +223,23 @@ class EmailVerificationHandler:
# 生成唯一ID
email_id = str(uuid.uuid4())
# 判断是否是自动获取失败后转为手动输入
is_auto_failure = EMAIL_CODE_TYPE != "INPUT"
# 存储到等待字典中
global pending_verification_codes
pending_verification_codes[email_id] = {
"email": source_email,
"status": "pending",
"created_at": datetime.now().isoformat(),
"code": None
"code": None,
"auto_failure": is_auto_failure # 标记是否是自动获取失败后的手动输入
}
info(f"已创建验证码请求 ID: {email_id},等待前端输入验证码")
if is_auto_failure:
info(f"已创建验证码请求 ID: {email_id},这是在自动获取失败后转为手动输入")
else:
info(f"已创建验证码请求 ID: {email_id},等待前端输入验证码")
# 循环等待验证码输入最多等待180秒
start_time = time.time()
@@ -232,14 +261,25 @@ class EmailVerificationHandler:
return None
def get_tempmail_email_code(self, source_email=None):
# 如果是自定义邮箱场景直接返回None不尝试通过临时邮箱API获取验证码
if self.custom_email is not None:
info("自定义邮箱注册场景不应通过临时邮箱API获取验证码跳过此步骤")
return None, None
info("开始获取邮件列表")
# 如果提供了source_email且当前不是使用custom_email则尝试解析它
if source_email and source_email != f"{self.username}@{self.domain}" and '@' in source_email:
# 确保在任务注册场景(非自定义邮箱)下使用配置邮箱
# 仅当source_email存在且与配置邮箱不同时才使用source_email(自定义邮箱场景)
using_custom_email = (source_email is not None and
'@' in source_email and
source_email != f"{self.username}@{self.domain}")
if using_custom_email:
username, domain = source_email.split('@', 1)
mail_list_url = f"https://tempmail.plus/api/mails?email={username}%40{domain}&limit=20&epin={self.pin}"
info(f"使用自定义邮箱获取验证码: {source_email}")
else:
# 任务注册场景,使用配置邮箱
mail_list_url = f"https://tempmail.plus/api/mails?email={self.username}%40{self.domain}&limit=20&epin={self.pin}"
info(f"使用配置邮箱获取验证码: {self.username}@{self.domain}")
@@ -276,11 +316,12 @@ class EmailVerificationHandler:
return None, None
info(f"开始获取邮件详情: {first_id}")
# 使用相同的用户名和域名获取邮件详情
if source_email and source_email != f"{self.username}@{self.domain}" and '@' in source_email:
username, domain = source_email.split('@', 1)
# 使用与邮件列表相同的邮箱账号获取邮件详情
if using_custom_email:
# 自定义邮箱场景
mail_detail_url = f"https://tempmail.plus/api/mails/{first_id}?email={username}%40{domain}&epin={self.pin}"
else:
# 任务注册场景,使用配置邮箱
mail_detail_url = f"https://tempmail.plus/api/mails/{first_id}?email={self.username}%40{self.domain}&epin={self.pin}"
try:
@@ -305,17 +346,20 @@ class EmailVerificationHandler:
# 从邮件文本中提取6位数字验证码
mail_text = mail_detail_data.get("text", "")
# 如果提供了source_email确保邮件内容包含该邮箱地址
if source_email and source_email.lower() not in mail_text.lower():
# 在自定义邮箱场景下检查邮件内容包含该邮箱,任务注册场景不需要检查
if using_custom_email and source_email and source_email.lower() not in mail_text.lower():
error(f"邮件内容不包含指定的邮箱地址: {source_email}")
else:
info(f"邮件内容包含指定的邮箱地址: {source_email}")
if using_custom_email:
info(f"邮件内容包含指定的邮箱地址: {source_email}")
else:
info(f"使用配置邮箱获取验证码,不需要检查邮件内容")
code_match = re.search(r"(?<![a-zA-Z@.])\b\d{6}\b", mail_text)
if code_match:
# 清理邮件
self._cleanup_mail(first_id, source_email)
# 清理邮件时使用正确的邮箱
self._cleanup_mail(first_id, source_email if using_custom_email else None)
return code_match.group(), first_id
return None, None
except requests.exceptions.Timeout:
@@ -329,12 +373,21 @@ class EmailVerificationHandler:
return None, None
def _cleanup_mail(self, first_id, source_email=None):
# 如果提供了source_email,优先使用它
username = self.username
domain = self.domain
if source_email and '@' in source_email:
# 如果提供了source_email且不为空,则判断是否为自定义邮箱场景
using_custom_email = (source_email is not None and
'@' in source_email and
source_email != f"{self.username}@{self.domain}")
# 设置用于清理邮件的用户名和域名
if using_custom_email:
# 自定义邮箱场景
username, domain = source_email.split('@', 1)
info(f"使用自定义邮箱清理邮件: {username}@{domain}")
else:
# 任务注册场景,使用配置邮箱
username = self.username
domain = self.domain
info(f"使用配置邮箱清理邮件: {username}@{domain}")
# 构造删除请求的URL和数据
delete_url = "https://tempmail.plus/api/mails/"
@@ -399,10 +452,28 @@ class EmailVerificationHandler:
# 获取zmail邮箱验证码
def get_zmail_email_code(self, source_email=None):
# 如果是自定义邮箱场景直接返回None不尝试通过zmail API获取验证码
if self.custom_email is not None:
info("自定义邮箱注册场景不应通过zmail API获取验证码跳过此步骤")
return None, None
info("开始获取邮件列表")
# 确保在任务注册场景(非自定义邮箱)下使用配置邮箱
# 仅当source_email存在且与配置邮箱不同时才使用source_email(自定义邮箱场景)
using_custom_email = (source_email is not None and
'@' in source_email and
source_email != f"{self.username}@{self.domain}")
# 获取邮件列表
# 优先使用传入的source_email其次是自定义邮箱最后才是配置中的邮箱
email_to_use = source_email if source_email else (self.custom_email if self.custom_email else f"{self.username}@{self.domain}")
if using_custom_email:
# 自定义邮箱场景
email_to_use = source_email
info(f"使用自定义邮箱获取验证码: {email_to_use}")
else:
# 任务注册场景,使用配置邮箱
email_to_use = f"{self.username}@{self.domain}"
info(f"使用配置邮箱获取验证码: {email_to_use}")
if '@' not in email_to_use:
error(f"邮箱格式错误: {email_to_use}")

View File

@@ -157,6 +157,18 @@
</div>
</div>
<!-- 添加验证码获取提示 -->
<div class="alert alert-info mt-3" id="verification-tip-alert">
<button type="button" class="btn-close float-end" aria-label="关闭"
onclick="$('#verification-tip-alert').hide()"></button>
<div class="d-flex align-items-center">
<i class="fas fa-info-circle me-2"></i>
<div>
<strong>验证码提示:</strong> 在任务注册过程中,如果自动获取验证码失败,系统将自动转为前端手动输入模式,请留意弹窗提示并及时输入验证码。
</div>
</div>
</div>
<div id="registration-details" class="mt-3" style="display: none;">
<div class="alert alert-info">
<div class="d-flex align-items-center">
@@ -693,6 +705,10 @@
<div class="modal-body">
<p>请检查以下邮箱中收到的验证码:</p>
<p class="fw-bold text-primary" id="verificationEmailDisplay"></p>
<!-- 添加验证码提示信息区域 -->
<div id="verification-message"></div>
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
<span>通常验证码为6位数字在邮件正文中</span>

View File

@@ -63,6 +63,9 @@ function initializeApplication() {
// 绑定模态框内按钮事件
bindModalEvents();
// 绑定验证码相关事件
bindVerificationEvents();
// 为所有模态框绑定全局事件
setupGlobalModalEvents();
@@ -78,9 +81,6 @@ function initializeApplication() {
// 绑定所有事件处理函数
bindEventHandlers();
// 启动验证码请求检查
startVerificationCodeCheck();
// 初始清理背景遮罩,确保页面加载时没有残留
cleanupModalBackdrops();
@@ -234,14 +234,20 @@ function bindEventHandlers() {
// 编辑配置按钮点击事件
$('#edit-config-btn')
.off('click')
.on('click', function () {
.on('click', function (e) {
// 阻止事件冒泡,防止可能的表单提交
e.preventDefault();
e.stopPropagation();
enableConfigForm(true);
});
// 取消配置编辑按钮点击事件
$('#cancel-config-btn')
.off('click')
.on('click', function () {
.on('click', function (e) {
// 阻止事件冒泡,防止可能的表单提交
e.preventDefault();
e.stopPropagation();
enableConfigForm(false);
loadConfig(); // 重新加载原始配置
});
@@ -1003,6 +1009,12 @@ function startTaskManually() {
if (data.success) {
showAlert('定时任务已成功启动', 'success');
checkTaskStatus();
// 显示验证码提示框
$('#verification-tip-alert').show();
// 启动验证码检查
startVerificationCodeCheck();
} else {
showAlert(`启动任务失败: ${data.message || '未知错误'}`, 'danger');
}
@@ -1026,12 +1038,18 @@ function stopTaskManually() {
if (data.success) {
showAlert('定时任务已成功停止', 'success');
// 隐藏验证码提示框
$('#verification-tip-alert').hide();
// 立即更新任务状态 - 添加这段代码
fetch('/registration/status')
.then((res) => res.json())
.then((statusData) => {
updateTaskStatusUI(statusData);
});
// 停止验证码检查
stopVerificationCodeCheck();
} else {
showAlert(`停止任务失败: ${data.message || '未知错误'}`, 'danger');
}
@@ -1123,6 +1141,9 @@ function maskPassword(password) {
// 页面加载动画
document.addEventListener('DOMContentLoaded', function () {
// 默认隐藏验证码提示框
$('#verification-tip-alert').hide();
// 修改动画类添加代码,删除对已删除元素的引用
const elements = [
{ selector: '.card', animation: 'animate__fadeIn', delay: 0.2 },
@@ -1583,6 +1604,9 @@ function loadConfig() {
$('#zmail-fields').show();
}
// 配置加载完毕后,立即更新任务状态显示
checkTaskStatus();
hideLoading();
} else {
showAlert('danger', '加载配置失败: ' + response.message);
@@ -1608,7 +1632,8 @@ function toggleProxySettings() {
// 添加配置保存回调,支持重启
function saveConfig() {
showLoading();
const isDynamicUA = $(this).prop('checked');
// 修复不依赖this上下文直接获取动态UA的状态
const isDynamicUA = $('#dynamic-useragent').is(':checked');
const configData = {
BROWSER_HEADLESS: $('#browser-headless').val() === 'true',
DYNAMIC_USERAGENT: isDynamicUA,
@@ -1638,6 +1663,9 @@ function saveConfig() {
success: function (response) {
hideLoading();
if (response.success) {
// 保存成功后立即更新任务状态显示
checkTaskStatus();
// 添加重启询问提示
showConfirmDialog(
'配置已成功保存',
@@ -2218,6 +2246,9 @@ function registerWithCustomEmail() {
showCustomRegistrationStatus('注册中,请稍候...', 'info');
$('#custom-registration').prop('disabled', true);
// 启动验证码检查
startVerificationCodeCheck();
// 调用API
$.ajax({
url: '/registration/custom',
@@ -2232,9 +2263,13 @@ function registerWithCustomEmail() {
// 刷新账号列表
setTimeout(function () {
loadAccounts(1, itemsPerPage);
// 注册成功后停止验证码检查
stopVerificationCodeCheck();
}, 2000);
} else {
showCustomRegistrationStatus('注册失败: ' + response.message, 'danger');
// 注册失败后停止验证码检查
stopVerificationCodeCheck();
}
$('#custom-registration').prop('disabled', false);
},
@@ -2248,6 +2283,17 @@ function registerWithCustomEmail() {
}
showCustomRegistrationStatus('注册失败: ' + message, 'danger');
$('#custom-registration').prop('disabled', false);
// 注册错误后停止验证码检查
stopVerificationCodeCheck();
},
// 添加complete回调确保无论如何都会停止验证码检查
complete: function () {
// 在所有请求完成后(无论成功或失败)确保验证码检查已停止
setTimeout(() => {
if (verificationCheckTimer) {
stopVerificationCodeCheck();
}
}, 1000);
},
});
}
@@ -2360,6 +2406,9 @@ function startVerificationCodeCheck() {
clearInterval(verificationCheckTimer);
}
// 显示验证码提示框
$('#verification-tip-alert').show();
// 每5秒检查一次是否有等待验证码输入的请求
verificationCheckTimer = setInterval(checkPendingVerification, 5000);
}
@@ -2368,6 +2417,31 @@ function stopVerificationCodeCheck() {
if (verificationCheckTimer) {
clearInterval(verificationCheckTimer);
verificationCheckTimer = null;
// 隐藏验证码提示框
$('#verification-tip-alert').hide();
// 调用后端接口清理所有待处理的验证码请求
fetch('/verification/clear')
.then((response) => response.json())
.then((data) => {
if (data.success) {
console.log('已清理待处理的验证码请求:', data.message);
// 如果当前有打开的验证码输入弹窗,关闭它
if ($('#codeInputModal').hasClass('show')) {
const modalElement = document.getElementById('codeInputModal');
const modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) {
modalInstance.hide();
setTimeout(() => cleanupModalBackdrops(), 300);
}
}
}
})
.catch((error) => {
console.error('清理验证码请求失败:', error);
});
}
}
@@ -2386,7 +2460,20 @@ function checkPendingVerification() {
})
.then((data) => {
if (data.success && data.data && data.data.length > 0) {
// 找到等待验证码输入的请求,显示模态框
// 检查是否有失败的验证码请求
const failedRequest = data.data.find((req) => req.status === 'failed');
if (failedRequest) {
// 显示验证失败信息并更新内容,但不停止验证码检查,因为系统会转为手动模式
showAlert(
`自动获取验证码失败: ${
failedRequest.message || '请检查邮箱设置'
}。系统已自动转为手动输入模式,请等待验证码输入弹窗。`,
'warning'
);
return;
}
// 处理正常的验证码请求
const pendingRequest = data.data[0]; // 取第一个请求
// 确保没有其他模态框正在显示
@@ -2421,6 +2508,24 @@ function showVerificationModal(pendingRequest) {
// 清空验证码输入框
$('#verificationCode').val('');
// 判断是否是自动失败后的手动输入请求
const isAutoFailureCase =
pendingRequest.hasOwnProperty('auto_failure') &&
pendingRequest.auto_failure === true;
// 设置模态框标题和内容,根据场景提供不同提示
if (pendingRequest.email.includes('@') && isAutoFailureCase) {
// 自动获取失败后的手动输入
$('#codeInputModalLabel').text('任务注册 - 手动输入验证码');
$('#verification-message').html(
'<div class="alert alert-warning mb-3">自动获取验证码失败,请手动输入验证码以继续注册流程。</div>'
);
} else if (pendingRequest.email.includes('@')) {
// 普通的手动输入验证码
$('#codeInputModalLabel').text('请输入验证码');
$('#verification-message').html('');
}
// 显示模态框前先清理可能存在的背景
cleanupModalBackdrops();
@@ -2590,7 +2695,19 @@ function submitVerificationCode() {
setTimeout(() => cleanupModalBackdrops(), 300);
// 如果验证成功,可以重新加载账号列表
setTimeout(fetchAccounts, 2000);
showAlert('等待账号注册完成...', 'info');
setTimeout(fetchAccounts, 10000); // 增加等待时间到10秒
// 任务可能已经完成,检查是否需要隐藏提示
fetch('/registration/status')
.then((res) => res.json())
.then((status) => {
if (status.task_status === 'stopped') {
$('#verification-tip-alert').hide();
stopVerificationCodeCheck();
}
})
.catch((error) => console.error('获取任务状态失败:', error));
}
} else {
showAlert(`提交验证码失败: ${data.message || '未知错误'}`, 'danger');
@@ -2645,20 +2762,18 @@ function fetchAccounts() {
})
.then((data) => {
if (data.success) {
// 保存所有账号数据
allAccounts = data.accounts || [];
// 保存账号数据
accounts = data.data || [];
// 应用过滤和排序
applyFiltersAndSort();
// 直接更新账号表格,不需要额外的过滤和排序
updateAccountsTable(accounts);
// 更新分页显示
$('#total-accounts').text(filteredAccounts.length);
// 更新显示
renderAccountsTable();
// 更新分页信息
updatePagination(data.pagination.page, data.pagination.total_pages);
$('#total-accounts').text(data.pagination.total_count);
} else {
showAlert(
`加载账号失败: ${data.message || '服务器返回错误'}`,
'加载账号失败: ' + (data.message || '服务器返回错误'),
'danger'
);
console.error('加载账号数据失败:', data);
@@ -2666,7 +2781,7 @@ function fetchAccounts() {
})
.catch((error) => {
console.error('获取账号列表时发生错误:', error);
showAlert(`加载账号失败: ${error.message}`, 'danger');
showAlert('加载账号失败: ' + error.message, 'danger');
// 如果是网络错误,显示更详细的提示
if (