Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit9a22cc1

Browse files
committed
updated
1 parent232e4fe commit9a22cc1

File tree

8 files changed

+544
-4
lines changed

8 files changed

+544
-4
lines changed

‎.env

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ JWT_ALGORITHM=RS256
1111

1212
CLIENT_ORIGIN=http://localhost:3000
1313

14+
VERIFICATION_SECRET=my-email-verification-secret
15+
16+
EMAIL_HOST=smtp.mailtrap.io
17+
EMAIL_PORT=587
18+
EMAIL_USERNAME=4aeca0c9318dd2
19+
EMAIL_PASSWORD=a987a0e0eac00d
20+
EMAIL_FROM=admin@admin.com
1421

1522
JWT_PRIVATE_KEY=LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlCT2dJQkFBSkJBSSs3QnZUS0FWdHVQYzEzbEFkVk94TlVmcWxzMm1SVmlQWlJyVFpjd3l4RVhVRGpNaFZuCi9KVHRsd3h2a281T0pBQ1k3dVE0T09wODdiM3NOU3ZNd2xNQ0F3RUFBUUpBYm5LaENOQ0dOSFZGaHJPQ0RCU0IKdmZ2ckRWUzVpZXAwd2h2SGlBUEdjeWV6bjd0U2RweUZ0NEU0QTNXT3VQOXhqenNjTFZyb1pzRmVMUWlqT1JhUwp3UUloQU84MWl2b21iVGhjRkltTFZPbU16Vk52TGxWTW02WE5iS3B4bGh4TlpUTmhBaUVBbWRISlpGM3haWFE0Cm15QnNCeEhLQ3JqOTF6bVFxU0E4bHUvT1ZNTDNSak1DSVFEbDJxOUdtN0lMbS85b0EyaCtXdnZabGxZUlJPR3oKT21lV2lEclR5MUxaUVFJZ2ZGYUlaUWxMU0tkWjJvdXF4MHdwOWVEejBEWklLVzVWaSt6czdMZHRDdUVDSUVGYwo3d21VZ3pPblpzbnU1clBsTDJjZldLTGhFbWwrUVFzOCtkMFBGdXlnCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0t
1623
JWT_PUBLIC_KEY=LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZ3d0RRWUpLb1pJaHZjTkFRRUJCUUFEU3dBd1NBSkJBSSs3QnZUS0FWdHVQYzEzbEFkVk94TlVmcWxzMm1SVgppUFpSclRaY3d5eEVYVURqTWhWbi9KVHRsd3h2a281T0pBQ1k3dVE0T09wODdiM3NOU3ZNd2xNQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ==

‎app/config.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ class Settings(BaseSettings):
1717

1818
CLIENT_ORIGIN:str
1919

20+
VERIFICATION_SECRET:str
21+
22+
EMAIL_HOST:str
23+
EMAIL_PORT:int
24+
EMAIL_USERNAME:str
25+
EMAIL_PASSWORD:str
26+
EMAIL_FROM:EmailStr
27+
2028
classConfig:
2129
env_file='./.env'
2230

‎app/email.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
fromtypingimportList
2+
fromfastapi_mailimportFastMail,MessageSchema,ConnectionConfig
3+
frompydanticimportEmailStr,BaseModel
4+
from .importmodels
5+
from .configimportsettings
6+
fromjinja2importEnvironment,select_autoescape,PackageLoader
7+
8+
9+
env=Environment(
10+
loader=PackageLoader('app','templates'),
11+
autoescape=select_autoescape(['html','xml'])
12+
)
13+
14+
15+
classEmailSchema(BaseModel):
16+
email:List[EmailStr]
17+
18+
19+
classEmail:
20+
def__init__(self,user:models.User,url:str,email:List[EmailStr]):
21+
self.name=user.name
22+
self.sender='Codevo <admin@admin.com>'
23+
self.email=email
24+
self.url=url
25+
pass
26+
27+
asyncdefsendMail(self,subject,template):
28+
# Define the config
29+
conf=ConnectionConfig(
30+
MAIL_USERNAME=settings.EMAIL_USERNAME,
31+
MAIL_PASSWORD=settings.EMAIL_PASSWORD,
32+
MAIL_FROM=settings.EMAIL_FROM,
33+
MAIL_PORT=settings.EMAIL_PORT,
34+
MAIL_SERVER=settings.EMAIL_HOST,
35+
MAIL_TLS=True,
36+
MAIL_SSL=False,
37+
USE_CREDENTIALS=True,
38+
VALIDATE_CERTS=True
39+
)
40+
# Generate the HTML template base on the template name
41+
template=env.get_template(f'{template}.html')
42+
43+
html=template.render(
44+
url=self.url,
45+
first_name=self.name,
46+
subject=subject
47+
)
48+
49+
# Define the message options
50+
message=MessageSchema(
51+
subject=subject,
52+
recipients=self.email,
53+
body=html,
54+
subtype="html"
55+
)
56+
57+
# Send the email
58+
fm=FastMail(conf)
59+
awaitfm.send_message(message)
60+
61+
asyncdefsendVerificationCode(self):
62+
awaitself.sendMail('Your verification code (Valid for 10min)','verification')

