feat: replace email flows with in-app notifications

Add recipient notification detail and filtering, a queued admin composer, and contextual automatic titles. Retire email delivery, obsolete auth routes, newsletter data, and related templates and settings.
This commit is contained in:
2026-09-26 12:23:10 +03:30
parent b706a2d10a
commit 3ec28638d1
54 changed files with 787 additions and 4843 deletions

View File

@@ -1,10 +1,6 @@
from django.contrib.auth.models import AbstractUser, UserManager as DjangoUserManager
from django.utils import timezone
from django.db import models
import uuid
from datetime import timedelta
from core.media import (
delete_image_derivatives_by_name,
get_image_previous_name,
@@ -80,11 +76,6 @@ class User(AbstractUser, BaseModel):
)
is_email_verified = models.BooleanField(default=False)
is_mobile_verified = models.BooleanField(default=False)
email_verification_token = models.UUIDField(default=uuid.uuid4, unique=True)
email_verification_sent_at = models.DateTimeField(null=True, blank=True)
password_reset_token = models.UUIDField(null=True, blank=True, unique=True)
password_reset_token_expires_at = models.DateTimeField(null=True, blank=True)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['mobile']
@@ -95,6 +86,7 @@ class User(AbstractUser, BaseModel):
db_table = 'users'
verbose_name = 'User'
verbose_name_plural = 'Users'
permissions = [('send_notification', 'Can send in-app notifications')]
def __str__(self):
identity = self.mobile or self.email or self.username
@@ -124,21 +116,15 @@ class User(AbstractUser, BaseModel):
is_active=True,
).exists()
def regenerate_verification_token(self):
self.email_verification_token = uuid.uuid4()
self.save(update_fields=['email_verification_token'])
def set_password_reset_token(self):
"""Generates a new password reset token and sets its expiry."""
self.password_reset_token = uuid.uuid4()
self.password_reset_token_expires_at = timezone.now() + timedelta(hours=1)
self.save(update_fields=['password_reset_token', 'password_reset_token_expires_at'])
def save(self, *args, **kwargs):
previous_image_name = get_image_previous_name(self, "profile_picture")
current_image_name = self.profile_picture.name if self.profile_picture else None
self.email = normalize_email_identity(self.email)
self.mobile = normalize_mobile_number(self.mobile)
if kwargs.get('update_fields') is None or 'email' in kwargs['update_fields']:
self.is_email_verified = bool(self.email)
if kwargs.get('update_fields') is not None:
kwargs['update_fields'] = set(kwargs['update_fields']) | {'is_email_verified'}
super().save(*args, **kwargs)