Source code for users.email_utils

import logging

from django.conf import settings
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail

logger = logging.getLogger(__name__)


[docs] def send_verification_email(user): """ Send an email verification link to the given user via SendGrid. Constructs an HTML email containing a verification link built from ``settings.FRONTEND_URL`` and the user's ``email_verification_token``. The link routes to ``/verify-email?token=<token>`` on the frontend. In **test mode** (``settings.SEND_EMAILS = False``) no email is sent; the verification link is logged at ``INFO`` level instead and the function returns ``True`` to allow the rest of the registration flow to proceed unaffected. In **production**, a SendGrid API call is made using ``settings.SENDGRID_API_KEY``. Any non-2xx response or raised exception is caught, logged at ``ERROR`` level, and returned as ``False`` — callers are responsible for deciding whether to surface this failure to the user. :param user: The user to whom the verification email should be sent. Must have a non-empty ``email_verification_token``; returns ``False`` immediately if the token is absent. :type user: users.models.User :returns: ``True`` if the email was sent successfully (or skipped in test mode), ``False`` if the token is missing, SendGrid returns a non-2xx status, or any exception is raised during the API call. :rtype: bool **Required settings:** - ``SEND_EMAILS`` — ``False`` to suppress sending in test/dev environments. - ``FRONTEND_URL`` — Base URL used to construct the verification link. - ``DEFAULT_FROM_EMAIL`` — Sender address for all outbound mail. - ``SENDGRID_API_KEY`` — API key for the SendGrid client. Example:: >>> token = user.generate_verification_token() >>> success = send_verification_email(user) >>> if not success: ... logger.warning('Verification email failed; user must request resend.') """ if not user.email_verification_token: return False if not settings.SEND_EMAILS: logger.info(f"TEST MODE: Would send verification email to {user.email}") logger.info(f"Verification link: {settings.FRONTEND_URL}/verify-email?token={user.email_verification_token}") return True verification_link = f"{settings.FRONTEND_URL}/verify-email?token={user.email_verification_token}" message = Mail( from_email=settings.DEFAULT_FROM_EMAIL, to_emails=user.email, subject="Verify your BruinBridge account", html_content=f""" <p>Please verify your email address to activate your account.</p> <p>Click the link below or copy and paste it into your browser:</p> <a href="{verification_link}">{verification_link}</a> <p>This link will expire in 1 hour.</p> """ ) try: sg = SendGridAPIClient(settings.SENDGRID_API_KEY) response = sg.send(message) if 200 <= response.status_code < 300: logger.info(f"Verification email sent to {user.email}") return True else: logger.error(f"SendGrid returned status {response.status_code}") return False except Exception as e: logger.error(f"Error sending verification email to {user.email}: {str(e)}") return False
[docs] def send_password_reset_email(user): """ Send a password reset link to the given user via SendGrid. Constructs an HTML email containing a reset link built from ``settings.FRONTEND_URL`` and the user's ``password_reset_token``. The link routes to ``/reset-password/<token>`` on the frontend. In **test mode** (``settings.SEND_EMAILS = False``) no email is sent; the reset link is logged at ``INFO`` level instead and the function returns ``True`` to allow the password reset flow to proceed unaffected. In **production**, a SendGrid API call is made using ``settings.SENDGRID_API_KEY``. Any non-2xx response or raised exception is caught, logged at ``ERROR`` level, and returned as ``False`` — callers are responsible for deciding whether to surface this failure to the user. :param user: The user to whom the reset email should be sent. Must have a non-empty ``password_reset_token``; returns ``False`` immediately if the token is absent. :type user: users.models.User :returns: ``True`` if the email was sent successfully (or skipped in test mode), ``False`` if the token is missing, SendGrid returns a non-2xx status, or any exception is raised during the API call. :rtype: bool **Required settings:** - ``SEND_EMAILS`` — ``False`` to suppress sending in test/dev environments. - ``FRONTEND_URL`` — Base URL used to construct the reset link. - ``DEFAULT_FROM_EMAIL`` — Sender address for all outbound mail. - ``SENDGRID_API_KEY`` — API key for the SendGrid client. Example:: >>> token = user.generate_password_reset_token() >>> success = send_password_reset_email(user) >>> if not success: ... logger.warning('Reset email failed; user must request a new link.') """ if not user.password_reset_token: return False if not settings.SEND_EMAILS: logger.info(f"TEST MODE: Would send password reset to {user.email}") logger.info(f"Reset link: {settings.FRONTEND_URL}/reset-password/{user.password_reset_token}") return True reset_link = f"{settings.FRONTEND_URL}/reset-password/{user.password_reset_token}" message = Mail( from_email=settings.DEFAULT_FROM_EMAIL, to_emails=user.email, subject="Reset your BruinBridge password", html_content=f""" <p>You requested to reset your password.</p> <p>To set a new password, click the link below or copy and paste it into your browser:</p> <a href="{reset_link}">{reset_link}</a> <p>This link will expire in 1 hour.</p> """ ) try: sg = SendGridAPIClient(settings.SENDGRID_API_KEY) response = sg.send(message) if 200 <= response.status_code < 300: logger.info(f"Password reset email sent to {user.email}") return True else: logger.error(f"SendGrid returned status {response.status_code}") return False except Exception as e: logger.error(f"Error sending password reset to {user.email}: {str(e)}") return False