Email rework and additional notifications
This commit is contained in:
parent
a22d8becbb
commit
4fb09e8fd7
@ -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)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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
20
app/mail/mail.py
Normal 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
24
app/mail/messages.py
Normal 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"
|
||||
}
|
||||
}
|
@ -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
|
@ -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):
|
||||
"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user