feat(email): use HTML templates for all transactional emails

- Update org invite, password reset, email verification, and account activation emails to use HTML templates
- Update MFA deadline reminder and suspension notifications to use HTML templates
- Add html_body parameter to _send_email_async for rich email content
This commit is contained in:
2026-04-05 15:44:22 +00:00
parent f2386ed1da
commit 6325d60097
5 changed files with 127 additions and 50 deletions
+11 -7
View File
@@ -9,6 +9,7 @@ from gatehouse_app.schemas.auth_schema import RegisterSchema, LoginSchema
from gatehouse_app.services.auth_service import AuthService
from gatehouse_app.services.mfa_policy_service import MfaPolicyService
from gatehouse_app.services.notification_service import NotificationService
from gatehouse_app.services.email_templates import build_email_verification_html
from gatehouse_app.utils.decorators import login_required
from gatehouse_app.utils.constants import AuditAction
from gatehouse_app.exceptions.auth_exceptions import InvalidCredentialsError
@@ -32,14 +33,17 @@ def register():
verify_token = EmailVerificationToken.generate(user_id=user.id)
app_url = current_app.config.get("APP_URL", "http://localhost:8080")
verify_link = f"{app_url}/verify-email?token={verify_token.token}"
subject = "Verify your Secuird email address"
body = (
f"Hi {user.full_name or user.email},\n\n"
f"Welcome to Secuird! Please verify your email address by clicking the link below (valid for 24 hours):\n"
f"{verify_link}\n\n"
f"Secuird Security Team"
email_body = build_email_verification_html(
user_name=user.full_name or user.email,
verify_link=verify_link,
expiry_hours=24,
)
NotificationService._send_email_async(
to_address=user.email,
subject="Verify your Secuird email address",
body=f"Verify your Secuird email: {verify_link}",
html_body=email_body,
)
NotificationService._send_email_async(to_address=user.email, subject=subject, body=body)
except Exception as exc:
logging.getLogger(__name__).warning(f"Failed to send verification email on register: {exc}")
+25 -21
View File
@@ -6,6 +6,11 @@ from gatehouse_app.extensions import limiter
from gatehouse_app.utils.response import api_response
from gatehouse_app.services.auth_service import AuthService
from gatehouse_app.services.notification_service import NotificationService
from gatehouse_app.services.email_templates import (
build_password_reset_html,
build_email_verification_html,
build_account_activation_html,
)
_logger = logging.getLogger(__name__)
@@ -27,17 +32,16 @@ def forgot_password():
reset_token = PasswordResetToken.generate(user_id=user.id)
app_url = current_app.config.get("APP_URL", "http://localhost:8080")
reset_link = f"{app_url}/reset-password?token={reset_token.token}"
email_body = build_password_reset_html(
user_name=user.full_name or user.email,
reset_link=reset_link,
expiry_hours=2,
)
NotificationService._send_email_async(
to_address=user.email,
subject="Reset your Secuird password",
body=(
f"Hi {user.full_name or user.email},\n\n"
f"You requested a password reset for your Secuird account.\n\n"
f"Click the link below to reset your password (valid for 2 hours):\n"
f"{reset_link}\n\n"
f"If you did not request this, you can safely ignore this email.\n\n"
f"Secuird Security Team"
),
body=f"Reset your Secuird password: {reset_link}",
html_body=email_body,
)
_logger.info(f"Password reset token generated for user {user.id}")
except Exception as exc:
@@ -129,15 +133,16 @@ def resend_verification():
verify_token = EmailVerificationToken.generate(user_id=user.id)
app_url = current_app.config.get("APP_URL", "http://localhost:8080")
verify_link = f"{app_url}/verify-email?token={verify_token.token}"
email_body = build_email_verification_html(
user_name=user.full_name or user.email,
verify_link=verify_link,
expiry_hours=24,
)
NotificationService._send_email_async(
to_address=user.email,
subject="Verify your Secuird email address",
body=(
f"Hi {user.full_name or user.email},\n\n"
f"Please verify your email address by clicking the link below (valid for 24 hours):\n"
f"{verify_link}\n\n"
f"Secuird Security Team"
),
body=f"Verify your Secuird email: {verify_link}",
html_body=email_body,
)
_logger.info(f"Verification email sent for user {user.id}")
except Exception as exc:
@@ -200,16 +205,15 @@ def resend_activation():
app_url = current_app.config.get("APP_URL", current_app.config.get("FRONTEND_URL", "http://localhost:8080"))
activate_link = f"{app_url}/activate?code={code}"
email_body = build_account_activation_html(
user_name=user.full_name or user.email,
activation_link=activate_link,
)
NotificationService._send_email_async(
to_address=user.email,
subject="Activate your Secuird account",
body=(
f"Hi {user.full_name or user.email},\n\n"
f"Please activate your Secuird account by clicking the link below:\n"
f"{activate_link}\n\n"
f"If you did not create an account, you can safely ignore this email.\n\n"
f"Secuird Security Team"
),
body=f"Activate your Secuird account: {activate_link}",
html_body=email_body,
)
_logger.info(f"Activation email re-sent to {user.id}")
except Exception as exc: