Files
vontor-cz/backend/account/models.py
2025-11-12 02:12:41 +01:00

169 lines
5.5 KiB
Python

import uuid
from django.db import models
from django.contrib.auth.models import AbstractUser, UserManager, Group, Permission
from django.core.validators import RegexValidator
from django.utils.crypto import get_random_string
from django.conf import settings
from django.db import models
from django.utils import timezone
from datetime import timedelta
from vontor_cz.models import SoftDeleteModel
from django.contrib.auth.models import UserManager
import logging
logger = logging.getLogger(__name__)
class CustomUserManager(UserManager):
# Inherit get_by_natural_key and all auth behaviors
use_in_migrations = True
class ActiveUserManager(CustomUserManager):
def get_queryset(self):
return super().get_queryset().filter(is_active=True)
class CustomUser(SoftDeleteModel, AbstractUser):
groups = models.ManyToManyField(
Group,
related_name="customuser_set",
blank=True,
help_text="The groups this user belongs to.",
related_query_name="customuser",
)
user_permissions = models.ManyToManyField(
Permission,
related_name="customuser_set",
blank=True,
help_text="Specific permissions for this user.",
related_query_name="customuser",
)
class Role(models.TextChoices):
ADMIN = "admin", "Admin"
MANAGER = "mod", "Moderator"
CUSTOMER = "regular", "Regular"
role = models.CharField(max_length=20, choices=Role.choices, default=Role.CUSTOMER)
phone_number = models.CharField(
null=True,
blank=True,
unique=True,
max_length=16,
validators=[RegexValidator(r'^\+?\d{9,15}$', message="Zadejte platné telefonní číslo.")]
)
email_verified = models.BooleanField(default=False)
email = models.EmailField(unique=True, db_index=True)
# + fields for email verification flow
email_verification_token = models.CharField(max_length=128, null=True, blank=True, db_index=True)
email_verification_sent_at = models.DateTimeField(null=True, blank=True)
#misc
gdpr = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
create_time = models.DateTimeField(auto_now_add=True)
#adresa
postal_code = models.CharField(max_length=20, blank=True)
city = models.CharField(null=True, blank=True, max_length=100)
street = models.CharField(null=True, blank=True, max_length=200)
street_number = models.PositiveIntegerField(null=True, blank=True)
country = models.CharField(null=True, blank=True, max_length=100)
# firemní fakturační údaje
company_name = models.CharField(max_length=255, blank=True)
ico = models.CharField(max_length=20, blank=True)
dic = models.CharField(max_length=20, blank=True)
postal_code = models.CharField(
blank=True,
null=True,
max_length=5,
validators=[
RegexValidator(
regex=r'^\d{5}$',
message="Postal code must contain exactly 5 digits.",
code='invalid_postal_code'
)
]
)
USERNAME_FIELD = "username"
REQUIRED_FIELDS = [
"email"
]
# Ensure default manager has get_by_natural_key
objects = CustomUserManager()
# Optional convenience manager for active users only
active = ActiveUserManager()
def delete(self, *args, **kwargs):
self.is_active = False
return super().delete(*args, **kwargs)
def save(self, *args, **kwargs):
is_new = self._state.adding # True if object hasn't been saved yet
# Pre-save flags for new users
if is_new:
if self.is_superuser or self.role == "admin":
# ensure admin flags are consistent
self.is_active = True
self.is_staff = True
self.is_superuser = True
self.role = "admin"
else:
self.is_staff = False
# First save to obtain a primary key
super().save(*args, **kwargs)
# Assign group after we have a PK
if is_new:
from django.contrib.auth.models import Group
group, _ = Group.objects.get_or_create(name=self.role)
# Use add/set now that PK exists
self.groups.set([group])
return super().save(*args, **kwargs)
def generate_email_verification_token(self, length: int = 48, save: bool = True) -> str:
token = get_random_string(length=length)
self.email_verification_token = token
self.email_verification_sent_at = timezone.now()
if save:
self.save(update_fields=["email_verification_token", "email_verification_sent_at"])
return token
def verify_email_token(self, token: str, max_age_hours: int = 48, save: bool = True) -> bool:
if not token or not self.email_verification_token:
return False
# optional expiry check
if self.email_verification_sent_at:
age = timezone.now() - self.email_verification_sent_at
if age > timedelta(hours=max_age_hours):
return False
if token != self.email_verification_token:
return False
if not self.email_verified:
self.email_verified = True
# clear token after success
self.email_verification_token = None
self.email_verification_sent_at = None
if save:
self.save(update_fields=["email_verified", "email_verification_token", "email_verification_sent_at"])
return True