fix: validate existing phone/username upfront and prevent duplicate registration OTP dispatch
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 36s
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 36s
This commit is contained in:
parent
1252fb12b2
commit
414fbffbcf
2 changed files with 69 additions and 31 deletions
|
|
@ -205,23 +205,18 @@ class RegisterSerializer(serializers.Serializer):
|
|||
if phone_search:
|
||||
q |= Q(username=f"user_{phone_search}") | Q(username=phone_search)
|
||||
|
||||
existing_user = User.objects.filter(q).first()
|
||||
if existing_user:
|
||||
existing_users = User.objects.filter(q)
|
||||
for existing_user in existing_users:
|
||||
if SupplierProfile.objects.filter(user=existing_user).exists() or SupplierOnboardingDraft.objects.filter(user=existing_user).exists():
|
||||
raise serializers.ValidationError("Account already registered with this username/mobile. Please log in.")
|
||||
else:
|
||||
existing_user.delete()
|
||||
return value
|
||||
|
||||
def validate_email(self, value):
|
||||
if not value:
|
||||
return value
|
||||
existing_user = User.objects.filter(email__iexact=value).first()
|
||||
if existing_user:
|
||||
if not SupplierProfile.objects.filter(user=existing_user).exists():
|
||||
existing_user.delete()
|
||||
else:
|
||||
raise serializers.ValidationError("Email already registered.")
|
||||
if existing_user and (SupplierProfile.objects.filter(user=existing_user).exists() or SupplierOnboardingDraft.objects.filter(user=existing_user).exists()):
|
||||
raise serializers.ValidationError("Email already registered. Please log in.")
|
||||
return value
|
||||
|
||||
def validate_phone(self, value):
|
||||
|
|
@ -233,32 +228,28 @@ class RegisterSerializer(serializers.Serializer):
|
|||
# Check existing profile (phone, support_phone, or user username)
|
||||
existing_profile = SupplierProfile.objects.filter(
|
||||
Q(phone__icontains=phone_search) |
|
||||
Q(support_phone__icontains=phone_search) |
|
||||
Q(support_phone__icontains=phone_search) |
|
||||
Q(user__username__icontains=phone_search)
|
||||
).first()
|
||||
if existing_profile:
|
||||
raise serializers.ValidationError("Mobile number is already registered. Please log in instead.")
|
||||
|
||||
# Check existing user
|
||||
existing_user = User.objects.filter(
|
||||
Q(username__icontains=phone_search) | Q(username=phone_search)
|
||||
).first()
|
||||
if existing_user:
|
||||
if SupplierProfile.objects.filter(user=existing_user).exists():
|
||||
# Check existing users matching phone
|
||||
matching_users = User.objects.filter(
|
||||
Q(username__icontains=phone_search) | Q(username=phone_search) | Q(username=f"user_{phone_search}")
|
||||
)
|
||||
for u in matching_users:
|
||||
if SupplierProfile.objects.filter(user=u).exists():
|
||||
raise serializers.ValidationError("Mobile number is already registered. Please log in instead.")
|
||||
draft = SupplierOnboardingDraft.objects.filter(user=existing_user).first()
|
||||
if draft:
|
||||
if SupplierOnboardingDraft.objects.filter(user=u).exists():
|
||||
raise serializers.ValidationError("An account with this mobile number already exists. Please log in to continue.")
|
||||
|
||||
existing_draft = SupplierOnboardingDraft.objects.filter(phone__icontains=phone_search).first()
|
||||
existing_draft = SupplierOnboardingDraft.objects.filter(
|
||||
Q(phone__icontains=phone_search) | Q(user__username__icontains=phone_search)
|
||||
).first()
|
||||
if existing_draft:
|
||||
raise serializers.ValidationError("An account with this mobile number already exists. Please log in to continue.")
|
||||
|
||||
# Clean up any orphaned auth_user record with this username/phone if no profile or draft exists
|
||||
orphans = User.objects.filter(username__icontains=phone_search)
|
||||
for orphan in orphans:
|
||||
if not SupplierProfile.objects.filter(user=orphan).exists() and not SupplierOnboardingDraft.objects.filter(user=orphan).exists():
|
||||
orphan.delete()
|
||||
return value
|
||||
|
||||
def validate_password(self, value):
|
||||
|
|
@ -271,6 +262,31 @@ class RegisterSerializer(serializers.Serializer):
|
|||
digits = ''.join(c for c in str(phone) if c.isdigit())
|
||||
clean_phone = digits[-10:] if len(digits) >= 10 else digits
|
||||
|
||||
if clean_phone:
|
||||
if SupplierProfile.objects.filter(
|
||||
Q(phone__icontains=clean_phone) |
|
||||
Q(support_phone__icontains=clean_phone) |
|
||||
Q(user__username__icontains=clean_phone)
|
||||
).exists():
|
||||
raise serializers.ValidationError({"phone": "Mobile number is already registered. Please log in instead."})
|
||||
|
||||
if SupplierOnboardingDraft.objects.filter(
|
||||
Q(phone__icontains=clean_phone) |
|
||||
Q(user__username__icontains=clean_phone)
|
||||
).exists():
|
||||
raise serializers.ValidationError({"phone": "An account with this mobile number already exists. Please log in to continue."})
|
||||
|
||||
matching_users = User.objects.filter(
|
||||
Q(username=clean_phone) |
|
||||
Q(username=f"user_{clean_phone}") |
|
||||
Q(username__icontains=clean_phone)
|
||||
)
|
||||
for u in matching_users:
|
||||
if SupplierProfile.objects.filter(user=u).exists():
|
||||
raise serializers.ValidationError({"phone": "Mobile number is already registered. Please log in instead."})
|
||||
if SupplierOnboardingDraft.objects.filter(user=u).exists():
|
||||
raise serializers.ValidationError({"phone": "An account with this mobile number already exists. Please log in to continue."})
|
||||
|
||||
if not data.get('email') or data.get('email').endswith('@tradhox.com'):
|
||||
data['email'] = ""
|
||||
if not data.get('password'):
|
||||
|
|
@ -290,13 +306,20 @@ class RegisterSerializer(serializers.Serializer):
|
|||
email_val = validated_data['email']
|
||||
phone_val = validated_data['phone']
|
||||
|
||||
# Purge leftover orphan user records matching username or email without profile
|
||||
# Purge leftover orphan user records matching username or email without profile or draft
|
||||
# Use raw SQL to avoid multi-database cross-relation CASCADE delete issues
|
||||
from django.db import connection
|
||||
for u in User.objects.filter(username__iexact=username_val):
|
||||
if not SupplierProfile.objects.filter(user=u).exists():
|
||||
u.delete()
|
||||
for u in User.objects.filter(email__iexact=email_val):
|
||||
if not SupplierProfile.objects.filter(user=u).exists():
|
||||
u.delete()
|
||||
if not SupplierProfile.objects.filter(user=u).exists() and not SupplierOnboardingDraft.objects.filter(user=u).exists():
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("DELETE FROM api_otprecord WHERE user_id = %s", [u.id])
|
||||
cursor.execute("DELETE FROM auth_user WHERE id = %s", [u.id])
|
||||
if email_val:
|
||||
for u in User.objects.filter(email__iexact=email_val):
|
||||
if not SupplierProfile.objects.filter(user=u).exists() and not SupplierOnboardingDraft.objects.filter(user=u).exists():
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("DELETE FROM api_otprecord WHERE user_id = %s", [u.id])
|
||||
cursor.execute("DELETE FROM auth_user WHERE id = %s", [u.id])
|
||||
|
||||
try:
|
||||
user = User.objects.create_user(
|
||||
|
|
|
|||
17
api/views.py
17
api/views.py
|
|
@ -16,7 +16,7 @@ from django.utils import timezone
|
|||
from django.contrib.auth import authenticate
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.text import slugify
|
||||
from rest_framework import viewsets, status, generics
|
||||
from rest_framework import viewsets, status, generics, serializers
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.decorators import action as drf_action
|
||||
|
|
@ -326,6 +326,21 @@ class SendWhatsAppOtpView(APIView):
|
|||
else:
|
||||
clean_number = digits
|
||||
|
||||
# If requesting registration OTP, ensure phone is not already registered
|
||||
phone_search = digits[-10:] if len(digits) >= 10 else digits
|
||||
if template_name == 'mobile_number_verification' and phone_search:
|
||||
is_already_registered = SupplierProfile.objects.filter(
|
||||
Q(phone__icontains=phone_search) |
|
||||
Q(support_phone__icontains=phone_search) |
|
||||
Q(user__username__icontains=phone_search)
|
||||
).exists()
|
||||
if is_already_registered:
|
||||
return Response({
|
||||
'error': 'Mobile number is already registered. Please log in instead.',
|
||||
'detail': 'Mobile number is already registered. Please log in instead.',
|
||||
'is_registered': True
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
user = request.user if (request.user and request.user.is_authenticated) else None
|
||||
if not user and digits:
|
||||
user = resolve_user_by_identifier(digits)
|
||||
|
|
|
|||
Loading…
Reference in a new issue