152 lines
5.3 KiB
Python
152 lines
5.3 KiB
Python
from celery import shared_task
|
||
from celery.utils.log import get_task_logger
|
||
from django.core.mail import send_mail
|
||
from django.conf import settings
|
||
from django.utils.http import urlsafe_base64_encode
|
||
from django.utils.encoding import force_bytes
|
||
from django.template.loader import render_to_string
|
||
from .tokens import *
|
||
from .models import CustomUser
|
||
|
||
logger = get_task_logger(__name__)
|
||
|
||
def send_email_with_context(recipients, subject, message=None, template_name=None, html_template_name=None, context=None):
|
||
"""
|
||
General function to send emails with a specific context.
|
||
Supports rendering plain text and HTML templates.
|
||
Converts `user` in context to a plain dict to avoid template access to the model.
|
||
"""
|
||
if isinstance(recipients, str):
|
||
recipients = [recipients]
|
||
|
||
html_message = None
|
||
if template_name or html_template_name:
|
||
# Best effort to resolve both templates if only one provided
|
||
if not template_name and html_template_name:
|
||
template_name = html_template_name.replace(".html", ".txt")
|
||
if not html_template_name and template_name:
|
||
html_template_name = template_name.replace(".txt", ".html")
|
||
|
||
ctx = dict(context or {})
|
||
# Sanitize user if someone passes the model by mistake
|
||
if "user" in ctx and not isinstance(ctx["user"], dict):
|
||
try:
|
||
ctx["user"] = _build_user_template_ctx(ctx["user"])
|
||
except Exception:
|
||
ctx["user"] = {}
|
||
|
||
message = render_to_string(template_name, ctx)
|
||
html_message = render_to_string(html_template_name, ctx)
|
||
|
||
try:
|
||
send_mail(
|
||
subject=subject,
|
||
message=message or "",
|
||
from_email=None,
|
||
recipient_list=recipients,
|
||
fail_silently=False,
|
||
html_message=html_message,
|
||
)
|
||
if settings.EMAIL_BACKEND == 'django.core.mail.backends.console.EmailBackend':
|
||
logger.debug(f"\nEMAIL OBSAH:\n{message}\nKONEC OBSAHU")
|
||
return True
|
||
except Exception as e:
|
||
logger.error(f"E-mail se neodeslal: {e}")
|
||
return False
|
||
|
||
|
||
def _build_user_template_ctx(user: CustomUser) -> dict:
|
||
"""
|
||
Return a plain dict for templates instead of passing the DB model.
|
||
Provides aliases to avoid template errors (firstname vs first_name).
|
||
Adds a backward-compatible key 'get_full_name' for templates using `user.get_full_name`.
|
||
"""
|
||
first_name = getattr(user, "first_name", "") or ""
|
||
last_name = getattr(user, "last_name", "") or ""
|
||
full_name = f"{first_name} {last_name}".strip()
|
||
return {
|
||
"id": user.pk,
|
||
"email": getattr(user, "email", "") or "",
|
||
"first_name": first_name,
|
||
"firstname": first_name, # alias for templates using `firstname`
|
||
"last_name": last_name,
|
||
"lastname": last_name, # alias for templates using `lastname`
|
||
"full_name": full_name,
|
||
"get_full_name": full_name, # compatibility for templates using method-style access
|
||
}
|
||
|
||
#----------------------------------------------------------------------------------------------------
|
||
|
||
# This function sends an email to the user for email verification after registration.
|
||
@shared_task
|
||
def send_email_verification_task(user_id):
|
||
try:
|
||
user = CustomUser.objects.get(pk=user_id)
|
||
except CustomUser.DoesNotExist:
|
||
logger.info(f"Task send_email_verification has failed. Invalid User ID was sent.")
|
||
return 0
|
||
|
||
uid = urlsafe_base64_encode(force_bytes(user.pk))
|
||
# {changed} generate and store a per-user token
|
||
token = user.generate_email_verification_token()
|
||
verify_url = f"{settings.FRONTEND_URL}/email-verification/?uidb64={uid}&token={token}"
|
||
|
||
context = {
|
||
"user": _build_user_template_ctx(user),
|
||
"action_url": verify_url,
|
||
"frontend_url": settings.FRONTEND_URL,
|
||
"cta_label": "Ověřit e‑mail",
|
||
}
|
||
|
||
send_email_with_context(
|
||
recipients=user.email,
|
||
subject="Ověření e‑mailu",
|
||
template_name="email/email_verification.txt",
|
||
html_template_name="email/email_verification.html",
|
||
context=context,
|
||
)
|
||
|
||
|
||
|
||
@shared_task
|
||
def send_email_test_task(email):
|
||
context = {
|
||
"action_url": settings.FRONTEND_URL,
|
||
"frontend_url": settings.FRONTEND_URL,
|
||
"cta_label": "Otevřít aplikaci",
|
||
}
|
||
send_email_with_context(
|
||
recipients=email,
|
||
subject="Testovací e‑mail",
|
||
template_name="email/test.txt",
|
||
html_template_name="email/test.html",
|
||
context=context,
|
||
)
|
||
|
||
|
||
@shared_task
|
||
def send_password_reset_email_task(user_id):
|
||
try:
|
||
user = CustomUser.objects.get(pk=user_id)
|
||
except CustomUser.DoesNotExist:
|
||
logger.info(f"Task send_password_reset_email has failed. Invalid User ID was sent.")
|
||
return 0
|
||
|
||
uid = urlsafe_base64_encode(force_bytes(user.pk))
|
||
token = password_reset_token.make_token(user)
|
||
reset_url = f"{settings.FRONTEND_URL}/reset-password/{uid}/{token}"
|
||
|
||
context = {
|
||
"user": _build_user_template_ctx(user),
|
||
"action_url": reset_url,
|
||
"frontend_url": settings.FRONTEND_URL,
|
||
"cta_label": "Obnovit heslo",
|
||
}
|
||
|
||
send_email_with_context(
|
||
recipients=user.email,
|
||
subject="Obnova hesla",
|
||
template_name="email/password_reset.txt",
|
||
html_template_name="email/password_reset.html",
|
||
context=context,
|
||
) |