Browse Source

fix guest user token

master
alireza 1 year ago
parent
commit
aa5613ed12
  1. 57
      apps/account/migrations/0008_auto_20250316_1247.py
  2. 31
      apps/account/migrations/0009_auto_20250316_1319.py
  3. 18
      apps/account/migrations/0010_loginhistory_timezone.py
  4. 13
      apps/account/models/notification.py
  5. 75
      apps/account/models/user.py
  6. 4
      apps/account/serializers/notification.py
  7. 32
      apps/account/serializers/user.py
  8. 1
      apps/account/urls.py
  9. 52
      apps/account/views/notification.py
  10. 95
      apps/account/views/user.py

57
apps/account/migrations/0008_auto_20250316_1247.py

@ -0,0 +1,57 @@
# Generated by Django 3.2.7 on 2025-03-16 12:47
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('account', '0007_notification'),
]
operations = [
migrations.AddField(
model_name='notification',
name='service',
field=models.CharField(choices=[('imam-javad', 'Imam Javad'), ('doboodi', 'Doboodi')], default='imam-javad', max_length=20, verbose_name='service'),
),
migrations.AddField(
model_name='user',
name='device_os',
field=models.CharField(choices=[('android', 'android'), ('apple', 'apple')], max_length=16, null=True),
),
migrations.AddField(
model_name='user',
name='username',
field=models.CharField(blank=True, max_length=150, null=True, unique=True),
),
migrations.AlterField(
model_name='user',
name='email',
field=models.EmailField(blank=True, help_text="Enter the user's email address.", max_length=254, null=True, unique=True, verbose_name='Email Address'),
),
migrations.AlterField(
model_name='user',
name='fullname',
field=models.CharField(blank=True, help_text='Enter the full name of the user.', max_length=255, null=True, verbose_name='Full Name'),
),
migrations.AlterUniqueTogether(
name='user',
unique_together={('email', 'device_id')},
),
migrations.CreateModel(
name='LocationHistory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lat', models.FloatField(blank=True, null=True, verbose_name='lat')),
('lon', models.FloatField(blank=True, null=True, verbose_name='lon')),
('country', models.CharField(blank=True, max_length=255, null=True, verbose_name='country')),
('city', models.CharField(blank=True, max_length=255, null=True, verbose_name='city')),
('ip', models.CharField(max_length=255, null=True)),
('at_time', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='location_history', to=settings.AUTH_USER_MODEL)),
],
),
]

31
apps/account/migrations/0009_auto_20250316_1319.py

@ -0,0 +1,31 @@
# Generated by Django 3.2.7 on 2025-03-16 13:19
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('account', '0008_auto_20250316_1247'),
]
operations = [
migrations.CreateModel(
name='LoginHistory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lat', models.FloatField(blank=True, null=True, verbose_name='lat')),
('lon', models.FloatField(blank=True, null=True, verbose_name='lon')),
('country', models.CharField(blank=True, max_length=255, null=True, verbose_name='country')),
('city', models.CharField(blank=True, max_length=255, null=True, verbose_name='city')),
('ip', models.CharField(max_length=255, null=True)),
('at_time', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='login_history', to=settings.AUTH_USER_MODEL)),
],
),
migrations.DeleteModel(
name='LocationHistory',
),
]

18
apps/account/migrations/0010_loginhistory_timezone.py

@ -0,0 +1,18 @@
# Generated by Django 3.2.7 on 2025-03-16 13:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0009_auto_20250316_1319'),
]
operations = [
migrations.AddField(
model_name='loginhistory',
name='timezone',
field=models.CharField(blank=True, max_length=100, null=True),
),
]

13
apps/account/models/notification.py

