Email rework and additional notifications

This commit is contained in:
Thastertyn 2024-05-04 10:18:01 +02:00
parent a22d8becbb
commit 4fb09e8fd7
7 changed files with 98 additions and 27 deletions

View File

@ -9,7 +9,7 @@ app = Flask(__name__)
from app.config import FlaskTesting, FlaskProduction
app.config.from_object(FlaskTesting)
mail = Mail(app)
flask_mail = Mail(app)
jwt_manager = JWTManager(app)
swag = Swagger(app, template=main_swagger)

View File

@ -10,7 +10,7 @@ def unauthorized(e):
@bp_errors.app_errorhandler(403)
def forbidden(e):
return {"Forbidden": "Forbidden from accessing this resource. Try logging in"}, 403
return {"Forbidden": "You shall not pass"}, 403
@bp_errors.app_errorhandler(404)
def not_found(e):

View File

@ -43,7 +43,10 @@ def logout():
jti = jwt['jti']
exp = jwt['exp']
result, status_code = UserService.logout(jti, exp)
user_id = get_jwt_identity()
result, status_code = UserService.logout(jti, exp, user_id)
return result, status_code
@ -62,7 +65,7 @@ def update_username():
jti = jwt['jti']
exp = jwt['exp']
UserService.logout(jti, exp)
UserService.logout(jti, exp, user_id)
return result, status_code
@ -81,7 +84,7 @@ def update_displayname():
jti = jwt['jti']
exp = jwt['exp']
UserService.logout(jti, exp)
UserService.logout(jti, exp, user_id)
return result, status_code
@ -100,7 +103,7 @@ def update_email():
jti = jwt['jti']
exp = jwt['exp']
UserService.logout(jti, exp)
UserService.logout(jti, exp, username)
return result, status_code
@ -120,7 +123,7 @@ def update_password():
jti = jwt['jti']
exp = jwt['exp']
UserService.logout(jti, exp)
UserService.logout(jti, exp, username)
return result, status_code
@ -136,6 +139,6 @@ def delete_user():
jti = jwt['jti']
exp = jwt['exp']
UserService.logout(jti, exp)
UserService.logout(jti, exp, user_id)
return result, status_code

20
app/mail/mail.py Normal file
View File

@ -0,0 +1,20 @@
from flask_mail import Message
from app import flask_mail
from app.mail.messages import messages
def send_mail(message: str, recipient: str):
body = messages[message]["body"]
subject = messages[message]["subject"]
msg = Message(subject, recipients=[recipient], body=body)
try:
flask_mail.send(msg)
return True
except Exception as e:
print(f"Failed to send email. Error: {e}")
return False

24
app/mail/messages.py Normal file
View File

@ -0,0 +1,24 @@
import datetime
messages = {
"register": {
"subject": "Successfully registered!",
"body": "Congratulations! Your account has been successfully created.\nThis mail also serves as a test that the email address is correct"
},
"login": {
"subject": "New Login detected!",
"body": "A new login token has been created"
},
"logout": {
"subject": "Successfully logged out",
"body": "A login has been revoked. No further action is needed."
},
"update": {
"subject": "Account updated",
"body": "Your account has been successfully updated. This also means you have been logged out of everywhere"
},
"delete": {
"subject": "Account Deleted!",
"body": "Your account has been deleted. No further action needed"
}
}

View File

@ -1,15 +0,0 @@
from flask_mail import Message
from app import mail
def send_mail(subject: str, recipient: str, body: str):
msg = Message(subject, recipients=[recipient])
msg.body = body
try:
mail.send(msg)
return True
except Exception as e:
print(f"Failed to send email. Error: {e}")
return False

View File

@ -9,7 +9,7 @@ from flask_jwt_extended import create_access_token
from app.extensions import db_connection
from app.extensions import jwt_redis_blocklist
from app.mail_utils import send_mail
from app.mail.mail import send_mail
class UserService:
@ -62,7 +62,7 @@ class UserService:
print(f"Error: {e}")
return {"Failed": "Failed to insert into database. Username or email are likely in use already"}, 500
send_mail("Successfully registered!", email, "Congratulations! Your account has been successfully created.\nThis mail also serves as a test that the email address is correct")
send_mail("register", email)
return {"Success": "User created successfully"}, 200
@ -81,10 +81,11 @@ class UserService:
try:
with db_connection.cursor(dictionary=True) as cursor:
cursor.execute("select id, password from user where username = %s", (username,))
cursor.execute("select id, email, password from user where username = %s", (username,))
result = cursor.fetchone()
user_id = result['id']
email = result['email']
password_hash = result['password']
if user_id is None:
@ -97,13 +98,15 @@ class UserService:
token = create_access_token(identity=user_id, expires_delta=expire)
send_mail("login", email)
return {"token": token}, 200
except Error as e:
return {"Failed": f"Failed to login. Error: {e}"}, 500
@staticmethod
def logout(jti, exp) -> Tuple[Union[dict, str], int]:
def logout(jti, exp, user_id) -> Tuple[Union[dict, str], int]:
"""
Logs out a user by invalidating the provided JWT.
@ -114,7 +117,9 @@ class UserService:
:return: Tuple containing a dictionary and an HTTP status code.
:rtype: Tuple[Union[dict, str], int]
"""
UserService.__invalidate_token(jti, exp)
UserService.__send_email("logout", id=user_id)
return {"Success": "Successfully logged out"}, 200
@ -128,6 +133,8 @@ class UserService:
:return: Tuple containing a dictionary and an HTTP status code.
:rtype: Tuple[Union[dict, str], int]
"""
UserService.__send_email("delete", id=user_id)
try:
with db_connection.cursor() as cursor:
@ -215,6 +222,38 @@ class UserService:
return {"Success": "Password successfully updated"}, 200
@staticmethod
def __send_email(message: str, username: str = None, id: str = None, email: str = None):
if email is not None:
send_mail(message, email)
return
if username is not None:
try:
with db_connection.cursor(dictionary=True) as cursor:
cursor.execute("select email from user where username = %s", (username,))
result = cursor.fetchone()
email = result['email']
send_mail("logout", email)
except Error as e:
return {"Failed": f"Failed to fetch some data. Error: {e}"}, 500
return
if id is not None:
try:
with db_connection.cursor(dictionary=True) as cursor:
cursor.execute("select email from user where id = %s", (id,))
result = cursor.fetchone()
email = result['email']
send_mail("logout", email)
except Error as e:
return {"Failed": f"Failed to fetch some data. Error: {e}"}, 500
return
raise ValueError("Invalid input data to send mail")
@staticmethod
def __invalidate_token(jti: str, exp: int):
"""