mirror of
https://github.com/cnlimiter/codex-register.git
synced 2026-06-09 09:29:39 +08:00
feat(mail): 添加收件箱功能,自动获得验证码
This commit is contained in:
@@ -84,7 +84,7 @@ a = Analysis(
|
|||||||
'src.core.utils',
|
'src.core.utils',
|
||||||
'src.services.base',
|
'src.services.base',
|
||||||
'src.services.tempmail',
|
'src.services.tempmail',
|
||||||
'src.services.custom_domain',
|
'src.services.moe_mail',
|
||||||
'src.services.outlook',
|
'src.services.outlook',
|
||||||
'src.services.outlook.account',
|
'src.services.outlook.account',
|
||||||
'src.services.outlook.base',
|
'src.services.outlook.base',
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class EmailServiceType(str, Enum):
|
|||||||
"""邮箱服务类型"""
|
"""邮箱服务类型"""
|
||||||
TEMPMAIL = "tempmail"
|
TEMPMAIL = "tempmail"
|
||||||
OUTLOOK = "outlook"
|
OUTLOOK = "outlook"
|
||||||
CUSTOM_DOMAIN = "custom_domain"
|
MOE_MAIL = "moe_mail"
|
||||||
TEMP_MAIL = "temp_mail"
|
TEMP_MAIL = "temp_mail"
|
||||||
DUCK_MAIL = "duck_mail"
|
DUCK_MAIL = "duck_mail"
|
||||||
FREEMAIL = "freemail"
|
FREEMAIL = "freemail"
|
||||||
@@ -109,7 +109,7 @@ EMAIL_SERVICE_DEFAULTS = {
|
|||||||
"smtp_port": 587,
|
"smtp_port": 587,
|
||||||
"timeout": 30,
|
"timeout": 30,
|
||||||
},
|
},
|
||||||
"custom_domain": {
|
"moe_mail": {
|
||||||
"base_url": "", # 需要用户配置
|
"base_url": "", # 需要用户配置
|
||||||
"api_key_header": "X-API-Key",
|
"api_key_header": "X-API-Key",
|
||||||
"timeout": 30,
|
"timeout": 30,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class SettingCategory(str, Enum):
|
|||||||
REGISTRATION = "registration"
|
REGISTRATION = "registration"
|
||||||
EMAIL = "email"
|
EMAIL = "email"
|
||||||
TEMPMAIL = "tempmail"
|
TEMPMAIL = "tempmail"
|
||||||
CUSTOM_DOMAIN = "custom_domain"
|
CUSTOM_DOMAIN = "moe_mail"
|
||||||
SECURITY = "security"
|
SECURITY = "security"
|
||||||
CPA = "cpa"
|
CPA = "cpa"
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ SETTING_DEFINITIONS: Dict[str, SettingDefinition] = {
|
|||||||
# 邮箱服务配置
|
# 邮箱服务配置
|
||||||
"email_service_priority": SettingDefinition(
|
"email_service_priority": SettingDefinition(
|
||||||
db_key="email.service_priority",
|
db_key="email.service_priority",
|
||||||
default_value={"tempmail": 0, "outlook": 1, "custom_domain": 2},
|
default_value={"tempmail": 0, "outlook": 1, "moe_mail": 2},
|
||||||
category=SettingCategory.EMAIL,
|
category=SettingCategory.EMAIL,
|
||||||
description="邮箱服务优先级"
|
description="邮箱服务优先级"
|
||||||
),
|
),
|
||||||
@@ -665,7 +665,7 @@ class Settings(BaseModel):
|
|||||||
registration_sleep_max: int = 30
|
registration_sleep_max: int = 30
|
||||||
|
|
||||||
# 邮箱服务配置
|
# 邮箱服务配置
|
||||||
email_service_priority: Dict[str, int] = {"tempmail": 0, "outlook": 1, "custom_domain": 2}
|
email_service_priority: Dict[str, int] = {"tempmail": 0, "outlook": 1, "moe_mail": 2}
|
||||||
|
|
||||||
# Tempmail.lol 配置
|
# Tempmail.lol 配置
|
||||||
tempmail_base_url: str = "https://api.tempmail.lol/v2"
|
tempmail_base_url: str = "https://api.tempmail.lol/v2"
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class Account(Base):
|
|||||||
client_id = Column(String(255)) # OAuth Client ID
|
client_id = Column(String(255)) # OAuth Client ID
|
||||||
account_id = Column(String(255))
|
account_id = Column(String(255))
|
||||||
workspace_id = Column(String(255))
|
workspace_id = Column(String(255))
|
||||||
email_service = Column(String(50), nullable=False) # 'tempmail', 'outlook', 'custom_domain'
|
email_service = Column(String(50), nullable=False) # 'tempmail', 'outlook', 'moe_mail'
|
||||||
email_service_id = Column(String(255)) # 邮箱服务中的ID
|
email_service_id = Column(String(255)) # 邮箱服务中的ID
|
||||||
proxy_used = Column(String(255))
|
proxy_used = Column(String(255))
|
||||||
registered_at = Column(DateTime, default=datetime.utcnow)
|
registered_at = Column(DateTime, default=datetime.utcnow)
|
||||||
@@ -89,7 +89,7 @@ class EmailService(Base):
|
|||||||
__tablename__ = 'email_services'
|
__tablename__ = 'email_services'
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
service_type = Column(String(50), nullable=False) # 'outlook', 'custom_domain'
|
service_type = Column(String(50), nullable=False) # 'outlook', 'moe_mail'
|
||||||
name = Column(String(100), nullable=False)
|
name = Column(String(100), nullable=False)
|
||||||
config = Column(JSONEncodedDict, nullable=False) # 服务配置(加密存储)
|
config = Column(JSONEncodedDict, nullable=False) # 服务配置(加密存储)
|
||||||
enabled = Column(Boolean, default=True)
|
enabled = Column(Boolean, default=True)
|
||||||
|
|||||||
@@ -117,6 +117,14 @@ class DatabaseSessionManager:
|
|||||||
Base.metadata.create_all(bind=self.engine)
|
Base.metadata.create_all(bind=self.engine)
|
||||||
|
|
||||||
with self.engine.connect() as conn:
|
with self.engine.connect() as conn:
|
||||||
|
# 数据迁移:将旧的 custom_domain 记录统一为 moe_mail
|
||||||
|
try:
|
||||||
|
conn.execute(text("UPDATE email_services SET service_type='moe_mail' WHERE service_type='custom_domain'"))
|
||||||
|
conn.execute(text("UPDATE accounts SET email_service='moe_mail' WHERE email_service='custom_domain'"))
|
||||||
|
conn.commit()
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"迁移 custom_domain -> moe_mail 时出错: {e}")
|
||||||
|
|
||||||
for table_name, column_name, column_type in migrations:
|
for table_name, column_name, column_type in migrations:
|
||||||
try:
|
try:
|
||||||
# 检查列是否存在
|
# 检查列是否存在
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from .freemail import FreemailService
|
|||||||
# 注册服务
|
# 注册服务
|
||||||
EmailServiceFactory.register(EmailServiceType.TEMPMAIL, TempmailService)
|
EmailServiceFactory.register(EmailServiceType.TEMPMAIL, TempmailService)
|
||||||
EmailServiceFactory.register(EmailServiceType.OUTLOOK, OutlookService)
|
EmailServiceFactory.register(EmailServiceType.OUTLOOK, OutlookService)
|
||||||
EmailServiceFactory.register(EmailServiceType.CUSTOM_DOMAIN, MeoMailEmailService)
|
EmailServiceFactory.register(EmailServiceType.MOE_MAIL, MeoMailEmailService)
|
||||||
EmailServiceFactory.register(EmailServiceType.TEMP_MAIL, TempMailService)
|
EmailServiceFactory.register(EmailServiceType.TEMP_MAIL, TempMailService)
|
||||||
EmailServiceFactory.register(EmailServiceType.DUCK_MAIL, DuckMailService)
|
EmailServiceFactory.register(EmailServiceType.DUCK_MAIL, DuckMailService)
|
||||||
EmailServiceFactory.register(EmailServiceType.FREEMAIL, FreemailService)
|
EmailServiceFactory.register(EmailServiceType.FREEMAIL, FreemailService)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class MeoMailEmailService(BaseEmailService):
|
|||||||
- default_expiry: 默认过期时间(毫秒)
|
- default_expiry: 默认过期时间(毫秒)
|
||||||
name: 服务名称
|
name: 服务名称
|
||||||
"""
|
"""
|
||||||
super().__init__(EmailServiceType.CUSTOM_DOMAIN, name)
|
super().__init__(EmailServiceType.MOE_MAIL, name)
|
||||||
|
|
||||||
# 必需配置检查
|
# 必需配置检查
|
||||||
required_keys = ["base_url", "api_key"]
|
required_keys = ["base_url", "api_key"]
|
||||||
|
|||||||
@@ -950,3 +950,105 @@ async def upload_account_to_tm(account_id: int, request: Optional[UploadTMReques
|
|||||||
success, message = upload_to_team_manager(account, api_url, api_key)
|
success, message = upload_to_team_manager(account, api_url, api_key)
|
||||||
|
|
||||||
return {"success": success, "message": message}
|
return {"success": success, "message": message}
|
||||||
|
|
||||||
|
|
||||||
|
# ============== Inbox Code ==============
|
||||||
|
|
||||||
|
def _build_inbox_config(db, service_type, email: str) -> dict:
|
||||||
|
"""根据账号邮箱服务类型从数据库构建服务配置(不传 proxy_url)"""
|
||||||
|
from ...database.models import EmailService as EmailServiceModel
|
||||||
|
from ...services import EmailServiceType as EST
|
||||||
|
|
||||||
|
if service_type == EST.TEMPMAIL:
|
||||||
|
settings = get_settings()
|
||||||
|
return {
|
||||||
|
"base_url": settings.tempmail_base_url,
|
||||||
|
"timeout": settings.tempmail_timeout,
|
||||||
|
"max_retries": settings.tempmail_max_retries,
|
||||||
|
}
|
||||||
|
|
||||||
|
if service_type == EST.MOE_MAIL:
|
||||||
|
# 按域名后缀匹配,找不到则取 priority 最小的
|
||||||
|
domain = email.split("@")[1] if "@" in email else ""
|
||||||
|
services = db.query(EmailServiceModel).filter(
|
||||||
|
EmailServiceModel.service_type == "moe_mail",
|
||||||
|
EmailServiceModel.enabled == True
|
||||||
|
).order_by(EmailServiceModel.priority.asc()).all()
|
||||||
|
svc = None
|
||||||
|
for s in services:
|
||||||
|
cfg = s.config or {}
|
||||||
|
if cfg.get("default_domain") == domain or cfg.get("domain") == domain:
|
||||||
|
svc = s
|
||||||
|
break
|
||||||
|
if not svc and services:
|
||||||
|
svc = services[0]
|
||||||
|
if not svc:
|
||||||
|
return None
|
||||||
|
cfg = svc.config.copy()
|
||||||
|
if "api_url" in cfg and "base_url" not in cfg:
|
||||||
|
cfg["base_url"] = cfg.pop("api_url")
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
# 其余服务类型:直接按 service_type 查数据库
|
||||||
|
type_map = {
|
||||||
|
EST.TEMP_MAIL: "temp_mail",
|
||||||
|
EST.DUCK_MAIL: "duck_mail",
|
||||||
|
EST.FREEMAIL: "freemail",
|
||||||
|
EST.OUTLOOK: "outlook",
|
||||||
|
}
|
||||||
|
db_type = type_map.get(service_type)
|
||||||
|
if not db_type:
|
||||||
|
return None
|
||||||
|
|
||||||
|
query = db.query(EmailServiceModel).filter(
|
||||||
|
EmailServiceModel.service_type == db_type,
|
||||||
|
EmailServiceModel.enabled == True
|
||||||
|
)
|
||||||
|
if service_type == EST.OUTLOOK:
|
||||||
|
# 按 config.email 匹配账号 email
|
||||||
|
services = query.all()
|
||||||
|
svc = next((s for s in services if (s.config or {}).get("email") == email), None)
|
||||||
|
else:
|
||||||
|
svc = query.order_by(EmailServiceModel.priority.asc()).first()
|
||||||
|
|
||||||
|
if not svc:
|
||||||
|
return None
|
||||||
|
cfg = svc.config.copy() if svc.config else {}
|
||||||
|
if "api_url" in cfg and "base_url" not in cfg:
|
||||||
|
cfg["base_url"] = cfg.pop("api_url")
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{account_id}/inbox-code")
|
||||||
|
async def get_account_inbox_code(account_id: int):
|
||||||
|
"""查询账号邮箱收件箱最新验证码"""
|
||||||
|
from ...services import EmailServiceFactory, EmailServiceType
|
||||||
|
|
||||||
|
with get_db() as db:
|
||||||
|
account = crud.get_account_by_id(db, account_id)
|
||||||
|
if not account:
|
||||||
|
raise HTTPException(status_code=404, detail="账号不存在")
|
||||||
|
|
||||||
|
try:
|
||||||
|
service_type = EmailServiceType(account.email_service)
|
||||||
|
except ValueError:
|
||||||
|
return {"success": False, "error": "不支持的邮箱服务类型"}
|
||||||
|
|
||||||
|
config = _build_inbox_config(db, service_type, account.email)
|
||||||
|
if config is None:
|
||||||
|
return {"success": False, "error": "未找到可用的邮箱服务配置"}
|
||||||
|
|
||||||
|
try:
|
||||||
|
svc = EmailServiceFactory.create(service_type, config)
|
||||||
|
code = svc.get_verification_code(
|
||||||
|
account.email,
|
||||||
|
email_id=account.email_service_id,
|
||||||
|
timeout=12
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return {"success": False, "error": str(e)}
|
||||||
|
|
||||||
|
if not code:
|
||||||
|
return {"success": False, "error": "未收到验证码邮件"}
|
||||||
|
|
||||||
|
return {"success": True, "code": code, "email": account.email}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ async def get_email_services_stats():
|
|||||||
for service_type, count in type_stats:
|
for service_type, count in type_stats:
|
||||||
if service_type == 'outlook':
|
if service_type == 'outlook':
|
||||||
stats['outlook_count'] = count
|
stats['outlook_count'] = count
|
||||||
elif service_type == 'custom_domain':
|
elif service_type == 'moe_mail':
|
||||||
stats['custom_count'] = count
|
stats['custom_count'] = count
|
||||||
elif service_type == 'temp_mail':
|
elif service_type == 'temp_mail':
|
||||||
stats['temp_mail_count'] = count
|
stats['temp_mail_count'] = count
|
||||||
@@ -191,8 +191,8 @@ async def get_service_types():
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": "custom_domain",
|
"value": "moe_mail",
|
||||||
"label": "自定义域名",
|
"label": "MoeMail",
|
||||||
"description": "自定义域名邮箱服务",
|
"description": "自定义域名邮箱服务",
|
||||||
"config_fields": [
|
"config_fields": [
|
||||||
{"name": "base_url", "label": "API 地址", "required": True},
|
{"name": "base_url", "label": "API 地址", "required": True},
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ def _normalize_email_service_config(
|
|||||||
if 'api_url' in normalized and 'base_url' not in normalized:
|
if 'api_url' in normalized and 'base_url' not in normalized:
|
||||||
normalized['base_url'] = normalized.pop('api_url')
|
normalized['base_url'] = normalized.pop('api_url')
|
||||||
|
|
||||||
if service_type == EmailServiceType.CUSTOM_DOMAIN:
|
if service_type == EmailServiceType.MOE_MAIL:
|
||||||
if 'domain' in normalized and 'default_domain' not in normalized:
|
if 'domain' in normalized and 'default_domain' not in normalized:
|
||||||
normalized['default_domain'] = normalized.pop('domain')
|
normalized['default_domain'] = normalized.pop('domain')
|
||||||
elif service_type in (EmailServiceType.TEMP_MAIL, EmailServiceType.FREEMAIL):
|
elif service_type in (EmailServiceType.TEMP_MAIL, EmailServiceType.FREEMAIL):
|
||||||
@@ -291,11 +291,11 @@ def _run_sync_registration_task(task_uuid: str, email_service_type: str, proxy:
|
|||||||
"max_retries": settings.tempmail_max_retries,
|
"max_retries": settings.tempmail_max_retries,
|
||||||
"proxy_url": actual_proxy_url,
|
"proxy_url": actual_proxy_url,
|
||||||
}
|
}
|
||||||
elif service_type == EmailServiceType.CUSTOM_DOMAIN:
|
elif service_type == EmailServiceType.MOE_MAIL:
|
||||||
# 检查数据库中是否有可用的自定义域名服务
|
# 检查数据库中是否有可用的自定义域名服务
|
||||||
from ...database.models import EmailService as EmailServiceModel
|
from ...database.models import EmailService as EmailServiceModel
|
||||||
db_service = db.query(EmailServiceModel).filter(
|
db_service = db.query(EmailServiceModel).filter(
|
||||||
EmailServiceModel.service_type == "custom_domain",
|
EmailServiceModel.service_type == "moe_mail",
|
||||||
EmailServiceModel.enabled == True
|
EmailServiceModel.enabled == True
|
||||||
).order_by(EmailServiceModel.priority.asc()).first()
|
).order_by(EmailServiceModel.priority.asc()).first()
|
||||||
|
|
||||||
@@ -796,7 +796,7 @@ async def start_registration(
|
|||||||
"""
|
"""
|
||||||
启动注册任务
|
启动注册任务
|
||||||
|
|
||||||
- email_service_type: 邮箱服务类型 (tempmail, outlook, custom_domain)
|
- email_service_type: 邮箱服务类型 (tempmail, outlook, moe_mail)
|
||||||
- proxy: 代理地址
|
- proxy: 代理地址
|
||||||
- email_service_config: 邮箱服务配置(outlook 需要提供账户信息)
|
- email_service_config: 邮箱服务配置(outlook 需要提供账户信息)
|
||||||
"""
|
"""
|
||||||
@@ -1069,7 +1069,7 @@ async def get_available_email_services():
|
|||||||
返回所有已启用的邮箱服务,包括:
|
返回所有已启用的邮箱服务,包括:
|
||||||
- tempmail: 临时邮箱(无需配置)
|
- tempmail: 临时邮箱(无需配置)
|
||||||
- outlook: 已导入的 Outlook 账户
|
- outlook: 已导入的 Outlook 账户
|
||||||
- custom_domain: 已配置的自定义域名服务
|
- moe_mail: 已配置的自定义域名服务
|
||||||
"""
|
"""
|
||||||
from ...database.models import EmailService as EmailServiceModel
|
from ...database.models import EmailService as EmailServiceModel
|
||||||
from ...config.settings import get_settings
|
from ...config.settings import get_settings
|
||||||
@@ -1091,7 +1091,7 @@ async def get_available_email_services():
|
|||||||
"count": 0,
|
"count": 0,
|
||||||
"services": []
|
"services": []
|
||||||
},
|
},
|
||||||
"custom_domain": {
|
"moe_mail": {
|
||||||
"available": False,
|
"available": False,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"services": []
|
"services": []
|
||||||
@@ -1135,32 +1135,32 @@ async def get_available_email_services():
|
|||||||
|
|
||||||
# 获取自定义域名服务
|
# 获取自定义域名服务
|
||||||
custom_services = db.query(EmailServiceModel).filter(
|
custom_services = db.query(EmailServiceModel).filter(
|
||||||
EmailServiceModel.service_type == "custom_domain",
|
EmailServiceModel.service_type == "moe_mail",
|
||||||
EmailServiceModel.enabled == True
|
EmailServiceModel.enabled == True
|
||||||
).order_by(EmailServiceModel.priority.asc()).all()
|
).order_by(EmailServiceModel.priority.asc()).all()
|
||||||
|
|
||||||
for service in custom_services:
|
for service in custom_services:
|
||||||
config = service.config or {}
|
config = service.config or {}
|
||||||
result["custom_domain"]["services"].append({
|
result["moe_mail"]["services"].append({
|
||||||
"id": service.id,
|
"id": service.id,
|
||||||
"name": service.name,
|
"name": service.name,
|
||||||
"type": "custom_domain",
|
"type": "moe_mail",
|
||||||
"default_domain": config.get("default_domain"),
|
"default_domain": config.get("default_domain"),
|
||||||
"priority": service.priority
|
"priority": service.priority
|
||||||
})
|
})
|
||||||
|
|
||||||
result["custom_domain"]["count"] = len(custom_services)
|
result["moe_mail"]["count"] = len(custom_services)
|
||||||
result["custom_domain"]["available"] = len(custom_services) > 0
|
result["moe_mail"]["available"] = len(custom_services) > 0
|
||||||
|
|
||||||
# 如果数据库中没有自定义域名服务,检查 settings
|
# 如果数据库中没有自定义域名服务,检查 settings
|
||||||
if not result["custom_domain"]["available"]:
|
if not result["moe_mail"]["available"]:
|
||||||
if settings.custom_domain_base_url and settings.custom_domain_api_key:
|
if settings.custom_domain_base_url and settings.custom_domain_api_key:
|
||||||
result["custom_domain"]["available"] = True
|
result["moe_mail"]["available"] = True
|
||||||
result["custom_domain"]["count"] = 1
|
result["moe_mail"]["count"] = 1
|
||||||
result["custom_domain"]["services"].append({
|
result["moe_mail"]["services"].append({
|
||||||
"id": None,
|
"id": None,
|
||||||
"name": "默认自定义域名服务",
|
"name": "默认自定义域名服务",
|
||||||
"type": "custom_domain",
|
"type": "moe_mail",
|
||||||
"from_settings": True
|
"from_settings": True
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -329,6 +329,7 @@ function renderAccounts(accounts) {
|
|||||||
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeMoreMenu(this);refreshToken(${account.id})">刷新</a>
|
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeMoreMenu(this);refreshToken(${account.id})">刷新</a>
|
||||||
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeMoreMenu(this);uploadAccount(${account.id})">上传</a>
|
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeMoreMenu(this);uploadAccount(${account.id})">上传</a>
|
||||||
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeMoreMenu(this);markSubscription(${account.id})">标记</a>
|
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeMoreMenu(this);markSubscription(${account.id})">标记</a>
|
||||||
|
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeMoreMenu(this);checkInboxCode(${account.id})">收件箱</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-danger btn-sm" onclick="deleteAccount(${account.id}, '${escapeHtml(account.email)}')">删除</button>
|
<button class="btn btn-danger btn-sm" onclick="deleteAccount(${account.id}, '${escapeHtml(account.email)}')">删除</button>
|
||||||
@@ -1217,3 +1218,34 @@ async function saveCookies(id) {
|
|||||||
toast.error('保存 Cookies 失败: ' + e.message);
|
toast.error('保存 Cookies 失败: ' + e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询收件箱验证码
|
||||||
|
async function checkInboxCode(id) {
|
||||||
|
toast.info('正在查询收件箱...');
|
||||||
|
try {
|
||||||
|
const result = await api.post(`/accounts/${id}/inbox-code`);
|
||||||
|
if (result.success) {
|
||||||
|
showInboxCodeResult(result.code, result.email);
|
||||||
|
} else {
|
||||||
|
toast.error('查询失败: ' + (result.error || '未收到验证码'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast.error('查询失败: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showInboxCodeResult(code, email) {
|
||||||
|
elements.modalBody.innerHTML = `
|
||||||
|
<div style="text-align:center; padding:24px 16px;">
|
||||||
|
<div style="font-size:13px;color:var(--text-muted);margin-bottom:12px;">
|
||||||
|
${escapeHtml(email)} 最新验证码
|
||||||
|
</div>
|
||||||
|
<div style="font-size:36px;font-weight:700;letter-spacing:8px;
|
||||||
|
color:var(--primary);font-family:monospace;margin-bottom:20px;">
|
||||||
|
${escapeHtml(code)}
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" onclick="copyToClipboard('${escapeHtml(code)}')">复制验证码</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
elements.detailModal.classList.add('active');
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ let toastShown = false; // 标记是否已显示过 toast
|
|||||||
let availableServices = {
|
let availableServices = {
|
||||||
tempmail: { available: true, services: [] },
|
tempmail: { available: true, services: [] },
|
||||||
outlook: { available: false, services: [] },
|
outlook: { available: false, services: [] },
|
||||||
custom_domain: { available: false, services: [] },
|
moe_mail: { available: false, services: [] },
|
||||||
temp_mail: { available: false, services: [] },
|
temp_mail: { available: false, services: [] },
|
||||||
duck_mail: { available: false, services: [] },
|
duck_mail: { available: false, services: [] },
|
||||||
freemail: { available: false, services: [] }
|
freemail: { available: false, services: [] }
|
||||||
@@ -293,15 +293,15 @@ function updateEmailServiceOptions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 自定义域名
|
// 自定义域名
|
||||||
if (availableServices.custom_domain.available) {
|
if (availableServices.moe_mail.available) {
|
||||||
const optgroup = document.createElement('optgroup');
|
const optgroup = document.createElement('optgroup');
|
||||||
optgroup.label = `🔗 自定义域名 (${availableServices.custom_domain.count} 个服务)`;
|
optgroup.label = `🔗 自定义域名 (${availableServices.moe_mail.count} 个服务)`;
|
||||||
|
|
||||||
availableServices.custom_domain.services.forEach(service => {
|
availableServices.moe_mail.services.forEach(service => {
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
option.value = `custom_domain:${service.id || 'default'}`;
|
option.value = `moe_mail:${service.id || 'default'}`;
|
||||||
option.textContent = service.name + (service.default_domain ? ` (@${service.default_domain})` : '');
|
option.textContent = service.name + (service.default_domain ? ` (@${service.default_domain})` : '');
|
||||||
option.dataset.type = 'custom_domain';
|
option.dataset.type = 'moe_mail';
|
||||||
if (service.id) {
|
if (service.id) {
|
||||||
option.dataset.serviceId = service.id;
|
option.dataset.serviceId = service.id;
|
||||||
}
|
}
|
||||||
@@ -402,8 +402,8 @@ function handleServiceChange(e) {
|
|||||||
if (service) {
|
if (service) {
|
||||||
addLog('info', `[系统] 已选择 Outlook 账户: ${service.name}`);
|
addLog('info', `[系统] 已选择 Outlook 账户: ${service.name}`);
|
||||||
}
|
}
|
||||||
} else if (type === 'custom_domain') {
|
} else if (type === 'moe_mail') {
|
||||||
const service = availableServices.custom_domain.services.find(s => s.id == id);
|
const service = availableServices.moe_mail.services.find(s => s.id == id);
|
||||||
if (service) {
|
if (service) {
|
||||||
addLog('info', `[系统] 已选择自定义域名服务: ${service.name}`);
|
addLog('info', `[系统] 已选择自定义域名服务: ${service.name}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
// 状态
|
// 状态
|
||||||
let outlookServices = [];
|
let outlookServices = [];
|
||||||
let customServices = []; // 合并 custom_domain + temp_mail + duck_mail
|
let customServices = []; // 合并 moe_mail + temp_mail + duck_mail
|
||||||
let selectedOutlook = new Set();
|
let selectedOutlook = new Set();
|
||||||
let selectedCustom = new Set();
|
let selectedCustom = new Set();
|
||||||
|
|
||||||
@@ -293,11 +293,11 @@ function getCustomServiceAddress(service) {
|
|||||||
return `${escapeHtml(baseUrl)}<div style="color: var(--text-muted); margin-top: 4px;">默认域名:@${escapeHtml(domain)}</div>`;
|
return `${escapeHtml(baseUrl)}<div style="color: var(--text-muted); margin-top: 4px;">默认域名:@${escapeHtml(domain)}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载自定义邮箱服务(custom_domain + temp_mail + duck_mail + freemail 合并)
|
// 加载自定义邮箱服务(moe_mail + temp_mail + duck_mail + freemail 合并)
|
||||||
async function loadCustomServices() {
|
async function loadCustomServices() {
|
||||||
try {
|
try {
|
||||||
const [r1, r2, r3, r4] = await Promise.all([
|
const [r1, r2, r3, r4] = await Promise.all([
|
||||||
api.get('/email-services?service_type=custom_domain'),
|
api.get('/email-services?service_type=moe_mail'),
|
||||||
api.get('/email-services?service_type=temp_mail'),
|
api.get('/email-services?service_type=temp_mail'),
|
||||||
api.get('/email-services?service_type=duck_mail'),
|
api.get('/email-services?service_type=duck_mail'),
|
||||||
api.get('/email-services?service_type=freemail')
|
api.get('/email-services?service_type=freemail')
|
||||||
@@ -422,7 +422,7 @@ async function handleAddCustom(e) {
|
|||||||
|
|
||||||
let serviceType, config;
|
let serviceType, config;
|
||||||
if (subType === 'moemail') {
|
if (subType === 'moemail') {
|
||||||
serviceType = 'custom_domain';
|
serviceType = 'moe_mail';
|
||||||
config = {
|
config = {
|
||||||
base_url: formData.get('api_url'),
|
base_url: formData.get('api_url'),
|
||||||
api_key: formData.get('api_key'),
|
api_key: formData.get('api_key'),
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ const statusMap = {
|
|||||||
service: {
|
service: {
|
||||||
tempmail: 'Tempmail.lol',
|
tempmail: 'Tempmail.lol',
|
||||||
outlook: 'Outlook',
|
outlook: 'Outlook',
|
||||||
custom_domain: '自定义域名',
|
moe_mail: 'MoeMail',
|
||||||
temp_mail: 'Temp-Mail(自部署)',
|
temp_mail: 'Temp-Mail(自部署)',
|
||||||
duck_mail: 'DuckMail',
|
duck_mail: 'DuckMail',
|
||||||
freemail: 'Freemail'
|
freemail: 'Freemail'
|
||||||
|
|||||||
@@ -109,7 +109,7 @@
|
|||||||
<option value="">全部邮箱服务</option>
|
<option value="">全部邮箱服务</option>
|
||||||
<option value="tempmail">Tempmail</option>
|
<option value="tempmail">Tempmail</option>
|
||||||
<option value="outlook">Outlook</option>
|
<option value="outlook">Outlook</option>
|
||||||
<option value="custom_domain">自定义域名</option>
|
<option value="moe_mail">MoeMail</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<input type="text" id="search-input" class="form-input" placeholder="🔍 搜索邮箱..." style="min-width: 200px;">
|
<input type="text" id="search-input" class="form-input" placeholder="🔍 搜索邮箱..." style="min-width: 200px;">
|
||||||
|
|||||||
@@ -178,7 +178,7 @@
|
|||||||
<select id="email-service" name="email_service" required>
|
<select id="email-service" name="email_service" required>
|
||||||
<option value="tempmail:default">Tempmail.lol (临时邮箱)</option>
|
<option value="tempmail:default">Tempmail.lol (临时邮箱)</option>
|
||||||
<option value="outlook">Outlook</option>
|
<option value="outlook">Outlook</option>
|
||||||
<option value="custom_domain">自定义域名</option>
|
<option value="moe_mail">MoeMail</option>
|
||||||
<option value="temp_mail">Temp-Mail 自部署</option>
|
<option value="temp_mail">Temp-Mail 自部署</option>
|
||||||
<option value="duck_mail">DuckMail</option>
|
<option value="duck_mail">DuckMail</option>
|
||||||
<option value="outlook_batch:all">Outlook 批量注册</option>
|
<option value="outlook_batch:all">Outlook 批量注册</option>
|
||||||
|
|||||||
Reference in New Issue
Block a user