@ -2,17 +2,24 @@ from django.db import models
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
class Notification(models.Model): class Notification(models.Model):
class ServiceChoices(models.TextChoices):
IMAM_JAVAD = 'imam-javad', 'Imam Javad'
DOBOODI = 'doboodi', 'Doboodi'
title = models.CharField(max_length=255, verbose_name=_('title')) title = models.CharField(max_length=255, verbose_name=_('title'))
message = models.TextField(max_length=512, verbose_name=_('message')) message = models.TextField(max_length=512, verbose_name=_('message'))
user = models.ForeignKey("account.User", on_delete=models.CASCADE, verbose_name=_('user'), related_name='notifications') user = models.ForeignKey("account.User", on_delete=models.CASCADE, verbose_name=_('user'), related_name='notifications')
is_read = models.BooleanField(default=False, verbose_name=_('is read')) is_read = models.BooleanField(default=False, verbose_name=_('is read'))
service = models.CharField(
max_length=20,
choices=ServiceChoices.choices,
default=ServiceChoices.IMAM_JAVAD,
verbose_name=_('service')
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at'), null=True) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at'), null=True)
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'), null=True) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'), null=True)
def __str__(self): def __str__(self):
return self.title return self.title

75
apps/account/models/user.py

@ -11,6 +11,10 @@ from apps.account.manager import UserManager
class User(AbstractUser): class User(AbstractUser):
class DeviceOs(models.TextChoices):
android = 'android', 'android'
apple = 'apple', 'apple'
class UserType(models.TextChoices): class UserType(models.TextChoices):
PROFESSOR = 'professor', 'Professor' PROFESSOR = 'professor', 'Professor'
CLIENT = 'client', 'Client' CLIENT = 'client', 'Client'
@ -22,32 +26,28 @@ class User(AbstractUser):
MALE = 'male', 'Male' MALE = 'male', 'Male'
FEMALE = 'female', 'Female' FEMALE = 'female', 'Female'
email = models.EmailField(unique=True, verbose_name="Email Address", help_text="Enter the user's email address.")
fullname = models.CharField(max_length=255, verbose_name="Full Name", help_text="Enter the full name of the user.")
last_name = None
first_name = None
username = models.CharField(unique=True, null=True, blank=True, max_length=150)
email = models.EmailField(unique=True, verbose_name="Email Address", help_text="Enter the user's email address.", null=True, blank=True)
fullname = models.CharField(max_length=255, verbose_name="Full Name", help_text="Enter the full name of the user.", null=True, blank=True)
birthdate = models.DateField(verbose_name=_('birthdate'), null=True, blank=True) birthdate = models.DateField(verbose_name=_('birthdate'), null=True, blank=True)
avatar = models.ImageField(null=True, blank=True, upload_to='users/avatars/%Y/%m/') avatar = models.ImageField(null=True, blank=True, upload_to='users/avatars/%Y/%m/')
phone_number = PhoneNumberField(unique=True, validators=[validate_possible_number], null=True, blank=True, verbose_name=_('phone')) phone_number = PhoneNumberField(unique=True, validators=[validate_possible_number], null=True, blank=True, verbose_name=_('phone'))
language = LanguageField(null=True) language = LanguageField(null=True)
username = None
last_name = None
first_name = None
gender = models.CharField(
max_length=20, choices=GenderChoices.choices, null=True, blank=True, verbose_name=_('Gender'), help_text="Select the user's gender."
)
user_type = models.CharField(
max_length=20,
choices=UserType.choices,
default=UserType.CLIENT,
verbose_name="User Type",
help_text="Type of the user."
)
gender = models.CharField(max_length=20, choices=GenderChoices.choices, null=True, blank=True, verbose_name=_('Gender'), help_text="Select the user's gender.")
user_type = models.CharField(max_length=20, choices=UserType.choices, default=UserType.CLIENT, verbose_name="User Type", help_text="Type of the user.")
date_joined = models.DateTimeField(auto_now_add=True, verbose_name="Date Joined", help_text="The date and time the user registered.")
city = models.CharField(verbose_name=_('City'), max_length=255, null=True, blank=True) city = models.CharField(verbose_name=_('City'), max_length=255, null=True, blank=True)
country = models.CharField(max_length=255, verbose_name=_('country'), null=True, blank=True) country = models.CharField(max_length=255, verbose_name=_('country'), null=True, blank=True)
device_id = models.CharField(verbose_name=_('device id'), max_length=255, null=True, blank=True) device_id = models.CharField(verbose_name=_('device id'), max_length=255, null=True, blank=True)
device_os = models.CharField(choices=DeviceOs.choices, null=True, max_length=16)
fcm = models.CharField(max_length=512, null=True, blank=True) fcm = models.CharField(max_length=512, null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True, verbose_name="Date Joined", help_text="The date and time the user registered.")
is_staff = models.BooleanField(default=False) is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True, verbose_name="Active", help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.") is_active = models.BooleanField(default=True, verbose_name="Active", help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.")
deleted_at = models.DateTimeField(null=True, blank=True) deleted_at = models.DateTimeField(null=True, blank=True)
@ -57,31 +57,36 @@ class User(AbstractUser):
EMAIL_FIELD = "email" EMAIL_FIELD = "email"
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["fullname", ]
USERNAME_FIELD = "username"
REQUIRED_FIELDS = []
def __str__(self):
username = self.email or self.fullname or self.device_id
return f"{username}-({self.user_type})"
def soft_delete(self): def soft_delete(self):
self.deleted_at = timezone.now() self.deleted_at = timezone.now()
self.is_active = False self.is_active = False
self.fullname = f'{self.fullname}:deleted' self.fullname = f'{self.fullname}:deleted'
number = str(random.randint(1000000000, 9999999999)) # ایجاد یک عدد رندوم 10 رقمی
number = str(random.randint(1000000000, 9999999999))
self.phone_number = f'{self.phone_number}:deleted{number}' self.phone_number = f'{self.phone_number}:deleted{number}'
self.email = f'{self.email}:deleted{number}' if self.email else None self.email = f'{self.email}:deleted{number}' if self.email else None
self.save() self.save()
# def clean(self):
# super().clean()
# if self.email == "":
# # fix db uniqueness error bcz of django charfield null to empty string conversion
# self.email = None
def __str__(self):
return f"{self.email} - {self.get_full_name()} - ({self.user_type})"
def save(self, *args, **kwargs):
self.username = self.email
if User.objects.filter(username=self.email).count():
self.username = f'{self.email}:{self.id}'
return super().save(*args, **kwargs)
def get_full_name(self): def get_full_name(self):
return self.fullname return self.fullname
@property
def is_guest(self):
return self.email is None
@property @property
def user_type_based_on_groups(self): def user_type_based_on_groups(self):
@ -97,3 +102,17 @@ class User(AbstractUser):
ordering = ("-id",) ordering = ("-id",)
verbose_name = "All Users" verbose_name = "All Users"
verbose_name_plural = "All Users" verbose_name_plural = "All Users"
unique_together = (
'email', 'device_id'
)
class LoginHistory(models.Model):
user = models.ForeignKey("account.User", on_delete=models.CASCADE, related_name='login_history')
lat = models.FloatField(verbose_name=_('lat'), null=True, blank=True)
lon = models.FloatField(verbose_name=_('lon'), null=True, blank=True)
country = models.CharField(max_length=255, verbose_name=_('country'), null=True, blank=True)
city = models.CharField(max_length=255, verbose_name=_('city'), null=True, blank=True)
ip = models.CharField(max_length=255, null=True)
timezone = models.CharField(max_length=100, null=True, blank=True)
at_time = models.DateTimeField(auto_now_add=True)

4
apps/account/serializers/notification.py

@ -9,10 +9,11 @@ from apps.account.models import User
class NotificationSerializer(serializers.ModelSerializer): class NotificationSerializer(serializers.ModelSerializer):
user_type = serializers.ChoiceField(choices=[('user', 'User'), ('merchant', 'Merchant')], default='user') user_type = serializers.ChoiceField(choices=[('user', 'User'), ('merchant', 'Merchant')], default='user')
service = serializers.ChoiceField(choices=Notification.ServiceChoices.choices, default=Notification.ServiceChoices.IMAM_JAVAD)
class Meta: class Meta:
model = Notification model = Notification
fields = ['id', 'title', 'message', 'is_read', 'user_type', 'created_at', 'updated_at']
fields = ['id', 'title', 'message', 'is_read', 'user_type', 'service', 'created_at', 'updated_at']
@ -22,3 +23,4 @@ class NotificationSendSerializer(serializers.Serializer):
data = serializers.DictField(required=False) data = serializers.DictField(required=False)
account_id = serializers.CharField(required=True) account_id = serializers.CharField(required=True)
user_type = serializers.CharField(required=True) user_type = serializers.CharField(required=True)
service = serializers.ChoiceField(choices=Notification.ServiceChoices.choices, default=Notification.ServiceChoices.IMAM_JAVAD)

