feat(auth): 邮箱验证码登录通道(正式兜底,替代不可用的短信)——/auth/email/code + /auth/email/login,SmtpEmailSender(stdlib smtplib,无新依赖)+ FakeEmailSender(仅本地);频控对齐短信口径(60s 冷却/邮箱 1h≤5·24h≤10/IP≤20/设备≤10);新增 EMAIL_*/SMTP_* 配置与 40112/40113/42912/42913/61001 错误码

This commit is contained in:
xsl
2026-09-15 00:02:55 +08:00
parent 5c4f1fc75a
commit df460eeea8
8 changed files with 1212 additions and 3 deletions
+30
View File
@@ -207,6 +207,25 @@ class Settings(BaseSettings):
sms_ip_daily_limit: int = 20
sms_device_daily_limit: int = 10
# --- 邮箱验证码登录(FR-ACC-14 v2 变更:短信通道由邮箱通道替代)------------
# fake = 验证码明文进日志(仅限本地开发,生产必须切 smtp);
# smtp = stdlib smtplib 经 anyio.to_thread 同步发送(不引 aiosmtplib 依赖)。
email_provider: str = "fake"
smtp_host: str = "" # 空 = smtp provider 不可装配(构造即 61001,不带病上岗)
smtp_port: int = 465
smtp_user: str = ""
smtp_pass: str = ""
smtp_use_ssl: bool = True
mail_from: str = "" # 空 = 发件人回退用 SMTP_USER
email_code_ttl_seconds: int = 300
email_code_length: int = 6
email_verify_max_attempts: int = 5
email_resend_cooldown_seconds: int = 60
email_hourly_limit: int = 5
email_daily_limit: int = 10
email_ip_daily_limit: int = 20
email_device_daily_limit: int = 10
# --- 极简运营后台(T-P2-09)----------------------------------------------
# 空字符串 = 后台全部端点 404(等价于未部署)。V1 极简鉴权:共享令牌,
# 后续接运营账号体系时替换。令牌只进 .env / SSM,绝不进代码。
@@ -230,6 +249,16 @@ class Settings(BaseSettings):
],
alias="REPORT_PIPELINE_STAGES_JSON",
)
# 深度诊断(tier=full)流水线:无视觉阶段(复用 basic 报告的 derived_features),
# 诊断装配复用,文案/校验换 full 专用阶段(四大块结构化产出)。
report_full_pipeline_stages: list[str] = Field(
default_factory=lambda: [
"packages.report.diagnosis_stage:DiagnosisStage",
"packages.report.full_copy_stage:FullCopyStage",
"packages.report.full_validation_stage:FullValidationStage",
],
alias="REPORT_FULL_PIPELINE_STAGES_JSON",
)
# 视觉提供者:deterministic = V1 骨架的确定性假实现(真实 RTMPose/BiRefNet
# 是 Sprint 1 结论落地后的替换项,接口位见 packages/report/vision.py)。
report_vision_provider: str = "deterministic"
@@ -254,6 +283,7 @@ class Settings(BaseSettings):
"upload_scenes",
"retrieval_rerank_weights",
"report_pipeline_stages",
"report_full_pipeline_stages",
mode="before",
)
@classmethod