users.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. from fastapi import APIRouter, Form, Depends, HTTPException, Body
  2. from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer
  3. from app.models.models import User
  4. from app.api import deps
  5. from sqlalchemy.orm import Session
  6. from typing import Any, Dict, Optional
  7. import secrets
  8. from fastapi_login.exceptions import InvalidCredentialsException
  9. from fastapi_login import LoginManager
  10. from datetime import timedelta,datetime
  11. from app.config import settings
  12. from pathlib import Path
  13. from jose import jwt
  14. import emails
  15. from emails.template import JinjaTemplate
  16. import logging
  17. import bcrypt
  18. import smtplib
  19. from email.mime.text import MIMEText
  20. from google.oauth2 import id_token
  21. from google.auth.transport import requests
  22. from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
  23. from fastapi.security.utils import get_authorization_scheme_param
  24. users = APIRouter()
  25. SECRET: str = secrets.token_urlsafe(32)
  26. manager = LoginManager(SECRET, '/login',default_expiry=timedelta(hours=72))
  27. @manager.user_loader()
  28. async def query_user(user_id: str):
  29. """
  30. Get a user from the db
  31. :param user_id: E-Mail of the user
  32. :return: None or the user object
  33. """
  34. result = User.filter(email=user_id).first()
  35. if not result:
  36. print('[]')
  37. return []
  38. return result
  39. @manager.user_loader()
  40. async def query_user_username(user_id: str):
  41. """
  42. Get a user from the db
  43. :param user_id: E-Mail of the user
  44. :return: None or the user object
  45. """
  46. result = await User.filter(username=user_id).first()
  47. if not result:
  48. print('[]')
  49. return []
  50. return result
  51. @users.post("/login")
  52. async def login(data: OAuth2PasswordRequestForm = Depends()):
  53. email = data.username
  54. password = data.password
  55. user = await query_user(email)
  56. print(user)
  57. access_token = manager.create_access_token(
  58. data={'sub': email}
  59. )
  60. if not user:
  61. # you can return any response or error of your choice
  62. return {"message":"查無此人"}
  63. # elif password != user.password:
  64. # raise InvalidCredentialsException
  65. else:
  66. stored_hashed_password = user.password.encode('utf-8')
  67. if bcrypt.checkpw(password.encode('utf-8'),stored_hashed_password):
  68. return {'msg':'登入成功','code':'200','access_token': access_token,'username':user.username,'email':user.email,'points':user.points}
  69. else:
  70. return {"message": "Invalid username or password"}
  71. oauth2_scheme = OAuth2PasswordBearer(tokenUrl="https://oauth2.googleapis.com/token")
  72. CLIENT_ID = settings.CLIENT_ID
  73. @users.post("/login/google/access-token")
  74. async def login(db: Session = Depends(deps.get_db), data: OAuth2PasswordRequestForm = Depends(), add_time_code: Optional[str] = None
  75. ) -> Any:
  76. """
  77. OAuth2 compatible token login, get an access token for future requests
  78. """
  79. user = await User.filter(username=data.username).first()
  80. if not user:
  81. u = await User.create(username=data.username, password=data.password)
  82. access_token = manager.create_access_token(
  83. data={'sub': data.username}
  84. )
  85. return_msg = {
  86. "access_token": access_token,
  87. "token_type": "bearer",
  88. }
  89. return return_msg
  90. # if add_time_code:
  91. # available_ser_no = crud.serial_number.available(db, ser_no=add_time_code)
  92. # print(available_ser_no)
  93. # if available_ser_no:
  94. # user_in = schemas.UserUpdate(available_time=user.available_time + available_ser_no.time)
  95. # crud.user.update(db, db_obj=user, obj_in=user_in)
  96. #
  97. # ser_no_in = schemas.SerialNumberUpdate(code=available_ser_no.code, is_used=True,
  98. # used_datetime=str(datetime.now()), owner_id=user.id)
  99. # crud.serial_number.update(db, db_obj=available_ser_no, obj_in=ser_no_in)
  100. # print(available_ser_no.time, type(available_ser_no.time))
  101. # return_msg['time_added'] = available_ser_no.time
  102. # else:
  103. # return_msg['time_added'] = -1
  104. # return return_msg
  105. @users.post("/logout")
  106. async def logout():
  107. return {"msg":"logout success","code":200}
  108. @users.post("/add")
  109. async def add(username: str = Form(default=''), password: str = Form(default=''), email: str = Form(default=''), re_password: str = Form(default='')):
  110. if username and password and email:
  111. user_email = await query_user(email)
  112. user_username = await query_user_username(username)
  113. if user_email or user_username:
  114. return {"msg":"該信箱或使用者名稱已存在","code":403}
  115. # elif user_username:
  116. # return {"msg":"該使用者名稱已存在","code":403}
  117. else:
  118. if password == re_password:
  119. hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
  120. u = await User.create(username=username, password=hashed_password, email=email)
  121. if u:
  122. message = '註冊成功'
  123. subject = '註冊信'
  124. print(message)
  125. send_email(email,message,subject)
  126. return {"msg": "註冊成功", "code": 200}
  127. else:
  128. return {"msg":"確認密碼錯誤","code":403}
  129. return {"msg": "create user failed", "code": 403}
  130. def generate_password_reset_token(email: str) -> str:
  131. delta = timedelta(hours=settings.EMAIL_RESET_TOKEN_EXPIRE_HOURS)
  132. now = datetime.utcnow()
  133. expires = now + delta
  134. exp = expires.timestamp()
  135. encoded_jwt = jwt.encode(
  136. {"exp": exp, "nbf": now, "sub": email}, settings.SECRET_KEY, algorithm="HS256",)
  137. print(encoded_jwt)
  138. return encoded_jwt
  139. def verify_password_reset_token(token: str):
  140. try:
  141. decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
  142. print(decoded_token)
  143. return decoded_token["sub"]
  144. except jwt.JWTError:
  145. return None
  146. def send_email(
  147. email_to: str,
  148. subject_template: str = "",
  149. html_template: str = "",
  150. environment: Dict[str, Any] = {},
  151. ):
  152. message = emails.Message(
  153. subject=JinjaTemplate(subject_template),
  154. html=JinjaTemplate(html_template),
  155. mail_from=(settings.EMAILS_FROM_NAME, settings.EMAILS_FROM_EMAIL),
  156. )
  157. smtp_options = {"host": settings.SMTP_HOST, "port": settings.SMTP_PORT}
  158. if settings.SMTP_TLS:
  159. smtp_options["tls"] = True
  160. if settings.SMTP_USER:
  161. smtp_options["user"] = settings.SMTP_USER
  162. if settings.SMTP_PASSWORD:
  163. smtp_options["password"] = settings.SMTP_PASSWORD
  164. response = message.send(to=email_to, render=environment, smtp=smtp_options)
  165. return {"message":f"send email result: {response}"}
  166. # logging.info(f"send email result: {response}")
  167. # email_content = MIMEText(message)
  168. # email_content["Subject"] = subject
  169. # email_content["From"] = 'zooey@choozmo.com'
  170. # email_content["To"] = email_to
  171. # try:
  172. # print('測試成功')
  173. # # Connect to the SMTP server
  174. # smtp_server = smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT)
  175. # smtp_server.starttls()
  176. #
  177. # # Login to the email account
  178. # smtp_server.login(settings.SMTP_HOST, settings.SMTP_PASSWORD)
  179. #
  180. # # Send the email
  181. # smtp_server.sendmail(settings.SMTP_HOST, email_to, email_content.as_string())
  182. #
  183. # # Close the connection
  184. # smtp_server.quit()
  185. #
  186. # return {"message": "Email sent successfully."}
  187. # except Exception as e:
  188. # print('測試失敗')
  189. def send_reset_password_email(email_to: str, email: str, token: str) -> None:
  190. subject = f"Password recovery for user {email}"
  191. with open(Path(settings.EMAIL_TEMPLATES_DIR) / "reset_password.html") as f:
  192. template_str = f.read()
  193. server_host = settings.SERVER_HOST
  194. link = f"{server_host}/reset-password?token={token}"
  195. message = '重新設定密碼'
  196. send_email(
  197. email_to=email_to,
  198. subject_template=subject,
  199. html_template=template_str,
  200. environment={
  201. "project_name": settings.PROJECT_NAME,
  202. "username": email,
  203. "email": email_to,
  204. "valid_hours": settings.EMAIL_RESET_TOKEN_EXPIRE_HOURS,
  205. "link": link,
  206. },
  207. )
  208. @users.post("/password-recovery/{email}")
  209. async def recover_password(email:str):
  210. user = await User.filter(email=email).first()
  211. if not user:
  212. raise HTTPException(
  213. status_code=404,
  214. detail="The user with this username does not exist in the system.",
  215. )
  216. password_reset_token = generate_password_reset_token(email=email)
  217. send_reset_password_email(
  218. email_to=user.email, email=email, token=password_reset_token
  219. )
  220. return {"msg": "Password recovery email sent"}
  221. from pydantic import BaseModel
  222. class Msg(BaseModel):
  223. msg: str
  224. @users.post("/reset-password/", response_model=Msg)
  225. async def reset_password(
  226. token: str = Body(...),
  227. new_password: str = Body(...),
  228. db: Session = Depends(deps.get_db),
  229. ) -> Any:
  230. """
  231. Reset password
  232. """
  233. email = verify_password_reset_token(token)
  234. print(email)
  235. if not email:
  236. raise HTTPException(status_code=400, detail="Invalid token")
  237. # user = await query_user(email)
  238. user = await User.filter(email=email).first()
  239. if not user:
  240. raise HTTPException(
  241. status_code=404,
  242. detail="The user with this username does not exist in the system.",
  243. )
  244. # elif not crud.user.is_active(user):
  245. # raise HTTPException(status_code=400, detail="Inactive user")
  246. hashed_password = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
  247. user.password = hashed_password
  248. # db.add(user)
  249. db.commit()
  250. print(user.password)
  251. db.close()
  252. return {"msg": "Password updated successfully"}
  253. @users.get("/delete_user/{id}")
  254. async def delete(id: int):
  255. if id:
  256. await User.filter(id=id).delete()
  257. return {"msg": "success", "code": 200}
  258. return {"msg": "failed", "code": 400}
  259. print('12334')