ZHIWEI666 commited on
Commit
02886ab
·
verified ·
1 Parent(s): d3ae963

Upload router_users.py

Browse files
Files changed (1) hide show
  1. router_users.py +21 -27
router_users.py CHANGED
@@ -1,5 +1,5 @@
1
  # router_users.py
2
- from fastapi import APIRouter, HTTPException
3
  import time
4
  import re
5
  import random
@@ -13,20 +13,19 @@ from models import UserRegister, UserLogin, UserUpdate, PasswordReset, FollowTog
13
  router = APIRouter()
14
 
15
  # ==========================================
16
- # 【核心新增】:验证码内存缓存与发送引擎
17
  # ==========================================
18
- # 结构: {"contact_action": {"code": "123456", "expires_at": 1690000000}}
19
  VERIFY_CODES = {}
20
 
21
  def send_email_code(to_email: str, code: str, action: str):
22
- """底层邮件发送引擎 (需在 HF Spaces 设置环境变量)"""
23
  sender = os.environ.get("SMTP_USER")
24
  pwd = os.environ.get("SMTP_PASS")
25
- server = os.environ.get("SMTP_SERVER", "smtp.qq.com") # 默认使用 QQ 邮箱 SMTP
26
  port = int(os.environ.get("SMTP_PORT", 465))
27
 
28
  if not sender or not pwd:
29
- print("警告: 未配置 SMTP_USER 或 SMTP_PASS 环境变量,跳过真实邮件发送")
30
  return
31
 
32
  action_str = "注册账号" if action == "register" else "修改/找回密码"
@@ -36,28 +35,28 @@ def send_email_code(to_email: str, code: str, action: str):
36
  msg['To'] = to_email
37
 
38
  try:
 
39
  if port == 465:
40
- with smtplib.SMTP_SSL(server, port) as smtp:
41
  smtp.login(sender, pwd)
42
  smtp.send_message(msg)
43
  else:
44
- with smtplib.SMTP(server, port) as smtp:
45
  smtp.starttls()
46
  smtp.login(sender, pwd)
47
  smtp.send_message(msg)
 
48
  except Exception as e:
49
- print(f"邮件发送失败: {e}")
50
- raise HTTPException(status_code=500, detail="邮件下发失败,请检查云端 SMTP 配置")
51
 
52
  def send_sms_code(phone: str, code: str, action: str):
53
  """短信发送接口挂载点"""
54
- # TODO: 这里接入阿里云/腾讯云短信 SDK
55
- print(f"[模拟短信发送] 手机号: {phone}, 验证码: {code}, 动作: {action}")
56
  pass
57
 
 
58
  @router.post("/api/users/send-code")
59
- async def send_verify_code(req: SendCodeRequest):
60
- # 生成 6 位纯数字验证码
61
  code = str(random.randint(100000, 999999))
62
  cache_key = f"{req.contact}_{req.action_type}"
63
 
@@ -67,25 +66,26 @@ async def send_verify_code(req: SendCodeRequest):
67
  "expires_at": int(time.time()) + 600
68
  }
69
 
 
70
  if req.contact_type == "email":
71
- send_email_code(req.contact, code, req.action_type)
72
  elif req.contact_type == "phone":
73
- send_sms_code(req.contact, code, req.action_type)
74
  else:
75
  raise HTTPException(status_code=400, detail="不支持的验证方式")
76
 
77
- return {"status": "success", "message": "验证码发送"}
78
 
79
 
80
  # ==========================================
81
- # 原有业务接口修改 (增加验证码鉴权)
82
  # ==========================================
83
 
84
  @router.post("/api/users/register")
85
  async def register_user(user: UserRegister):
86
  users_db = db.load_data("users.json", default_data={})
87
 
88
- # 【新增】:校验注册验证码
89
  cache_key = f"{user.email}_register"
90
  cached = VERIFY_CODES.get(cache_key)
91
  if not cached or cached["code"] != user.code or int(time.time()) > cached["expires_at"]:
