fix: remove incorrect get_users override, keep logging in save method only

This commit is contained in:
Игорь Надеин 2026-01-05 11:20:56 +03:00
parent 80b19dd1b3
commit b9fca4ddab
4 changed files with 103 additions and 8 deletions

View File

@ -1,4 +1,5 @@
# backend/common/emails.py
import logging
from pathlib import Path
from typing import Iterable, Mapping, Optional
@ -7,6 +8,8 @@ from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.html import strip_tags
logger = logging.getLogger(__name__)
def send_templated_mail(
subject: str,
@ -22,23 +25,54 @@ def send_templated_mail(
templates/<template_base>.txt (опционально)
attachments: список троек (name, content_bytes, mimetype)
"""
html_body = render_to_string(f"{template_base}.html", context)
to_list = list(to)
logger.info(
f"[send_templated_mail] 📧 НАЧАЛО ОТПРАВКИ EMAIL\n"
f" Subject: '{subject}'\n"
f" Template: {template_base}\n"
f" Recipients: {to_list}\n"
f" From: {from_email or settings.DEFAULT_FROM_EMAIL}"
)
try:
html_body = render_to_string(f"{template_base}.html", context)
logger.info(f"[send_templated_mail] ✅ HTML шаблон загружен ({len(html_body)} символов)")
except Exception as e:
logger.error(f"[send_templated_mail] ❌ Ошибка загрузки HTML шаблона: {e}")
raise
# .txt файл опционален — если его нет, берём strip_tags от html
try:
text_body = render_to_string(f"{template_base}.txt", context)
logger.info(f"[send_templated_mail] ✅ TXT шаблон загружен ({len(text_body)} символов)")
except Exception:
text_body = strip_tags(html_body)
logger.info(f"[send_templated_mail] ⚠️ TXT шаблон не найден, используется strip_tags от HTML")
email = EmailMultiAlternatives(
subject=subject,
body=text_body,
from_email=from_email or settings.DEFAULT_FROM_EMAIL,
to=list(to),
to=to_list,
)
email.attach_alternative(html_body, "text/html")
if attachments:
for name, data, mimetype in attachments:
email.attach(name, data, mimetype)
logger.info(f"[send_templated_mail] 📎 Вложение: {name} ({len(data)} bytes, {mimetype})")
logger.info(f"[send_templated_mail] ➡️ Вызов email.send()...")
try:
result = email.send(fail_silently=False)
logger.info(f"[send_templated_mail] ✅ EMAIL УСПЕШНО ОТПРАВЛЕН (result={result})")
return result
except Exception as e:
logger.error(
f"[send_templated_mail] ❌ ОШИБКА ОТПРАВКИ EMAIL: {type(e).__name__}: {e}",
exc_info=True
)
raise
return email.send(fail_silently=False)

View File

@ -2,14 +2,17 @@
from __future__ import annotations
import base64
import logging
import re
from typing import Any, Dict, List, Optional
from django import forms
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.forms import AuthenticationForm, PasswordResetForm
from django.core.files.uploadedfile import UploadedFile
from PIL import Image
logger = logging.getLogger(__name__)
class EmailOrPhoneAuthenticationForm(AuthenticationForm):
username = forms.CharField(
@ -315,3 +318,36 @@ class RegistrationForm(forms.Form):
class VerifyEmailForm(forms.Form):
email = forms.EmailField(label="Email")
code = forms.CharField(max_length=32, label="Код из письма")
class CustomPasswordResetForm(PasswordResetForm):
"""Кастомная форма восстановления пароля с логированием"""
def save(self, *args, **kwargs):
"""Переопределяем save для логирования процесса отправки"""
email = self.cleaned_data["email"]
logger.info(
f"\n{'='*80}\n"
f"[CustomPasswordResetForm.save] 🔐 СОХРАНЕНИЕ ФОРМЫ ВОССТАНОВЛЕНИЯ ПАРОЛЯ\n"
f" Email: {email}\n"
f"{'='*80}"
)
# Используем встроенный метод get_users - это не статический метод,
# а внутренний механизм Django для получения пользователей
logger.info(
f"[CustomPasswordResetForm.save] ➡️ Вызов super().save() для отправки emails..."
)
try:
result = super().save(*args, **kwargs)
logger.info(
f"[CustomPasswordResetForm.save] ✅ super().save() успешно завершен"
)
return result
except Exception as e:
logger.error(
f"[CustomPasswordResetForm.save] ❌ Ошибка в super().save(): {type(e).__name__}: {e}",
exc_info=True
)
raise

View File

@ -1,5 +1,6 @@
from __future__ import annotations
import logging
from api.client import ApiClient, get_api_client
from django.contrib import messages
from django.contrib.auth import logout
@ -20,7 +21,9 @@ from django.views import View
from django.views.decorators.http import require_http_methods
from .forms_auth import (EmailOrPhoneAuthenticationForm, RegistrationForm,
VerifyEmailForm)
VerifyEmailForm, CustomPasswordResetForm)
logger = logging.getLogger(__name__)
def _apply_api_errors_to_form(form, data: dict):
@ -65,9 +68,6 @@ def register_view(request):
Страница регистрации: показываем Django-форму и шлём валидные данные в /api/v1/auth/register/
(аватар пока локально, в API не отправляем — загрузим после верификации).
"""
import logging
logger = logging.getLogger(__name__)
api = get_api_client(request)
if request.method == "GET":
@ -220,6 +220,27 @@ class PasswordResetView(DjangoPasswordResetView):
html_email_template_name = "emails/password_reset_email.html"
subject_template_name = "auth/password_reset_subject.txt"
success_url = "/auth/password-reset/done/"
form_class = CustomPasswordResetForm
def post(self, request, *args, **kwargs):
"""Переопределяем post для логирования"""
logger.info(
f"\n{'='*80}\n"
f"[PasswordResetView.post] 🔑 ЗАПРОС НА ВОССТАНОВЛЕНИЕ ПАРОЛЯ\n"
f" Email/телефон: {request.POST.get('email', '?')}\n"
f" IP: {request.META.get('REMOTE_ADDR')}\n"
f"{'='*80}"
)
return super().post(request, *args, **kwargs)
def form_valid(self, form):
"""Логирование успешной валидации формы"""
logger.info(
f"[PasswordResetView.form_valid] ✅ Форма валидна, email={form.cleaned_data.get('email')}"
)
return super().form_valid(form)
class PasswordResetDoneView(DjangoPasswordResetDoneView):

View File

@ -208,6 +208,10 @@ LOGGING = {
"documents": {"handlers": ["console"], "level": "DEBUG", "propagate": False},
"bots": {"handlers": ["console"], "level": "DEBUG", "propagate": False},
"communications": {"handlers": ["console"], "level": "DEBUG", "propagate": False},
"notifications": {"handlers": ["console"], "level": "INFO", "propagate": False},
"requests_app": {"handlers": ["console"], "level": "DEBUG", "propagate": False},
"employees": {"handlers": ["console"], "level": "INFO", "propagate": False},
"common": {"handlers": ["console"], "level": "INFO", "propagate": False},
"django": {"handlers": ["console"], "level": "INFO", "propagate": False},
"aiogram": {"handlers": ["console"], "level": "INFO", "propagate": False},
# полезно видеть ошибки и отладку ldap3