32
apps/account/serializers/user.py

@ -18,10 +18,11 @@ class UserProfileSerializer(serializers.ModelSerializer):
required=False, required=False,
help_text="Select the user's gender." help_text="Select the user's gender."
) )
fcm = serializers.CharField(required=False, help_text="Firebase Cloud Messaging token.")
class Meta: class Meta:
model = User model = User
fields = ['id', 'fullname', 'avatar', 'email', 'phone_number', 'password', 'info', 'skill', 'city', 'country', 'birthdate', 'gender']
read_only_fields = ['email', 'info', 'skill']
fields = ['id', 'device_id', 'fcm', 'fullname', 'avatar', 'email', 'phone_number', 'password', 'info', 'skill', 'city', 'country', 'birthdate', 'gender']
read_only_fields = ['email', 'info', 'skill', 'device_id']
# def validate_email(self, value): # def validate_email(self, value):
# if User.objects.filter(email=value).exists(): # if User.objects.filter(email=value).exists():
@ -44,7 +45,7 @@ class UserProfileSerializer(serializers.ModelSerializer):
class UserRegisterSerializer(serializers.ModelSerializer): class UserRegisterSerializer(serializers.ModelSerializer):
password_confirmation = serializers.CharField(write_only=True) password_confirmation = serializers.CharField(write_only=True)
fcm = serializers.CharField(required=False) fcm = serializers.CharField(required=False)
device_id = serializers.CharField(required=False)
device_id = serializers.CharField(required=True)
email = serializers.EmailField() email = serializers.EmailField()
class Meta: class Meta:
@ -55,6 +56,7 @@ class UserRegisterSerializer(serializers.ModelSerializer):
'email': {'required': True,}, 'email': {'required': True,},
'password': {'required': True,}, 'password': {'required': True,},
'password_confirmation': {'required': True,}, 'password_confirmation': {'required': True,},
'device_id': {'required': True,},
} }
def validate_email(self, value): def validate_email(self, value):
@ -75,8 +77,6 @@ class UserRegisterSerializer(serializers.ModelSerializer):
# If there are any errors, raise ValidationError # If there are any errors, raise ValidationError
data.pop('password_confirmation', None) data.pop('password_confirmation', None)
data.pop('fcm', None)
data.pop('device_id', None)
return data return data
@ -102,11 +102,12 @@ class UserLoginSerializer(serializers.ModelSerializer):
password = serializers.CharField(style={'input_type': 'password'}, trim_whitespace=False) password = serializers.CharField(style={'input_type': 'password'}, trim_whitespace=False)
fcm = serializers.CharField(required=False) fcm = serializers.CharField(required=False)
device_id = serializers.CharField(required=False) device_id = serializers.CharField(required=False)
timezone = serializers.CharField(required=False, allow_null=True, allow_blank=True)
class Meta: class Meta:
model = User model = User
fields = ['id', 'phone_number', 'password', 'fullname', 'avatar', 'email', 'token', 'fcm', 'device_id']
fields = ['id', 'phone_number', 'password', 'fullname', 'avatar', 'email', 'token', 'fcm', 'device_id', 'timezone']
def get_token(self, obj): def get_token(self, obj):
token, created = Token.objects.get_or_create(user=obj) token, created = Token.objects.get_or_create(user=obj)
@ -159,3 +160,22 @@ class UserResetPasswordSerializer(serializers.ModelSerializer):
return data return data
class UserGuestSerializer(serializers.ModelSerializer):
lat = serializers.CharField(max_length=255, allow_null=True, allow_blank=True, required=False)
lon = serializers.CharField(max_length=255, allow_null=True, allow_blank=True, required=False)
fcm = serializers.CharField(required=False)
device_id = serializers.CharField(required=False)
device_os = serializers.ChoiceField(choices=User.DeviceOs.choices, required=False)
timezone = serializers.CharField(required=False, allow_null=True, allow_blank=True)
class Meta:
model = User
fields = ['device_id', 'fcm', 'device_os', 'lat', 'lon', 'timezone']
def validate(self, data):
# Make sure at least device_id is provided
if not data.get('device_id'):
raise serializers.ValidationError({"device_id": "Device ID is required for guest users."})
return data