‎app/oauth2.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
importbase64
2+
fromdatetimeimportdatetime,timedelta
23
fromtypingimportList
34
fromfastapiimportDepends,HTTPException,status
45
fromfastapi_jwt_authimportAuthJWT
56
frompydanticimportBaseModel
7+
fromjoseimportjwt,JWTError
68

79
from .importmodels
810
from .databaseimportget_db
@@ -63,3 +65,28 @@ def require_user(db: Session = Depends(get_db), Authorize: AuthJWT = Depends()):
6365
raiseHTTPException(
6466
status_code=status.HTTP_401_UNAUTHORIZED,detail='Token is invalid or has expired')
6567
returnuser_id
68+
69+
70+
defcreate_verification_token(user_id:str):
71+
expires=datetime.utcnow()+timedelta(minutes=30)
72+
payload= {'user_id':user_id,'exp':expires}
73+
returnjwt.encode(payload,settings.VERIFICATION_SECRET,algorithm='HS256')
74+
75+
76+
defverify_email_token(token:str):
77+
try:
78+
decoded=jwt.decode(
79+
token,settings.VERIFICATION_SECRET,algorithms=['HS256'])
80+
user_id=decoded.get('user_id')
81+
ifnotuser_id:
82+
raiseHTTPException(
83+
status_code=status.HTTP_400_BAD_REQUEST,detail='Could not verify user')
84+
exceptJWTErrorase:
85+
error=e.__class__.__name__
86+
print(error)
87+
iferror=='ExpiredSignatureError':
88+
raiseHTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
89+
detail='Token is invalid or has expired')
90+
raiseHTTPException(
91+
status_code=status.HTTP_400_BAD_REQUEST,detail='Could not verify user')
92+
returnuser_id

‎app/routers/auth.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@
88
from ..databaseimportget_db
99
fromapp.oauth2importAuthJWT
1010
from ..configimportsettings
11+
from ..emailimportEmail
1112

1213

1314
router=APIRouter()
1415
ACCESS_TOKEN_EXPIRES_IN=settings.ACCESS_TOKEN_EXPIRES_IN
1516
REFRESH_TOKEN_EXPIRES_IN=settings.REFRESH_TOKEN_EXPIRES_IN
1617

1718

18-
@router.post('/register',status_code=status.HTTP_201_CREATED,response_model=schemas.UserResponse)
19-
asyncdefcreate_user(payload:schemas.CreateUserSchema,db:Session=Depends(get_db)):
19+
@router.post('/register',status_code=status.HTTP_201_CREATED)
20+
asyncdefcreate_user(payload:schemas.CreateUserSchema,request:Request,db:Session=Depends(get_db)):
2021
# Check if user already exist
2122
user=db.query(models.User).filter(
2223
models.User.email==EmailStr(payload.email.lower())).first()
@@ -31,13 +32,22 @@ async def create_user(payload: schemas.CreateUserSchema, db: Session = Depends(g
3132
payload.password=utils.hash_password(payload.password)
3233
delpayload.passwordConfirm
3334
payload.role='user'
34-
payload.verified=True
35+
payload.verified=False
3536
payload.email=EmailStr(payload.email.lower())
3637
new_user=models.User(**payload.dict())
3738
db.add(new_user)
3839
db.commit()
3940
db.refresh(new_user)
40-
returnnew_user
41+
42+
try:
43+
token=oauth2.create_verification_token(str(new_user.id))
44+
url=f"{request.url.scheme}://{request.client.host}:{request.url.port}/api/auth/verifyemail/{token}"
45+
awaitEmail(new_user,url, [payload.email]).sendVerificationCode()
46+
exceptExceptionaserror:
47+
print('Error',error)
48+
raiseHTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
49+
detail='There was an error sending email')
50+
return {'status':'success','message':'Verification token successfully sent to your email'}
4151

4252

4353
@router.post('/login')
@@ -115,3 +125,19 @@ def logout(response: Response, Authorize: AuthJWT = Depends(), user_id: str = De
115125
response.set_cookie('logged_in','',-1)
116126

117127
return {'status':'success'}
128+
129+
130+
@router.get('/verifyemail/{token}')
131+
defverify_me(token:str,db:Session=Depends(get_db)):
132+
id=oauth2.verify_email_token(token)
133+
user_query=db.query(models.User).filter(models.User.id==id)
134+
user=user_query.first()
135+
ifnotuser:
136+
raiseHTTPException(
137+
status_code=status.HTTP_404_NOT_FOUND,detail='User no longer exist')
138+
user_query.update({'verified':True},synchronize_session=False)
139+
db.commit()
140+
return {
141+
"status":"success",
142+
"message":"Account verified successfully"
143+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp