diff --git a/backend/.env.local b/backend/.env.local index 9f8c9e3..9e28739 100644 --- a/backend/.env.local +++ b/backend/.env.local @@ -24,4 +24,6 @@ SITE_TITLE = '' SITE_HEADER = '' # jwt token configs ACCESS_TOKEN_LIFETIME = 5000 -REFRESH_TOKEN_LIFETIME = 5000 \ No newline at end of file +REFRESH_TOKEN_LIFETIME = 5000 + +SMS_API_KEY = '' \ No newline at end of file diff --git a/backend/account/admin.py b/backend/account/admin.py index 029f197..26e8d99 100644 --- a/backend/account/admin.py +++ b/backend/account/admin.py @@ -6,4 +6,4 @@ from unfold.admin import ModelAdmin @admin.register(User) class UserAdmin(ModelAdmin): list_display = ['phone', 'email', 'is_superuser'] - readonly_fields = ['password', 'last_login', 'otp_expiry'] \ No newline at end of file + readonly_fields = ['password', 'last_login', 'otp_expiry', 'otp_hash'] \ No newline at end of file diff --git a/backend/account/migrations/0007_remove_user_otp_user_otp_hash_alter_user_otp_expiry.py b/backend/account/migrations/0007_remove_user_otp_user_otp_hash_alter_user_otp_expiry.py new file mode 100644 index 0000000..5b6182a --- /dev/null +++ b/backend/account/migrations/0007_remove_user_otp_user_otp_hash_alter_user_otp_expiry.py @@ -0,0 +1,27 @@ +# Generated by Django 5.1.2 on 2024-12-15 17:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0006_useraddressmodel'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='otp', + ), + migrations.AddField( + model_name='user', + name='otp_hash', + field=models.CharField(blank=True, max_length=64, null=True), + ), + migrations.AlterField( + model_name='user', + name='otp_expiry', + field=models.DateTimeField(blank=True, null=True), + ), + ] diff --git a/backend/account/models.py b/backend/account/models.py index 6fef0b4..3f01069 100644 --- a/backend/account/models.py +++ b/backend/account/models.py @@ -5,7 +5,7 @@ import random from datetime import datetime, timedelta from django.utils import timezone from rest_framework_simplejwt.token_blacklist.models import BlacklistedToken, OutstandingToken - +import hashlib class UserManager(BaseUserManager): def create_user(self, phone, password=None): if not phone: @@ -40,8 +40,8 @@ class User(AbstractBaseUser, PermissionsMixin): is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) date_joined = models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ثبتنام') - otp = models.CharField(max_length=6, blank=True, null=True, verbose_name='تاریخ ثبتنام') - otp_expiry = models.DateTimeField(blank=True, null=True, verbose_name='تاریخ تمام شدن otp') + otp_hash = models.CharField(max_length=64, null=True, blank=True, verbose_name='کد یک بار مصرف') + otp_expiry = models.DateTimeField(null=True, blank=True, verbose_name='تاریخ تمام شدن کد یک بار مصرف') objects = UserManager() USERNAME_FIELD = 'phone' @@ -57,19 +57,24 @@ class User(AbstractBaseUser, PermissionsMixin): + def _hash_otp(self, otp): + return hashlib.sha256(otp.encode()).hexdigest() + def set_otp(self): - self.otp = str(random.randint(100000, 999999)) - self.otp_expiry = timezone.now() + timedelta(minutes=5) + 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 = None + self.otp_hash = None self.otp_expiry = None self.save() def verify_otp(self, otp_code): - if self.otp == otp_code and self.otp_expiry > timezone.now(): - return True + if self.otp_hash and self.otp_expiry > timezone.now(): + return self.otp_hash == self._hash_otp(otp_code) return False @@ -89,13 +94,13 @@ class User(AbstractBaseUser, PermissionsMixin): if self.first_name and self.last_name: return f'{self.first_name} {self.last_name}' else: - return self.email + 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.email + return self.phone class UserAddressModel(models.Model): diff --git a/backend/account/views.py b/backend/account/views.py index 5c963a1..eeeb285 100644 --- a/backend/account/views.py +++ b/backend/account/views.py @@ -28,8 +28,8 @@ class SendOTPView(APIView): user, created = User.objects.get_or_create(phone=phone) print(created) print(user.phone) - user.set_otp() - message = f"کد یک بار مصرف : {user.otp}" + otp = user.set_otp() + message = f"کد یک بار مصرف : {otp}" print(message) # send otp return Response({'detail': 'OTP sent successfully'}, status=status.HTTP_200_OK) diff --git a/backend/requirements.txt b/backend/requirements.txt index 53c4c54..de429bc 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -26,6 +26,7 @@ factory_boy==3.3.1 Faker==28.4.1 frozenlist==1.4.1 geoip2==4.8.0 +ghasedak_sms==1.0.3 gnupg==2.3.1 h11==0.14.0 httpagentparser==1.9.5 diff --git a/backend/utils/sms.py b/backend/utils/sms.py new file mode 100644 index 0000000..e0de4c8 --- /dev/null +++ b/backend/utils/sms.py @@ -0,0 +1,16 @@ +from dotenv import load_dotenv +import os +from ghasedak_sms import Ghasedak + +load_dotenv(".env.local") +sms_api = Ghasedak(api_key=os.getenv("SMS_API_KEY")) + +### ارسال پیامک تکی + # response = sms_api.send_single_sms(message=hello, world!, receptor='0935*****', linenumber='3000*****', senddate='', checkid='') + +### ارسال پیامک گروهی + # response = sms_api.send_bulk_sms(message='hello, world!', receptors=['09xxxxxxxxx', '09xxxxxxxxx'], linenumber='3000*****', senddate='', checkid='') + +### ارسال پیام otp + + #response = sms_api.send_otp_sms(receptor='09359****', message='OTP message') \ No newline at end of file