1
apps/account/urls.py

@ -13,6 +13,7 @@ urlpatterns = [
path('register/', views.UserRegisterView.as_view(), name='user-register'), path('register/', views.UserRegisterView.as_view(), name='user-register'),
path('verify/', views.UserVerifyView.as_view(), name='user-verify'), path('verify/', views.UserVerifyView.as_view(), name='user-verify'),
path('login/', views.UserLoginView.as_view(), name='user-login'), path('login/', views.UserLoginView.as_view(), name='user-login'),
path('guest/', views.UserGuestView.as_view(), name='user-guest'),
# path('notif/', views.NotificationListView.as_view(), name='user-notif'), # path('notif/', views.NotificationListView.as_view(), name='user-notif'),

52
apps/account/views/notification.py

@ -17,7 +17,17 @@ class NotificationListView(generics.ListAPIView):
@swagger_auto_schema( @swagger_auto_schema(
operation_description="Retrieve a list of notifications for the authenticated user or merchant account.", operation_description="Retrieve a list of notifications for the authenticated user or merchant account.",
tags=['Notifications']
tags=['Notifications'],
manual_parameters=[
openapi.Parameter(
'service',
openapi.IN_QUERY,
description="Filter notifications by service (imam-javad or doboodi)",
type=openapi.TYPE_STRING,
enum=['imam-javad', 'doboodi'],
required=False
)
]
) )
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
""" """
@ -27,14 +37,23 @@ class NotificationListView(generics.ListAPIView):
- **Method**: GET - **Method**: GET
- **URL**: /api/notifications/ - **URL**: /api/notifications/
- **Response**: Includes details of notifications such as title, message, is read status, creation date, and update date.
- **Query Parameters**:
- `service`: Optional. Filter notifications by service ('imam-javad' or 'doboodi')
- **Response**: Includes details of notifications such as title, message, is read status, service, creation date, and update date.
- **Headers**: `Authorization: Bearer <token>` for authentication. - **Headers**: `Authorization: Bearer <token>` for authentication.
""" """
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
def get_queryset(self): def get_queryset(self):
user = self.request.user user = self.request.user
return Notification.objects.filter(user=user).order_by('-created_at')
queryset = Notification.objects.filter(user=user)
# Filter by service if provided in query params
service = self.request.query_params.get('service', None)
if service:
queryset = queryset.filter(service=service)
return queryset.order_by('-created_at')
class NotificationReadAllView(generics.GenericAPIView): class NotificationReadAllView(generics.GenericAPIView):
@ -45,18 +64,41 @@ class NotificationReadAllView(generics.GenericAPIView):
@swagger_auto_schema( @swagger_auto_schema(
operation_description="Mark all notifications as read for the authenticated user or merchant account.", operation_description="Mark all notifications as read for the authenticated user or merchant account.",
tags=['Notifications'], tags=['Notifications'],
manual_parameters=[
openapi.Parameter(
'service',
openapi.IN_QUERY,
description="Filter notifications to mark as read by service (imam-javad or doboodi)",
type=openapi.TYPE_STRING,
enum=['imam-javad', 'doboodi'],
required=False
)
],
responses={ responses={
200: "All notifications marked as read", 200: "All notifications marked as read",
403: "Forbidden",
} }
) )
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
user = request.user user = request.user
service = request.query_params.get('service', None)
# Get base queryset for user's notifications
notifications = Notification.objects.filter(user=user) notifications = Notification.objects.filter(user=user)
# Apply service filtering based on query parameter
if service == 'doboodi':
# If service is doboodi, only mark doboodi notifications as read
notifications = notifications.filter(service=Notification.ServiceChoices.DOBOODI)
status_message = 'all doboodi notifications marked as read'
else:
# Default: mark all imam-javad notifications as read (exclude doboodi)
notifications = notifications.exclude(service=Notification.ServiceChoices.DOBOODI)
status_message = 'all imam-javad notifications marked as read'
# Update the filtered notifications
notifications.update(is_read=True) notifications.update(is_read=True)
return Response({'status': 'all notifications marked as read'}, status=status.HTTP_200_OK)
return Response({'status': status_message}, status=status.HTTP_200_OK)

95
apps/account/views/user.py

@ -5,6 +5,7 @@ from rest_framework.generics import CreateAPIView, RetrieveUpdateAPIView, Generi
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from django.db.models import Q
from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from rest_framework.exceptions import AuthenticationFailed from rest_framework.exceptions import AuthenticationFailed
@ -22,7 +23,7 @@ from rest_framework.exceptions import ValidationError
from utils.exceptions import InvaliedCodeVrify, ExpiredCodeException, ServiceUnavailableException from utils.exceptions import InvaliedCodeVrify, ExpiredCodeException, ServiceUnavailableException
from apps.account.models import User from apps.account.models import User
from apps.account.serializers import UserRegisterSerializer, UserProfileSerializer, UserVerifySerializer, UserLoginSerializer, UserRecoverPasswordSerializer, UserResetPasswordSerializer
from apps.account.serializers import UserRegisterSerializer, UserProfileSerializer, UserVerifySerializer, UserLoginSerializer, UserRecoverPasswordSerializer, UserResetPasswordSerializer, UserGuestSerializer
from utils.redis import RedisManager from utils.redis import RedisManager
from utils.exceptions import AppAPIException from utils.exceptions import AppAPIException
from utils import send_email, is_valid_email from utils import send_email, is_valid_email
@ -34,6 +35,71 @@ logger = logging.getLogger(__name__)
class UserGuestView(CreateAPIView):
permission_classes = [AllowAny]
serializer_class = UserGuestSerializer
@swagger_auto_schema(
operation_description="Create a guest user account with device information",
request_body=UserGuestSerializer,
)
def post(self, request, *args, **kwargs):
logger.info(f'GuestAuthView--> {request.data}')
return super().post(request, *args, **kwargs)
@staticmethod
def generate_login_token(user):
token, created = Token.objects.update_or_create(user=user)
return token.key
def get_client_ip(self):
request = self.request
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = self.perform_create(serializer)
return Response({
'token': self.generate_login_token(user),
}, status=200)
def perform_create(self, serializer):
device_id = serializer.validated_data.get('device_id')
device_os = serializer.validated_data.get('device_os')
fcm = serializer.validated_data.get('fcm')
lat, lon = serializer.validated_data.get('lat'), serializer.validated_data.get('lon')
user_timezone = serializer.validated_data.pop('timezone', None)
serializer_data = dict(serializer.validated_data)
obj = User.objects.select_for_update().filter(Q(device_id=device_id)).first()
if not obj:
obj, created = User.objects.select_for_update().get_or_create(
device_id=device_id,
defaults=serializer_data
)
if created:
logger.info(f'Guest-(created)->: {obj.device_id}')
obj.last_login = timezone.now()
obj.save()
login_history_obj = obj.login_history.create(
lat=lat,
lon=lon,
ip=self.get_client_ip(),
timezone=user_timezone,
)
return obj
class UserRegisterView(CreateAPIView): class UserRegisterView(CreateAPIView):
@ -52,7 +118,7 @@ class UserRegisterView(CreateAPIView):
code = RedisManager.generate_otp_code() code = RedisManager.generate_otp_code()
logger.info(f"phone= {data['email']}") logger.info(f"phone= {data['email']}")
print(f' send {code}/{data["email"]}')
print(f'send {code}/{data["email"]}')
phone_number = RedisManager().add_to_redis(code, **data) phone_number = RedisManager().add_to_redis(code, **data)
send_email([data['email']], code) send_email([data['email']], code)
@ -99,8 +165,8 @@ class UserVerifyView(CreateAPIView):
user = self.perform_create( user = self.perform_create(
email=serializer.data['email'],**verify_data email=serializer.data['email'],**verify_data
) )
Token.objects.filter(user=user).delete()
token = Token.objects.create(user=user)
# Token.objects.filter(user=user).delete()
token = Token.objects.get_or_create(user=user)
return Response(data={ return Response(data={
'token': str(token), 'token': str(token),
'user_id': user.id, 'user_id': user.id,
@ -112,7 +178,6 @@ class UserVerifyView(CreateAPIView):
def valied_code(self, current_code, save_code): def valied_code(self, current_code, save_code):
if (current_code and save_code) and ( current_code != save_code): if (current_code and save_code) and ( current_code != save_code):
# raise InvaliedCodeVrify()
raise ValidationError({"code": "code notfound"}) raise ValidationError({"code": "code notfound"})
return current_code return current_code
@ -128,7 +193,10 @@ class UserVerifyView(CreateAPIView):
user.set_password(kwargs['password']) user.set_password(kwargs['password'])
user.save() user.save()
else: else:
user = User.objects.create(**kwargs)
user = User.objects.flter(device_id=kwargs['device_id']).first()
if not user:
user = User.objects.create(**kwargs)
user.set_password(kwargs['password']) user.set_password(kwargs['password'])
user.last_login = timezone.now() user.last_login = timezone.now()
user.is_active = True user.is_active = True
@ -148,6 +216,15 @@ class UserLoginView(CreateAPIView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
return super().post(request, *args, **kwargs) return super().post(request, *args, **kwargs)
@staticmethod
def get_client_ip(self):
request = self.request
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
@ -156,6 +233,7 @@ class UserLoginView(CreateAPIView):
user = authenticate(request, username=request.data['email'], password=data['password']) user = authenticate(request, username=request.data['email'], password=data['password'])
if not user: if not user:
raise ValidationError({"email": "Unable to log in with provided credentials."}) raise ValidationError({"email": "Unable to log in with provided credentials."})
user_timezone = serializer.validated_data.pop('timezone', None)
user.last_login = timezone.now() user.last_login = timezone.now()
user.is_active = True user.is_active = True
user.save user.save
@ -163,6 +241,10 @@ class UserLoginView(CreateAPIView):
serializer_data = serializer.data serializer_data = serializer.data
serializer_data['token'] = token.key serializer_data['token'] = token.key
login_history_obj = obj.login_history.create(
ip=self.get_client_ip(),
timezone=user_timezone,
)
return Response({ return Response({
"id": user.id, "id": user.id,
"fullname": user.fullname, "fullname": user.fullname,
@ -173,6 +255,7 @@ class UserLoginView(CreateAPIView):
}, status=status.HTTP_201_CREATED) }, status=status.HTTP_201_CREATED)
class UserProfileView(RetrieveAPIView): class UserProfileView(RetrieveAPIView):
serializer_class = UserProfileSerializer serializer_class = UserProfileSerializer
permission_classes = [IsAuthenticated, IsActiveUser] permission_classes = [IsAuthenticated, IsActiveUser]

Loading…
Cancel
Save