ZHIWEI666 commited on
Commit
1ed36f1
·
verified ·
1 Parent(s): 998c1ee

Upload router_users.py

Browse files
Files changed (1) hide show
  1. router_users.py +73 -20
router_users.py CHANGED
@@ -6,6 +6,8 @@ import random
6
  import os
7
  import json
8
  import urllib.request
 
 
9
  import 数据库连接 as db
10
  from notifications import add_notification
11
  from models import UserRegister, UserLogin, UserUpdate, PasswordReset, FollowToggle, PrivacySettings, SendCodeRequest
@@ -40,14 +42,12 @@ def send_email_code(to_email: str, code: str, action: str):
40
  </div>
41
  """
42
 
43
- # 组装要发送给 Make.com 的数据
44
  data = {
45
  "to": to_email,
46
  "subject": subject,
47
  "html": html_content
48
  }
49
 
50
- # 发送普通的 HTTPS POST 请求,绝对不会被拦截
51
  req = urllib.request.Request(
52
  webhook_url,
53
  data=json.dumps(data).encode('utf-8'),
@@ -60,14 +60,76 @@ def send_email_code(to_email: str, code: str, action: str):
60
  print(f"❌ Webhook 触发失败: {e}")
61
 
62
  def send_sms_code(phone: str, code: str, action: str):
63
- """短信发送接口挂载点"""
64
- print(f"[模拟短信] 手机: {phone}, 验证码: {code}")
65
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
- # 【核心优化】:引入 BackgroundTasks
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  @router.post("/api/users/send-code")
69
  async def send_verify_code(req: SendCodeRequest, bg_tasks: BackgroundTasks):
70
- # 【新增核心逻辑】:如果是找回密码,先去数据库里核对联系方式
71
  if req.action_type == "reset":
72
  if not req.account:
73
  raise HTTPException(status_code=400, detail="找回密码需先填写当前账号")
@@ -82,17 +144,14 @@ async def send_verify_code(req: SendCodeRequest, bg_tasks: BackgroundTasks):
82
  if req.contact_type == "phone" and user.get("phone") != req.contact:
83
  raise HTTPException(status_code=400, detail="填写的手机号与该账号绑定的手机号不一���")
84
 
85
- # 原有的生成验证码和发送逻辑保持不变
86
  code = str(random.randint(100000, 999999))
87
  cache_key = f"{req.contact}_{req.action_type}"
88
 
89
- # 存入缓存,设置 10 分钟过期
90
  VERIFY_CODES[cache_key] = {
91
  "code": code,
92
  "expires_at": int(time.time()) + 600
93
  }
94
 
95
- # 放入后台队列执行,立刻给前端返回成功响应
96
  if req.contact_type == "email":
97
  bg_tasks.add_task(send_email_code, req.contact, code, req.action_type)
98
  elif req.contact_type == "phone":
@@ -102,17 +161,11 @@ async def send_verify_code(req: SendCodeRequest, bg_tasks: BackgroundTasks):
102
 
103
  return {"status": "success", "message": "验证码发送请求已提交"}
104
 
105
-
106
- # ==========================================
107
- # 原有业务接口
108
- # ==========================================
109
-
110
  @router.post("/api/users/register")
111
  async def register_user(user: UserRegister):
112
  users_db = db.load_data("users.json", default_data={})
113
 
114
- # 校验注册验证码
115
- cache_key = f"{user.email}_register"
116
  cached = VERIFY_CODES.get(cache_key)
117
  if not cached or cached["code"] != user.code or int(time.time()) > cached["expires_at"]:
118
  raise HTTPException(status_code=400, detail="验证码不正确或已过期")
@@ -124,10 +177,10 @@ async def register_user(user: UserRegister):
124
  if user.intro and len(user.intro) > 100: raise HTTPException(status_code=400, detail="个人介绍不能超过100个字符")
125
  if user.account in users_db: raise HTTPException(status_code=400, detail="该账号已被注册")
126
  for existing_user in users_db.values():
127
- if existing_user.get("email") == user.email: raise HTTPException(status_code=400, detail="该邮箱已被绑定")
128
- if existing_user.get("phone") == user.phone: raise HTTPException(status_code=400, detail="该手机号已被绑定")
129
 
130
- VERIFY_CODES.pop(cache_key, None) # 验证通过,销毁验证码
131
 
132
  new_user = user.dict()
133
  new_user.pop("code", None)
 
6
  import os
7
  import json
8
  import urllib.request
9
+ import urllib.parse
10
+ import base64
11
  import 数据库连接 as db
12
  from notifications import add_notification
13
  from models import UserRegister, UserLogin, UserUpdate, PasswordReset, FollowToggle, PrivacySettings, SendCodeRequest
 
42
  </div>
43
  """
44
 
 
45
  data = {
46
  "to": to_email,
47
  "subject": subject,
48
  "html": html_content
49
  }
