from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin, Group from django.db import models from django.utils.translation import gettext_lazy as _ import random from datetime import datetime, timedelta from django.utils import timezone from rest_framework_simplejwt.token_blacklist.models import BlacklistedToken, OutstandingToken import hashlib from django.contrib import admin from django.conf import settings class UserManager(BaseUserManager): def create_user(self, phone, password=None): if not phone: raise ValueError('Users must have a phone number') user = self.model( phone=phone, ) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, phone, password=None): user = self.create_user( phone=phone, ) user.is_staff = True user.is_superuser = True user.set_password(password) user.save(using=self._db) return user class User(AbstractBaseUser, PermissionsMixin): email = models.EmailField(max_length=255, verbose_name='ایمیل', blank=True, null=True) phone = models.CharField(max_length=12, verbose_name='شماره تماس', unique=True) first_name = models.CharField(max_length=50, blank=True, verbose_name='نام') last_name = models.CharField(max_length=50, blank=True, verbose_name='نام خانوادگی') profile_photo = models.ImageField(upload_to='profile_photos/', null=True, blank=True, verbose_name='عکس پروفایل') is_active = models.BooleanField(default=True, verbose_name='فعال بودن کاربر') is_staff = models.BooleanField(default=False, verbose_name='کارمند') gender_option = ( ('مرد', 'مرد'), ('زن', 'زن') ) gender = models.CharField(choices=gender_option, max_length=20, verbose_name='جنسیت') birth_date = models.DateField() date_joined = models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ثبتنام') otp_hash = models.CharField(max_length=64, null=True, blank=True, verbose_name='کد یک بار مصرف') otp_expiry = models.DateTimeField(null=True, blank=True, verbose_name='تاریخ تمام شدن کد یک بار مصرف') video_uploader = models.BooleanField(default=False, help_text='اپلود کننده ی ویدیویی اموزشی پنل ادمین', verbose_name='اپلودر اموزش') objects = UserManager() USERNAME_FIELD = 'phone' REQUIRED_FIELDS = [] @property def groups(self): return None @property def full_name(self): if self.first_name and self.last_name: return f"{self.first_name} {self.last_name}" return None @property def user_permissions(self): return None class Meta: verbose_name = 'کاربر' verbose_name_plural = 'کاربران' def _hash_otp(self, otp): return hashlib.sha256(otp.encode()).hexdigest() def set_otp(self): raw_otp = str(random.randint(100000, 999999)) self.otp_hash = self._hash_otp(raw_otp) self.otp_expiry = timezone.now() + timedelta(minutes=5) self.save() return raw_otp def clear_otp(self): self.otp_hash = None self.otp_expiry = None self.save() def verify_otp(self, otp_code): if self.otp_hash and self.otp_expiry > timezone.now(): return self.otp_hash == self._hash_otp(otp_code) return False def blacklist_user_tokens(self): try: tokens = OutstandingToken.objects.filter(user=self) for token in tokens: BlacklistedToken.objects.get_or_create(token=token) except Exception as e: print(f"block list error: {e}") def __str__(self): if self.first_name and self.last_name: return f'{self.first_name} {self.last_name}' else: return self.phone def get_name(self): if self.first_name and self.last_name: return f'{self.first_name} {self.last_name}' else: return self.phone class UserAddressModel(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='address', verbose_name='کاربر') name = models.CharField(max_length=30, verbose_name='نام ادرس') address = models.TextField(verbose_name='ادرس کامل') postal_code = models.CharField(max_length=10, verbose_name='کد پستی') phone = models.CharField(max_length=11, verbose_name='شماره تماس برای ارسال', help_text='شماره تماس کاربر و شماره ی ارسالی میتواند متفاوت باشد') city = models.CharField(max_length=30, verbose_name='شهر') province = models.CharField(max_length=30, verbose_name='استان') for_me = models.BooleanField(default=False, verbose_name='برای خود کاربر') is_main = models.BooleanField(default=False) def __str__(self): return f"{self.user.phone}, {self.name}" class Meta: verbose_name = 'ادرس کاربر' verbose_name_plural = 'ادرس های کاربر' import os import json from pywebpush import webpush, WebPushException class PushSubscription(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) endpoint = models.TextField(unique=True) keys = models.JSONField() created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f'{self.user} push' def send_notif(self, title, body, icon=''): payload = { "title": title, "body": body, "icon": 'https://api.heymlz.com' + icon } try: webpush( subscription_info={ "endpoint": self.endpoint, "keys": self.keys }, data=json.dumps(payload), vapid_private_key=settings.VAPID_PRIVATE_KEY, vapid_claims={ "sub": "mailto:admin@example.com" } ) except WebPushException as ex: print("Failed to send notification:", ex) @classmethod def send_group_notification(cls, user, title, body): payload = { "title": title, "body": body, "icon": '' } subscriptions = PushSubscription.objects.filter(user=user) for sub in subscriptions: try: webpush( subscription_info={ "endpoint": sub.endpoint, "keys": sub.keys }, data=json.dumps(payload), vapid_private_key=settings.VAPID_PRIVATE_KEY, vapid_claims={ "sub": "mailto:admin@example.com", 'aud': 'https://mamalizz-cooked.vercel.app' } ) except WebPushException as ex: print(f"Failed to send notification to {sub.user}:", ex)