schon/engine/vibes_auth/utils/emailing.py
Egor fureunoir Gorbunov ad320235d6 feat(payments, vibes_auth, core): introduce decimal fields, 2FA, and admin OTP
- Refactored monetary fields across models to use `DecimalField` for improved precision.
- Implemented two-factor authentication (2FA) for admin logins with OTP codes.
- Added ability to generate admin OTP via management commands.
- Updated Docker Compose override for dev-specific port bindings.
- Included template for 2FA OTP verification to enhance security.

Additional changes:
- Upgraded and downgraded various dependencies (e.g., django-celery-beat and yarl).
- Replaced float-based calculations with decimal for consistent rounding behavior.
- Improved admin user management commands for activation and OTP generation.
2026-03-03 00:42:21 +03:00

95 lines
3.3 KiB
Python

from celery.app import shared_task
from constance import config
from django.conf import settings
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.core.mail import EmailMessage
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.utils.translation import activate
from django.utils.translation import gettext_lazy as _
from engine.core.utils import get_dynamic_email_connection
from engine.vibes_auth.models import User
@shared_task(queue="default")
def send_verification_email_task(user_pk: str) -> tuple[bool, str]:
try:
user = User.objects.get(pk=user_pk)
user.refresh_from_db()
raw_token = user.refresh_activation_token()
user.save(update_fields=["activation_token", "activation_token_created"])
activate(user.language)
email_subject = _(f"{settings.PROJECT_NAME} | Activate Account")
email_body = render_to_string(
"../templates/user_verification_email.html",
{
"user_first_name": user.first_name,
"activation_link": f"https://{settings.STOREFRONT_DOMAIN}/{user.language}/activate-user?uid={urlsafe_base64_encode(force_bytes(user.uuid))}"
f"&token={urlsafe_base64_encode(force_bytes(raw_token))}",
"project_name": settings.PROJECT_NAME,
},
)
email = EmailMessage(
subject=email_subject,
body=email_body,
from_email=f"{settings.PROJECT_NAME} <{config.EMAIL_FROM}>",
to=[user.email],
connection=get_dynamic_email_connection(),
)
email.content_subtype = "html"
email.send()
except User.DoesNotExist:
return False, f"User not found with the given pk: {user_pk}"
except Exception as e:
return False, f"Something went wrong while sending an email: {e!s}"
else:
return True, str(user.uuid)
@shared_task(queue="default")
def send_reset_password_email_task(user_pk: str) -> tuple[bool, str]:
try:
user = User.objects.get(pk=user_pk)
user.refresh_from_db()
activate(user.language)
email_subject = _(f"{settings.PROJECT_NAME} | Reset Password")
email_body = render_to_string(
"../templates/user_reset_password_email.html",
{
"user_first_name": user.first_name,
"reset_link": f"https://{settings.STOREFRONT_DOMAIN}/{user.language}/reset-password?uid="
f"{urlsafe_base64_encode(force_bytes(user.pk))}"
f"&token={PasswordResetTokenGenerator().make_token(user)}",
"project_name": settings.PROJECT_NAME,
},
)
email = EmailMessage(
subject=email_subject,
body=email_body,
from_email=f"{settings.PROJECT_NAME} <{config.EMAIL_FROM}>",
to=[user.email],
connection=get_dynamic_email_connection(),
)
email.content_subtype = "html"
email.send()
except User.DoesNotExist:
return False, f"User not found with the given pk: {user_pk}"
except Exception as e:
return False, f"Something went wrong while sending an email: {e!s}"
else:
return True, str(user.uuid)