Przeglądaj źródła

忘記密碼、註冊信、三方登入

zooey 2 lat temu
rodzic
commit
d97b8c3025
3 zmienionych plików z 138 dodań i 19 usunięć
  1. 128 12
      app/api/users.py
  2. 9 6
      app/config.py
  3. 1 1
      app/session.py

+ 128 - 12
app/api/users.py

@@ -1,9 +1,9 @@
-from fastapi import APIRouter, Form, Depends, HTTPException
-from fastapi.security import OAuth2PasswordRequestForm
+from fastapi import APIRouter, Form, Depends, HTTPException, Body
+from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer
 from app.models.models import User
 from app.api import deps
 from sqlalchemy.orm import Session
-from typing import Any, Dict
+from typing import Any, Dict, Optional
 import secrets
 from fastapi_login.exceptions import InvalidCredentialsException
 from fastapi_login import LoginManager
@@ -15,6 +15,13 @@ import emails
 from emails.template import JinjaTemplate
 import logging
 import bcrypt
+import smtplib
+from email.mime.text import MIMEText
+from google.oauth2 import id_token
+from google.auth.transport import requests
+from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
+from fastapi.security.utils import get_authorization_scheme_param
+
 
 
 users = APIRouter()
@@ -30,7 +37,7 @@ async def query_user(user_id: str):
     :param user_id: E-Mail of the user
     :return: None or the user object
     """
-    result = await User.filter(email=user_id).first()
+    result = User.filter(email=user_id).first()
     if not result:
         print('[]')
         return []
@@ -74,6 +81,42 @@ async def login(data: OAuth2PasswordRequestForm = Depends()):
         else:
             return {"message": "Invalid username or password"}
 
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="https://oauth2.googleapis.com/token")
+
+CLIENT_ID = settings.CLIENT_ID
+@users.post("/login/google/access-token")
+async def login(db: Session = Depends(deps.get_db), data: OAuth2PasswordRequestForm = Depends(), add_time_code: Optional[str] = None
+    ) -> Any:
+    """
+    OAuth2 compatible token login, get an access token for future requests
+    """
+    user = await User.filter(username=data.username).first()
+    if not user:
+        u = await User.create(username=data.username, password=data.password)
+
+    access_token = manager.create_access_token(
+        data={'sub': data.username}
+    )
+    return_msg = {
+        "access_token": access_token,
+        "token_type": "bearer",
+    }
+    return return_msg
+    # if add_time_code:
+    #     available_ser_no = crud.serial_number.available(db, ser_no=add_time_code)
+    #     print(available_ser_no)
+    #     if available_ser_no:
+    #         user_in = schemas.UserUpdate(available_time=user.available_time + available_ser_no.time)
+    #         crud.user.update(db, db_obj=user, obj_in=user_in)
+    #
+    #         ser_no_in = schemas.SerialNumberUpdate(code=available_ser_no.code, is_used=True,
+    #                                                used_datetime=str(datetime.now()), owner_id=user.id)
+    #         crud.serial_number.update(db, db_obj=available_ser_no, obj_in=ser_no_in)
+    #         print(available_ser_no.time, type(available_ser_no.time))
+    #         return_msg['time_added'] = available_ser_no.time
+    #     else:
+    #         return_msg['time_added'] = -1
+    # return return_msg
 
 @users.post("/logout")
 async def logout():
@@ -94,7 +137,10 @@ async def add(username: str = Form(default=''), password: str = Form(default='')
                 hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
                 u = await User.create(username=username, password=hashed_password, email=email)
                 if u:
-                    # send_email()
+                    message = '註冊成功'
+                    subject = '註冊信'
+                    print(message)
+                    send_email(email,message,subject)
                     return {"msg": "註冊成功", "code": 200}
             else:
                 return {"msg":"確認密碼錯誤","code":403}
@@ -108,18 +154,25 @@ def generate_password_reset_token(email: str) -> str:
     expires = now + delta
     exp = expires.timestamp()
     encoded_jwt = jwt.encode(
-        {"exp": exp, "nbf": now, "sub": email}, settings.SECRET_KEY, algorithm="HS256",
-    )
+        {"exp": exp, "nbf": now, "sub": email}, settings.SECRET_KEY, algorithm="HS256",)
+    print(encoded_jwt)
     return encoded_jwt
 
+def verify_password_reset_token(token: str):
+    try:
+        decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
+        print(decoded_token)
+        return decoded_token["sub"]
+    except jwt.JWTError:
+        return None
+
 
 def send_email(
     email_to: str,
     subject_template: str = "",
     html_template: str = "",
     environment: Dict[str, Any] = {},
-) -> None:
-    # assert settings.EMAILS_ENABLED, "no provided configuration for email variables"
+):
     message = emails.Message(
         subject=JinjaTemplate(subject_template),
         html=JinjaTemplate(html_template),
@@ -133,7 +186,30 @@ def send_email(
     if settings.SMTP_PASSWORD:
         smtp_options["password"] = settings.SMTP_PASSWORD
     response = message.send(to=email_to, render=environment, smtp=smtp_options)
-    logging.info(f"send email result: {response}")
+    return {"message":f"send email result: {response}"}
+    # logging.info(f"send email result: {response}")
+    # email_content = MIMEText(message)
+    # email_content["Subject"] = subject
+    # email_content["From"] = 'zooey@choozmo.com'
+    # email_content["To"] = email_to
+    # try:
+    #     print('測試成功')
+    #     # Connect to the SMTP server
+    #     smtp_server = smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT)
+    #     smtp_server.starttls()
+    #
+    #     # Login to the email account
+    #     smtp_server.login(settings.SMTP_HOST, settings.SMTP_PASSWORD)
+    #
+    #     # Send the email
+    #     smtp_server.sendmail(settings.SMTP_HOST, email_to, email_content.as_string())
+    #
+    #     # Close the connection
+    #     smtp_server.quit()
+    #
+    #     return {"message": "Email sent successfully."}
+    # except Exception as e:
+    #     print('測試失敗')
 
 
 def send_reset_password_email(email_to: str, email: str, token: str) -> None:
@@ -142,12 +218,13 @@ def send_reset_password_email(email_to: str, email: str, token: str) -> None:
         template_str = f.read()
     server_host = settings.SERVER_HOST
     link = f"{server_host}/reset-password?token={token}"
+    message = '重新設定密碼'
     send_email(
         email_to=email_to,
         subject_template=subject,
         html_template=template_str,
         environment={
-            # "project_name": settings.PROJECT_NAME,
+            "project_name": settings.PROJECT_NAME,
             "username": email,
             "email": email_to,
             "valid_hours": settings.EMAIL_RESET_TOKEN_EXPIRE_HOURS,
@@ -171,6 +248,43 @@ async def recover_password(email:str):
     return {"msg": "Password recovery email sent"}
 
 
+from pydantic import BaseModel
+
+
+class Msg(BaseModel):
+    msg: str
+@users.post("/reset-password/", response_model=Msg)
+async def reset_password(
+    token: str = Body(...),
+    new_password: str = Body(...),
+    db: Session = Depends(deps.get_db),
+) -> Any:
+    """
+    Reset password
+    """
+    email = verify_password_reset_token(token)
+    print(email)
+    if not email:
+        raise HTTPException(status_code=400, detail="Invalid token")
+    # user = await query_user(email)
+    user = await User.filter(email=email).first()
+
+    if not user:
+        raise HTTPException(
+            status_code=404,
+            detail="The user with this username does not exist in the system.",
+        )
+    # elif not crud.user.is_active(user):
+    #     raise HTTPException(status_code=400, detail="Inactive user")
+    hashed_password = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
+    user.password = hashed_password
+    # db.add(user)
+    db.commit()
+    print(user.password)
+    db.close()
+    return {"msg": "Password updated successfully"}
+
+
 
 @users.get("/delete_user/{id}")
 async def delete(id: int):
@@ -178,4 +292,6 @@ async def delete(id: int):
         await User.filter(id=id).delete()
         return {"msg": "success", "code": 200}
 
-    return {"msg": "failed", "code": 400}
+    return {"msg": "failed", "code": 400}
+
+print('12334')

+ 9 - 6
app/config.py

@@ -11,13 +11,15 @@ class Settings(BaseSettings):
     ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8
     SERVER_HOST: AnyHttpUrl = "http://cmm.ai:8088"
 
+    PROJECT_NAME = 'ntcri'
+
     SMTP_TLS: bool = True
-    SMTP_PORT: Optional[int] = None
-    SMTP_HOST: Optional[str] = None
-    SMTP_USER: Optional[str] = None
-    SMTP_PASSWORD: Optional[str] = None
-    EMAILS_FROM_EMAIL: Optional[EmailStr] = None
-    EMAILS_FROM_NAME: Optional[str] = None
+    SMTP_PORT: Optional[int] = "587"
+    SMTP_HOST: Optional[str] = "smtp.gmail.com"
+    SMTP_USER: Optional[str] = "zooey@choozmo.com"
+    SMTP_PASSWORD: Optional[str] = "bnufmjzkyezputip"
+    EMAILS_FROM_EMAIL: Optional[EmailStr] = 'zooey@choozmo.com'
+    EMAILS_FROM_NAME: Optional[str] = 'zooey'
     EMAILS_ENABLED: bool = True
 
     @validator("EMAILS_ENABLED", pre=True, check_fields=False)
@@ -30,5 +32,6 @@ class Settings(BaseSettings):
     EMAIL_TEMPLATES_DIR: str = "C:\/Users\/s1301\/PycharmProjects\/fastdemo\/app\email-templates"
     EMAIL_RESET_TOKEN_EXPIRE_HOURS: int = 48
 
+    CLIENT_ID = "626437744072-q6djn202411is5vdk2v0tu8fo7n07qr0.apps.googleusercontent.com"
 
 settings = Settings()

+ 1 - 1
app/session.py

@@ -9,4 +9,4 @@ password = "pAssw0rd"
 host = "db.ptt.cx:3306"
 db_name = "test"
 engine = create_engine(f'mysql://{user}:{password}@{host}/{db_name}', pool_pre_ping=True)
-SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
+SessionLocal = sessionmaker(autocommit=False, autoflush=True, bind=engine)