@@ -101,11 +101,10 @@ async def register_user(user: UserRegister):
101
  if existing_user.get("email") == user.email: raise HTTPException(status_code=400, detail="该邮箱已被绑定")
102
  if existing_user.get("phone") == user.phone: raise HTTPException(status_code=400, detail="该手机号已被绑定")
103
 
104
- # 验证通过,销毁验证码
105
- VERIFY_CODES.pop(cache_key, None)
106
 
107
  new_user = user.dict()
108
- new_user.pop("code", None) # 不将验证码存入数据库
109
  new_user.update({"created_at": int(time.time()), "followers": [], "following": [], "privacy": {"follows": False, "likes": False, "favorites": False, "downloads": False}})
110
  users_db[user.account] = new_user
111
  db.save_data("users.json", users_db)
@@ -146,16 +145,13 @@ async def update_user_profile(account: str, update_data: UserUpdate):
146
  async def reset_password(account: str, pwd_data: PasswordReset):
147
  users_db = db.load_data("users.json", default_data={})
148
  if account not in users_db: raise HTTPException(status_code=404, detail="用户不存在")
149
-
150
  user = users_db[account]
151
 
152
- # 【新增】:校验账号与提供的联系方式是否匹配,防止通过自己的邮箱改别人的密码
153
  if pwd_data.verify_type == "email" and user.get("email") != pwd_data.verify_contact:
154
  raise HTTPException(status_code=400, detail="填写的邮箱与该账号绑定的邮箱不匹配")
155
  if pwd_data.verify_type == "phone" and user.get("phone") != pwd_data.verify_contact:
156
  raise HTTPException(status_code=400, detail="填写的手机号与该账号绑定的手机号不匹配")
157
 
158
- # 【新增】:校验重置验证码
159
  cache_key = f"{pwd_data.verify_contact}_reset"
160
  cached = VERIFY_CODES.get(cache_key)
161
  if not cached or cached["code"] != pwd_data.code or int(time.time()) > cached["expires_at"]:
@@ -164,9 +160,7 @@ async def reset_password(account: str, pwd_data: PasswordReset):
164
  if len(pwd_data.new_password) < 6: raise HTTPException(status_code=400, detail="新密码必须大于等于6个字符")
165
  if not re.match(r'^[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};\':"\\|,.<>\/?]{6,}$', pwd_data.new_password): raise HTTPException(status_code=400, detail="新密码包含不支持的特殊字符")
166
 
167
- # 验证通过,销毁验证码
168
  VERIFY_CODES.pop(cache_key, None)
169
-
170
  user["password"] = pwd_data.new_password
171
  db.save_data("users.json", users_db)
172
  return {"status": "success"}
 
1
  # router_users.py
2
+ from fastapi import APIRouter, HTTPException, BackgroundTasks
3
  import time
4
  import re
5
  import random
 
13
  router = APIRouter()
14
 
15
  # ==========================================
16
+ # 验证码内存缓存与发送引擎
17
  # ==========================================
 
18
  VERIFY_CODES = {}
19
 
20
  def send_email_code(to_email: str, code: str, action: str):
21
+ """底层邮件发送引擎"""
22
  sender = os.environ.get("SMTP_USER")
23
  pwd = os.environ.get("SMTP_PASS")
24
+ server = os.environ.get("SMTP_SERVER", "smtp.qq.com")
25
  port = int(os.environ.get("SMTP_PORT", 465))
26
 
27
  if not sender or not pwd:
28
+ print("警告: 未配置 SMTP_USER 或 SMTP_PASS,跳过邮件发送")
29
  return
30
 
31
  action_str = "注册账号" if action == "register" else "修改/找回密码"
 
35
  msg['To'] = to_email
36
 
37
  try:
38
+ # 【核心优化】:加入 timeout=10 防卡死机制
39
  if port == 465:
40
+ with smtplib.SMTP_SSL(server, port, timeout=10) as smtp:
41
  smtp.login(sender, pwd)
42
  smtp.send_message(msg)
43
  else:
44
+ with smtplib.SMTP(server, port, timeout=10) as smtp:
45
  smtp.starttls()
46
  smtp.login(sender, pwd)
