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:
@@ -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
|
||||
|
||||
@@ -34,6 +34,8 @@ class ErrorCode(IntEnum):
|
||||
SESSION_REVOKED = 40105
|
||||
SMS_CODE_INVALID = 40110
|
||||
SMS_CODE_EXPIRED = 40111
|
||||
EMAIL_CODE_INVALID = 40112
|
||||
EMAIL_CODE_EXPIRED = 40113
|
||||
FORBIDDEN = 40300
|
||||
QUOTA_EXHAUSTED = 40301
|
||||
CONSENT_REQUIRED = 40302
|
||||
@@ -46,10 +48,12 @@ class ErrorCode(IntEnum):
|
||||
REPORT_EXPORT_NOT_READY = 40421
|
||||
STATE_CONFLICT = 40900
|
||||
TASK_BONUS_ALREADY_CLAIMED = 40910
|
||||
REPORT_ALREADY_EXISTS = 40920
|
||||
REPORT_ALREADY_EXISTS = 40920 # 已下线,仅历史兼容:重复生成改由 report_basic 配额兜底
|
||||
RATE_LIMITED = 42900
|
||||
SMS_SEND_TOO_FREQUENT = 42910
|
||||
SMS_DAILY_LIMIT = 42911
|
||||
EMAIL_SEND_TOO_FREQUENT = 42912
|
||||
EMAIL_DAILY_LIMIT = 42913
|
||||
|
||||
# --- 5xxxx server -----------------------------------------------------
|
||||
INTERNAL_ERROR = 50000
|
||||
@@ -67,6 +71,7 @@ class ErrorCode(IntEnum):
|
||||
MODERATION_UNAVAILABLE = 60005
|
||||
LLM_SCENE_DEGRADED = 60006
|
||||
SMS_PROVIDER_FAILED = 61000
|
||||
EMAIL_PROVIDER_FAILED = 61001
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
@@ -95,6 +100,8 @@ ERROR_META: dict[ErrorCode, ErrorMeta] = {
|
||||
ErrorCode.SESSION_REVOKED: ErrorMeta(401, "该会话已登出", "请重新登录"),
|
||||
ErrorCode.SMS_CODE_INVALID: ErrorMeta(401, "验证码错误", "请检查后重试"),
|
||||
ErrorCode.SMS_CODE_EXPIRED: ErrorMeta(401, "验证码已过期", "请重新获取"),
|
||||
ErrorCode.EMAIL_CODE_INVALID: ErrorMeta(401, "验证码错误", "请检查后重试"),
|
||||
ErrorCode.EMAIL_CODE_EXPIRED: ErrorMeta(401, "验证码已过期", "请重新获取"),
|
||||
ErrorCode.FORBIDDEN: ErrorMeta(403, "没有访问权限"),
|
||||
ErrorCode.QUOTA_EXHAUSTED: ErrorMeta(403, "今日额度已用完", "明天恢复,或完成任务多换几次"),
|
||||
ErrorCode.CONSENT_REQUIRED: ErrorMeta(403, "需要先完成授权", "请阅读并同意后继续"),
|
||||
@@ -111,10 +118,13 @@ ERROR_META: dict[ErrorCode, ErrorMeta] = {
|
||||
ErrorCode.TASK_BONUS_ALREADY_CLAIMED: ErrorMeta(
|
||||
409, "这个周期的任务奖励已领取", "下个周期再来"
|
||||
),
|
||||
# 40920 已无任何 raise 方(service 终身资格硬校验已删),保留仅为兼容旧端缓存的错误文案。
|
||||
ErrorCode.REPORT_ALREADY_EXISTS: ErrorMeta(409, "本期报告已生成过", "每 30 天可重新生成一次"),
|
||||
ErrorCode.RATE_LIMITED: ErrorMeta(429, "操作太频繁了", "请稍后再试"),
|
||||
ErrorCode.SMS_SEND_TOO_FREQUENT: ErrorMeta(429, "发送太频繁了", "请稍后再试"),
|
||||
ErrorCode.SMS_DAILY_LIMIT: ErrorMeta(429, "今日发送次数已达上限", "明天再试"),
|
||||
ErrorCode.EMAIL_SEND_TOO_FREQUENT: ErrorMeta(429, "发送太频繁了", "请稍后再试"),
|
||||
ErrorCode.EMAIL_DAILY_LIMIT: ErrorMeta(429, "今日发送次数已达上限", "明天再试"),
|
||||
ErrorCode.INTERNAL_ERROR: ErrorMeta(500, "服务开小差了", "请稍后重试"),
|
||||
ErrorCode.DB_ERROR: ErrorMeta(500, "服务开小差了", "请稍后重试"),
|
||||
ErrorCode.TASK_STATE_INVALID: ErrorMeta(500, "任务状态异常", "请稍后在首页「报告」中查看"),
|
||||
@@ -138,6 +148,7 @@ ERROR_META: dict[ErrorCode, ErrorMeta] = {
|
||||
503, "该功能今日暂时关闭", "AI 服务今日已达服务上限,明天恢复"
|
||||
),
|
||||
ErrorCode.SMS_PROVIDER_FAILED: ErrorMeta(502, "短信服务暂时不可用", "请稍后重试"),
|
||||
ErrorCode.EMAIL_PROVIDER_FAILED: ErrorMeta(502, "邮件服务暂时不可用", "请稍后重试"),
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user