mirror of
https://github.com/ddCat-main/cursor-auto-register.git
synced 2025-12-24 13:38:01 +08:00
feat: 添加自定义邮箱注册场景支持,优化验证码处理逻辑。新增清理待处理验证码请求的接口,改进前端提示信息,确保在自动获取验证码失败时转为手动输入模式。
This commit is contained in:
87
api.py
87
api.py
@@ -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模式环境变量
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}")
|
||||
|
||||
16
index.html
16
index.html
@@ -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>
|
||||
|
||||
153
static/js/app.js
153
static/js/app.js
@@ -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 (
|
||||
|
||||
Reference in New Issue
Block a user