47
  smtp.send_message(msg)
48
+ print(f"✅ 成功发送验证码 {code} 至 {to_email}")
49
  except Exception as e:
50
+ print(f"邮件发送失败: {e}")
 
51
 
52
  def send_sms_code(phone: str, code: str, action: str):
53
  """短信发送接口挂载点"""
54
+ print(f"[模拟短信] 手机号: {phone}, 验证码: {code}")
 
55
  pass
56
 
57
+ # 【核心优化】:引入 BackgroundTasks
58
  @router.post("/api/users/send-code")
59
+ async def send_verify_code(req: SendCodeRequest, bg_tasks: BackgroundTasks):
 
60
  code = str(random.randint(100000, 999999))
61
  cache_key = f"{req.contact}_{req.action_type}"
62
 
 
66
  "expires_at": int(time.time()) + 600
67
  }
68
 
69
+ # 放入后台队列执行,立刻给前端返回成功响应
70
  if req.contact_type == "email":
71
+ bg_tasks.add_task(send_email_code, req.contact, code, req.action_type)
72
  elif req.contact_type == "phone":
73
+ bg_tasks.add_task(send_sms_code, req.contact, code, req.action_type)
74
  else:
75
  raise HTTPException(status_code=400, detail="不支持的验证方式")
76
 
77
+ return {"status": "success", "message": "验证码发送请求已提交"}
78
 
79
 
80
  # ==========================================
81
+ # 原有业务接口
82
  # ==========================================
83
 
84
  @router.post("/api/users/register")
85
  async def register_user(user: UserRegister):
86
  users_db = db.load_data("users.json", default_data={})
87
 
88
+ # 校验注册验证码
89
  cache_key = f"{user.email}_register"
90
  cached = VERIFY_CODES.get(cache_key)
91
  if not cached or cached["code"] != user.code or int(time.time()) > cached["expires_at"]:
 
101
  if existing_user.get("email") == user.email: raise HTTPException(status_code=400, detail="该邮箱已被绑定")
102
  if existing_user.get("phone") == user.phone: raise HTTPException(status_code=400, detail="该手机号已被绑定")
103
 
104
+ VERIFY_CODES.pop(cache_key, None) # 验证通过,销毁验证码
 
105
 
106
  new_user = user.dict()
107
+ new_user.pop("code", None)
108
  new_user.update({"created_at": int(time.time()), "followers": [], "following": [], "privacy": {"follows": False, "likes": False, "favorites": False, "downloads": False}})
109
  users_db[user.account] = new_user
110
  db.save_data("users.json", users_db)
 
145
  async def reset_password(account: str, pwd_data: PasswordReset):
146
  users_db = db.load_data("users.json", default_data={})
147
  if account not in users_db: raise HTTPException(status_code=404, detail="用户不存在")
 
148
  user = users_db[account]
149
 
 
150
  if pwd_data.verify_type == "email" and user.get("email") != pwd_data.verify_contact:
151
  raise HTTPException(status_code=400, detail="填写的邮箱与该账号绑定的邮箱不匹配")
152
  if pwd_data.verify_type == "phone" and user.get("phone") != pwd_data.verify_contact:
153
  raise HTTPException(status_code=400, detail="填写的手机号与该账号绑定的手机号不匹配")
154
 
 
155
  cache_key = f"{pwd_data.verify_contact}_reset"
156
  cached = VERIFY_CODES.get(cache_key)
157
  if not cached or cached["code"] != pwd_data.code or int(time.time()) > cached["expires_at"]:
 
160
  if len(pwd_data.new_password) < 6: raise HTTPException(status_code=400, detail="新密码必须大于等于6个字符")
161
  if not re.match(r'^[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};\':"\\|,.<>\/?]{6,}$', pwd_data.new_password): raise HTTPException(status_code=400, detail="新密码包含不支持的特殊字符")
162
 
 
163
  VERIFY_CODES.pop(cache_key, None)
 
164
  user["password"] = pwd_data.new_password
165
  db.save_data("users.json", users_db)
166
  return {"status": "success"}