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
|
from app.config import FlaskTesting, FlaskProduction
|
||||||
app.config.from_object(FlaskTesting)
|
app.config.from_object(FlaskTesting)
|
||||||
|
|
||||||
mail = Mail(app)
|
flask_mail = Mail(app)
|
||||||
jwt_manager = JWTManager(app)
|
jwt_manager = JWTManager(app)
|
||||||
swag = Swagger(app, template=main_swagger)
|
swag = Swagger(app, template=main_swagger)
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ def unauthorized(e):
|
|||||||
|
|
||||||
@bp_errors.app_errorhandler(403)
|
@bp_errors.app_errorhandler(403)
|
||||||
def forbidden(e):
|
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)
|
@bp_errors.app_errorhandler(404)
|
||||||
def not_found(e):
|
def not_found(e):
|
||||||
|
@ -43,7 +43,10 @@ def logout():
|
|||||||
|
|
||||||
jti = jwt['jti']
|
jti = jwt['jti']
|
||||||
exp = jwt['exp']
|
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
|
return result, status_code
|
||||||
|
|
||||||
@ -62,7 +65,7 @@ def update_username():
|
|||||||
|
|
||||||
jti = jwt['jti']
|
jti = jwt['jti']
|
||||||
exp = jwt['exp']
|
exp = jwt['exp']
|
||||||
UserService.logout(jti, exp)
|
UserService.logout(jti, exp, user_id)
|
||||||
|
|
||||||
return result, status_code
|
return result, status_code
|
||||||
|
|
||||||
@ -81,7 +84,7 @@ def update_displayname():
|
|||||||
|
|
||||||
jti = jwt['jti']
|
jti = jwt['jti']
|
||||||
exp = jwt['exp']
|
exp = jwt['exp']
|
||||||
UserService.logout(jti, exp)
|
UserService.logout(jti, exp, user_id)
|
||||||
|
|
||||||
return result, status_code
|
return result, status_code
|
||||||
|
|
||||||
@ -100,7 +103,7 @@ def update_email():
|
|||||||
|
|
||||||
jti = jwt['jti']
|
jti = jwt['jti']
|
||||||
exp = jwt['exp']
|
exp = jwt['exp']
|
||||||
UserService.logout(jti, exp)
|
UserService.logout(jti, exp, username)
|
||||||
|
|
||||||
return result, status_code
|
return result, status_code
|
||||||
|
|
||||||
@ -120,7 +123,7 @@ def update_password():
|
|||||||
|
|
||||||
jti = jwt['jti']
|
jti = jwt['jti']
|
||||||
exp = jwt['exp']
|
exp = jwt['exp']
|
||||||
UserService.logout(jti, exp)
|
UserService.logout(jti, exp, username)
|
||||||
|
|
||||||
return result, status_code
|
return result, status_code
|
||||||
|
|
||||||
@ -136,6 +139,6 @@ def delete_user():
|
|||||||
|
|
||||||
jti = jwt['jti']
|
jti = jwt['jti']
|
||||||
exp = jwt['exp']
|
exp = jwt['exp']
|
||||||
UserService.logout(jti, exp)
|
UserService.logout(jti, exp, user_id)
|
||||||
|
|
||||||
return result, status_code
|
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 db_connection
|
||||||
from app.extensions import jwt_redis_blocklist
|
from app.extensions import jwt_redis_blocklist
|
||||||
|
|
||||||
from app.mail_utils import send_mail
|
from app.mail.mail import send_mail
|
||||||
|
|
||||||
|
|
||||||
class UserService:
|
class UserService:
|
||||||
@ -62,7 +62,7 @@ class UserService:
|
|||||||
print(f"Error: {e}")
|
print(f"Error: {e}")
|
||||||
return {"Failed": "Failed to insert into database. Username or email are likely in use already"}, 500
|
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
|
return {"Success": "User created successfully"}, 200
|
||||||
|
|
||||||
@ -81,10 +81,11 @@ class UserService:
|
|||||||
try:
|
try:
|
||||||
with db_connection.cursor(dictionary=True) as cursor:
|
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()
|
result = cursor.fetchone()
|
||||||
|
|
||||||
user_id = result['id']
|
user_id = result['id']
|
||||||
|
email = result['email']
|
||||||
password_hash = result['password']
|
password_hash = result['password']
|
||||||
|
|
||||||
if user_id is None:
|
if user_id is None:
|
||||||
@ -97,13 +98,15 @@ class UserService:
|
|||||||
|
|
||||||
token = create_access_token(identity=user_id, expires_delta=expire)
|
token = create_access_token(identity=user_id, expires_delta=expire)
|
||||||
|
|
||||||
|
send_mail("login", email)
|
||||||
|
|
||||||
return {"token": token}, 200
|
return {"token": token}, 200
|
||||||
|
|
||||||
except Error as e:
|
except Error as e:
|
||||||
return {"Failed": f"Failed to login. Error: {e}"}, 500
|
return {"Failed": f"Failed to login. Error: {e}"}, 500
|
||||||
|
|
||||||
@staticmethod
|
@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.
|
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.
|
:return: Tuple containing a dictionary and an HTTP status code.
|
||||||
:rtype: Tuple[Union[dict, str], int]
|
:rtype: Tuple[Union[dict, str], int]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
UserService.__invalidate_token(jti, exp)
|
UserService.__invalidate_token(jti, exp)
|
||||||
|
UserService.__send_email("logout", id=user_id)
|
||||||
|
|
||||||
return {"Success": "Successfully logged out"}, 200
|
return {"Success": "Successfully logged out"}, 200
|
||||||
|
|
||||||
@ -129,6 +134,8 @@ class UserService:
|
|||||||
:rtype: Tuple[Union[dict, str], int]
|
:rtype: Tuple[Union[dict, str], int]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
UserService.__send_email("delete", id=user_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with db_connection.cursor() as cursor:
|
with db_connection.cursor() as cursor:
|
||||||
cursor.execute("delete from user where id = %s", (user_id,))
|
cursor.execute("delete from user where id = %s", (user_id,))
|
||||||
@ -215,6 +222,38 @@ class UserService:
|
|||||||
|
|
||||||
return {"Success": "Password successfully updated"}, 200
|
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
|
@staticmethod
|
||||||
def __invalidate_token(jti: str, exp: int):
|
def __invalidate_token(jti: str, exp: int):
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user