308 lines
11 KiB
Python
308 lines
11 KiB
Python
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
|
|
import requests
|
|
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(blank=True, null=True)
|
|
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 ShopModel(models.Model):
|
|
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='shop', verbose_name='کاربر')
|
|
shop_name = models.CharField(max_length=100, verbose_name='نام فروشگاه')
|
|
shop_description = models.TextField(verbose_name='توضیحات فروشگاه')
|
|
|
|
def __str__(self):
|
|
return f"{self.user.phone} - {self.shop_name}"
|
|
|
|
class Meta:
|
|
verbose_name = 'فروشگاه'
|
|
verbose_name_plural = 'فروشگاه ها'
|
|
|
|
|
|
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, verbose_name='ادرس اصلی کاربر')
|
|
|
|
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'
|
|
|
|
class Meta:
|
|
verbose_name = 'اشتراک نوتیفیکیشن'
|
|
verbose_name_plural = 'اشتراک های نوتیفیکیشن'
|
|
|
|
def send_notif(self, title, body, icon):
|
|
payload = {
|
|
"title": 'فروشگاه هی ملز',
|
|
"body": body,
|
|
"icon": 'https://api.heymlz.com' + icon,
|
|
"image": 'https://api.heymlz.com' + icon,
|
|
}
|
|
print(payload)
|
|
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": 'فروشگاه هی ملز',
|
|
"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)
|
|
|
|
|
|
|
|
class NotifBaseModel(models.Model):
|
|
title = models.CharField(max_length=50)
|
|
content = models.TextField()
|
|
image = models.ImageField()
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
is_read = models.BooleanField(default=False)
|
|
|
|
class Meta:
|
|
abstract = True
|
|
|
|
def __str__(self):
|
|
return self.title
|
|
|
|
class NewsModel(NotifBaseModel):
|
|
|
|
def notif_type(self):
|
|
return 'NEWS'
|
|
|
|
|
|
class UserNotificationModel(NotifBaseModel):
|
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
|
|
|
def notif_type(self):
|
|
return 'USER_NOTIF'
|
|
|
|
|
|
def get_location_from_ip(ip_address):
|
|
try:
|
|
response = requests.get(f"http://ip-api.com/json/{ip_address}")
|
|
data = response.json()
|
|
if data["status"] == "success":
|
|
return data['country'], data['regionName'], data['city'], data.get('zip', 'ناموجود'), data['lat'], data['lon'], data['isp']
|
|
else:
|
|
print("Error fetching data: ", data["message"])
|
|
return None
|
|
except Exception as e:
|
|
print(f"An error occurred: {e}")
|
|
return None
|
|
|
|
class SecurityBreachAttemptModel(models.Model):
|
|
ip_address = models.CharField(max_length=100, verbose_name="آدرس آیپی")
|
|
country = models.CharField(max_length=101, verbose_name="کشور", blank=True, null=True)
|
|
region_name = models.CharField(max_length=102, verbose_name="منطقه", blank=True, null=True)
|
|
city = models.CharField(max_length=103, verbose_name="شهر", blank=True, null=True)
|
|
zip_code = models.CharField(max_length=104, verbose_name="کد پستی", blank=True, null=True)
|
|
lon = models.CharField(max_length=105, verbose_name="طول جغرافیایی", blank=True, null=True)
|
|
lat = models.CharField(max_length=106, verbose_name="عرض جغرافیایی", blank=True, null=True)
|
|
isp = models.CharField(max_length=107, verbose_name="ارائهدهنده اینترنت (ISP)", blank=True, null=True)
|
|
viewd = models.BooleanField(default=False, verbose_name='تماشا شده')
|
|
created_at = models.DateTimeField(auto_now_add=True, verbose_name='شروع حمله')
|
|
trys = models.IntegerField(default=0, verbose_name='تعداد تلاش ها')
|
|
|
|
def save(self, *args, **kwargs):
|
|
if not self.id:
|
|
location_data = get_location_from_ip(self.ip_address)
|
|
if location_data:
|
|
self.country, self.region_name, self.city, self.zip_code, self.lat, self.lon, self.isp = location_data
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
def __str__(self):
|
|
return f'تلاش نفوذ از {self.ip_address} در {self.city}, {self.country}'
|
|
class Meta:
|
|
verbose_name = "تلاش نفوذ"
|
|
verbose_name_plural = "تلاشهای نفوذ"
|
|
|
|
# class NotifModel(models.Model):
|
|
# subject = models.CharField(max_length=100)
|
|
# description = models.TextField()
|
|
|
|
# def __str__(self):
|
|
# return f'{self.subject[:30]}'
|
|
|
|
from product.models import ProductModel
|
|
|
|
class UserFavorites(models.Model):
|
|
user = models.OneToOneField(User, verbose_name=_('User'), on_delete=models.CASCADE)
|
|
products = models.ManyToManyField(ProductModel, verbose_name=_('Likes'), blank=True,)
|
|
|
|
def __str__(self):
|
|
return f'{self.user} likes'
|
|
|
|
class Meta:
|
|
verbose_name = _("User Favorites")
|
|
verbose_name_plural = _("Users Favorites") |