From b281ad906c997671aba521603778b92591f5d8d5 Mon Sep 17 00:00:00 2001 From: FarmerJohnsBessie Date: Sun, 14 Jul 2024 21:36:40 -0400 Subject: [PATCH] Added score to room matches --- api/adapters.py | 12 +++++++++ api/serializers.py | 40 +++++++++++++++++++++++++++--- api/urls.py | 3 ++- api/user_views.py | 59 +++++++++++++++++++++++++++++++++++++------- requirements.txt | Bin 1178 -> 1328 bytes satduel/settings.py | 39 ++++++++++++++++++++++++++++- satduel/urls.py | 5 +++- 7 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 api/adapters.py diff --git a/api/adapters.py b/api/adapters.py new file mode 100644 index 0000000..000fcfc --- /dev/null +++ b/api/adapters.py @@ -0,0 +1,12 @@ +from allauth.account.adapter import DefaultAccountAdapter +from django.conf import settings + +class CustomAccountAdapter(DefaultAccountAdapter): + def send_confirmation_mail(self, request, emailconfirmation, signup): + activate_url = f"{settings.FRONTEND_URL}/confirm-email/{emailconfirmation.key}/" + ctx = { + "user": emailconfirmation.email_address.user, + "activate_url": activate_url, + "key": emailconfirmation.key, + } + self.send_mail("account/email/email_confirmation", emailconfirmation.email_address.email, ctx) diff --git a/api/serializers.py b/api/serializers.py index 297fb18..6cfd5c5 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -1,7 +1,9 @@ from django.contrib.auth.models import User -from rest_framework import serializers from api.models import Question, Profile, Room, TrackedQuestion, FriendRequest - +from rest_framework import serializers +from allauth.account.adapter import get_adapter +from allauth.account.utils import setup_user_email +from dj_rest_auth.registration.serializers import RegisterSerializer class QuestionSerializer(serializers.ModelSerializer): choices = serializers.SerializerMethodField() @@ -60,4 +62,36 @@ class FriendRequestSerializer(serializers.ModelSerializer): class Meta: model = FriendRequest - fields = ['id', 'from_user', 'to_user', 'timestamp', 'status'] \ No newline at end of file + fields = ['id', 'from_user', 'to_user', 'timestamp', 'status'] + +class CustomRegisterSerializer(RegisterSerializer): + first_name = serializers.CharField(required=True, write_only=True) + last_name = serializers.CharField(required=True, write_only=True) + grade = serializers.IntegerField(required=True, write_only=True) + + def get_cleaned_data(self): + data_dict = super().get_cleaned_data() + data_dict['first_name'] = self.validated_data.get('first_name', '') + data_dict['last_name'] = self.validated_data.get('last_name', '') + data_dict['grade'] = self.validated_data.get('grade') + print("Cleaned data:", data_dict) + return data_dict + + def save(self, request): + adapter = get_adapter() + user = adapter.new_user(request) + self.cleaned_data = self.get_cleaned_data() + adapter.save_user(request, user, self) + setup_user_email(request, user, []) + user.save() + profile = Profile.objects.create( + user=user, + biography='This user is lazy, he did not write anything yet', + grade=self.cleaned_data.get('grade') + ) + print("Created profile:", profile) + return user + + def validate(self, data): + print("Received data in validate:", data) + return super().validate(data) \ No newline at end of file diff --git a/api/urls.py b/api/urls.py index c456329..cec80cb 100644 --- a/api/urls.py +++ b/api/urls.py @@ -3,6 +3,7 @@ from . import views from . import user_views +from .user_views import CustomRegisterView urlpatterns = [ path('questions/', views.get_random_questions, name='get_random_questions'), @@ -21,7 +22,7 @@ path('login/', user_views.login_view, name='login'), path('logout/', user_views.logout_view, name='logout'), - path('register/', user_views.register, name='register'), + path('register/', CustomRegisterView.as_view(), name='register'), path('match/', views.match, name='match'), path('match/questions/', views.get_match_questions, name='get_match_questions'), diff --git a/api/user_views.py b/api/user_views.py index 70dd4af..05a6aa9 100644 --- a/api/user_views.py +++ b/api/user_views.py @@ -1,13 +1,22 @@ import json +from dj_rest_auth.registration.views import RegisterView from django.contrib.auth.models import User from django.http import JsonResponse +from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt from django.contrib.auth import authenticate, login, logout from django.views.decorators.http import require_POST +from rest_framework import status +from rest_framework.decorators import api_view +from rest_framework.response import Response from api.models import Profile +from allauth.account.models import EmailAddress + +from api.serializers import CustomRegisterSerializer + @csrf_exempt def login_view(request): @@ -17,17 +26,21 @@ def login_view(request): password = data.get('password') user = authenticate(request, username=username, password=password) if user is not None: - login(request, user) - return JsonResponse({ - 'message': 'Logged In Successfully', - 'username': user.username, - 'email': user.email, - 'id': user.id, - }, status=200) + if EmailAddress.objects.filter(user=user, verified=True).exists(): + login(request, user) + return JsonResponse({ + 'message': 'Logged In Successfully', + 'username': user.username, + 'email': user.email, + 'id': user.id, + }, status=200) + else: + return JsonResponse({'error': 'Please verify your email address before logging in'}, status=401) else: return JsonResponse({'error': 'Invalid credentials'}, status=401) return JsonResponse({'error': 'Only POST method is allowed'}, status=405) + @require_POST # Ensures that this view can only be accessed via POST request @csrf_exempt # Disables CSRF protection for this view def logout_view(request): @@ -62,7 +75,7 @@ def register(request): Profile.objects.create( user=user, biography='This user is lazy, he did not write anything yet', - grade= grade + grade=grade ) if user: login(request, user) @@ -75,4 +88,32 @@ def register(request): else: return JsonResponse({'error': 'Registration successful but login failed'}, status=400) else: - return JsonResponse({'error': 'Invalid HTTP method'}, status=405) \ No newline at end of file + return JsonResponse({'error': 'Invalid HTTP method'}, status=405) + + + + +@method_decorator(csrf_exempt, name='dispatch') +class CustomRegisterView(RegisterView): + serializer_class = CustomRegisterSerializer + + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + if serializer.is_valid(): + user = serializer.save(request) + email_address = EmailAddress.objects.filter(user=user).first() + response_data = { + 'id': user.id, + 'username': user.username, + 'email': user.email, + 'first_name': user.first_name, + 'last_name': user.last_name, + 'is_active': user.is_active, + 'email_verified': email_address.verified if email_address else False + } + return Response(response_data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def perform_create(self, serializer): + user = serializer.save(self.request) + return user \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index ae2f77d32717ffd82c010f864c00469d4f21a356..75dcc66ab31120dc45a488e6c9bf19130c7632a5 100644 GIT binary patch delta 107 zcmbQmxq)i~3!`ERLl%QBLlHwNLoq`Mkd??#3Zyd_Y=O{>L65;;;%`&gM1~w7M3yoD jN|`Yj14+}#vW%YGNJdP~WfW%y%1=JcXt=q7QHmJ=o%a>K delta 20 ccmdnMHH&it3*%%xMxDuBjAonvFq$v}07Fg&JOBUy diff --git a/satduel/settings.py b/satduel/settings.py index 6ef8aa4..c70a842 100644 --- a/satduel/settings.py +++ b/satduel/settings.py @@ -43,8 +43,15 @@ 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', + 'rest_framework.authtoken', 'corsheaders', 'api', + 'django.contrib.sites', + 'allauth', + 'allauth.account', + 'dj_rest_auth', + 'dj_rest_auth.registration', + 'allauth.socialaccount', ] MIDDLEWARE = [ @@ -57,8 +64,15 @@ 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'corsheaders.middleware.CorsMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', + 'allauth.account.middleware.AccountMiddleware', ] +AUTHENTICATION_BACKENDS = [ + 'django.contrib.auth.backends.ModelBackend', + 'allauth.account.auth_backends.AuthenticationBackend', +] + + CORS_ALLOW_ALL_ORIGINS = True ROOT_URLCONF = 'satduel.urls' @@ -114,6 +128,10 @@ ), } +REST_AUTH_REGISTER_SERIALIZERS = { + 'REGISTER_SERIALIZER': 'api.serializers.CustomRegisterSerializer', +} + # Internationalization # https://docs.djangoproject.com/en/5.0/topics/i18n/ @@ -138,4 +156,23 @@ # Default primary key field type # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' \ No newline at end of file +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + + +SITE_ID = 1 + +REST_USE_JWT = True +ACCOUNT_EMAIL_VERIFICATION = 'mandatory' +ACCOUNT_EMAIL_REQUIRED = True +ACCOUNT_AUTHENTICATION_METHOD = 'email' + +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST = os.getenv('EMAIL_HOST') +EMAIL_PORT = int(os.getenv('EMAIL_PORT', 587)) # Default to 587 if not set +EMAIL_USE_TLS = os.getenv('EMAIL_USE_TLS', 'True') == 'True' # Convert string to boolean +EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER') +EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD') +DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL') + +ACCOUNT_ADAPTER = 'api.adapters.CustomAccountAdapter' # Replace 'your_app' with the actual app name +FRONTEND_URL = 'http://localhost:3000' # Replace with your frontend URL diff --git a/satduel/urls.py b/satduel/urls.py index 850742a..e55d3d2 100644 --- a/satduel/urls.py +++ b/satduel/urls.py @@ -19,5 +19,8 @@ urlpatterns = [ path('admin/', admin.site.urls), - path('api/', include('api.urls')) + path('api/', include('api.urls')), + path('auth/', include('dj_rest_auth.urls')), + path('auth/registration/', include('dj_rest_auth.registration.urls')), + path('accounts/', include('allauth.urls')), # allauth urls ]