50
 
 
51
  req = urllib.request.Request(
52
  webhook_url,
53
  data=json.dumps(data).encode('utf-8'),
 
60
  print(f"❌ Webhook 触发失败: {e}")
61
 
62
  def send_sms_code(phone: str, code: str, action: str):
63
+ """支持 阿里云 与 Twilio 的双引擎短信发送路由"""
64
+ action_str = "注册账" if action == "register" else "修改/找回密码"
65
+
66
+ # ==========================================
67
+ # 引擎 A:Twilio (无需装 SDK,走纯 HTTP 接口,适合海外或免审核测试)
68
+ # ==========================================
69
+ twilio_sid = os.environ.get("TWILIO_SID")
70
+ if twilio_sid:
71
+ token = os.environ.get("TWILIO_TOKEN")
72
+ from_phone = os.environ.get("TWILIO_FROM")
73
+
74
+ body = f"【ComfyUI社区】您正在请求{action_str},验证码是:{code},10分钟内有效。"
75
+ url = f"https://api.twilio.com/2010-04-01/Accounts/{twilio_sid}/Messages.json"
76
+ auth = base64.b64encode(f"{twilio_sid}:{token}".encode('utf-8')).decode('utf-8')
77
+ data = urllib.parse.urlencode({'To': phone, 'From': from_phone, 'Body': body}).encode('utf-8')
78
+
79
+ req = urllib.request.Request(url, data=data)
80
+ req.add_header("Authorization", f"Basic {auth}")
81
+ try:
82
+ with urllib.request.urlopen(req, timeout=10) as response:
83
+ print(f"✅ Twilio 短信已成功下发至 {phone}")
84
+ except Exception as e:
85
+ print(f"❌ Twilio 发送失败: {e}")
86
+ return
87
 
88
+ # ==========================================
89
+ # 引擎 B:阿里云 (国内首选,到达率最高)
90
+ # ==========================================
91
+ aliyun_ak = os.environ.get("ALIYUN_AK")
92
+ if aliyun_ak:
93
+ try:
94
+ from alibabacloud_dysmsapi20170525.client import Client as DysmsapiClient
95
+ from alibabacloud_tea_openapi import models as open_api_models
96
+ from alibabacloud_dysmsapi20170525 import models as dysmsapi_models
97
+ except ImportError:
98
+ print("❌ 缺少阿里云 SDK,请在 requirements.txt 中添加 alibabacloud_dysmsapi20170525")
99
+ return
100
+
101
+ sk = os.environ.get("ALIYUN_SK")
102
+ sign_name = os.environ.get("ALIYUN_SIGN_NAME") # 短信签名,例如 "阿里云"
103
+ tpl_code = os.environ.get("ALIYUN_TPL_REGISTER") if action == "register" else os.environ.get("ALIYUN_TPL_RESET")
104
+
105
+ config = open_api_models.Config(access_key_id=aliyun_ak, access_key_secret=sk)
106
+ config.endpoint = 'dysmsapi.aliyuncs.com'
107
+ client = DysmsapiClient(config)
108
+
109
+ send_req = dysmsapi_models.SendSmsRequest(
110
+ phone_numbers=phone,
111
+ sign_name=sign_name,
112
+ template_code=tpl_code,
113
+ template_param=json.dumps({"code": code})
114
+ )
115
+ try:
116
+ response = client.send_sms(send_req)
117
+ if response.body.code == "OK":
118
+ print(f"✅ 阿里云短信已成功下发至 {phone}")
119
+ else:
120
+ print(f"❌ 阿里云下发失败: {response.body.message}")
121
+ except Exception as e:
122
+ print(f"❌ 阿里云请求异常: {e}")
123
+ return
124
+
125
+ # 如果都没有配置,则降级为控制台打印模拟
126
+ print(f"⚠️ 未配置短信秘钥,模拟下发 -> 手机号: {phone}, 验证码: {code}")
127
+
128
+ # ==========================================
129
+ # 接口路由
130
+ # ==========================================
131
  @router.post("/api/users/send-code")
132
  async def send_verify_code(req: SendCodeRequest, bg_tasks: BackgroundTasks):
 
133
  if req.action_type == "reset":
134
  if not req.account:
135
  raise HTTPException(status_code=400, detail="找回密码需先填写当前账号")
 
144
  if req.contact_type == "phone" and user.get("phone") != req.contact:
145
  raise HTTPException(status_code=400, detail="填写的手机号与该账号绑定的手机号不一���")
146
 
 
147
  code = str(random.randint(100000, 999999))
148
  cache_key = f"{req.contact}_{req.action_type}"
149
 
 
150
  VERIFY_CODES[cache_key] = {
151
  "code": code,
152
  "expires_at": int(time.time()) + 600
153
  }
154
 
 
155
  if req.contact_type == "email":
156
  bg_tasks.add_task(send_email_code, req.contact, code, req.action_type)
157
  elif req.contact_type == "phone":
 
161
 
162
  return {"status": "success", "message": "验证码发送请求已提交"}
163
 
 
 
 
 
 
164
  @router.post("/api/users/register")
165
  async def register_user(user: UserRegister):
166
  users_db = db.load_data("users.json", default_data={})
167
 
168
+ cache_key = f"{user.email}_register" if user.email else f"{user.phone}_register"
 
169
  cached = VERIFY_CODES.get(cache_key)
170
  if not cached or cached["code"] != user.code or int(time.time()) > cached["expires_at"]:
171
  raise HTTPException(status_code=400, detail="验证码不正确或已过期")
 
177
  if user.intro and len(user.intro) > 100: raise HTTPException(status_code=400, detail="个人介绍不能超过100个字符")
178
  if user.account in users_db: raise HTTPException(status_code=400, detail="该账号已被注册")
179
  for existing_user in users_db.values():
180
+ if user.email and existing_user.get("email") == user.email: raise HTTPException(status_code=400, detail="该邮箱已被绑定")
181
+ if user.phone and existing_user.get("phone") == user.phone: raise HTTPException(status_code=400, detail="该手机号已被绑定")
182
 
183
+ VERIFY_CODES.pop(cache_key, None)
184
 
185
  new_user = user.dict()
186
  new_user.pop("code", None)