Introduces a OneToOneField for carrier in the Refund model to support future integration and refactoring. Adds and updates TODO comments for email template usage, error handling in Stripe webhooks, and clarifies logic placement in Stripe models.
153 lines
5.4 KiB
Python
153 lines
5.4 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__)
|
||
|
||
# TODO: předělat funkci ať funguje s base.html šablonou na emaily !!!
|
||
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,
|
||
) |