from django.shortcuts import render from rest_framework import generics, status from rest_framework.response import Response from rest_framework.permissions import AllowAny, IsAdminUser, IsAuthenticated from rest_framework.views import APIView from django.contrib.auth import get_user_model from django.contrib.auth.password_validation import validate_password from django.core.exceptions import ValidationError from django.utils import timezone from datetime import timedelta from django.core.mail import send_mail from django.conf import settings from .serializers import UserRegistrationSerializer, UserSerializer, ResetPasswordSerializer, ResetPasswordConfirmSerializer from .models import User, PasswordResetToken import secrets import string User = get_user_model() # Create your views here. class RegisterView(generics.CreateAPIView): """ API endpoint for user registration. create: Register a new user with the following fields: - username (required) - password (required) - password2 (required, must match password) - email (required) - first_name (required) - last_name (required) - role (optional, defaults to 'user') """ permission_classes = (AllowAny,) serializer_class = UserRegistrationSerializer def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) return Response( {"message": "User registered successfully. Please login to continue."}, status=status.HTTP_201_CREATED, headers=headers ) class UserViewSet(generics.ListAPIView): """ API endpoint for viewing and editing users. """ queryset = User.objects.all() serializer_class = UserSerializer permission_classes = [IsAdminUser] class UserDetailView(generics.RetrieveUpdateAPIView): """ API endpoint for retrieving and updating the current user's data. """ serializer_class = UserSerializer permission_classes = [IsAuthenticated] def get_object(self): return self.request.user def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) return Response(serializer.data) class ChangePasswordView(APIView): """ API endpoint for changing user password. """ permission_classes = [IsAuthenticated] def post(self, request): user = request.user old_password = request.data.get('old_password') new_password = request.data.get('new_password') if not old_password or not new_password: return Response( {"detail": "Both old and new password are required."}, status=status.HTTP_400_BAD_REQUEST ) if not user.check_password(old_password): return Response( {"detail": "Current password is incorrect."}, status=status.HTTP_400_BAD_REQUEST ) try: validate_password(new_password, user) except ValidationError as e: return Response( {"detail": list(e.messages)}, status=status.HTTP_400_BAD_REQUEST ) user.set_password(new_password) user.save() return Response( {"detail": "Password successfully updated."}, status=status.HTTP_200_OK ) class ResetPasswordView(generics.GenericAPIView): permission_classes = [AllowAny] serializer_class = ResetPasswordSerializer def post(self, request): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) email = serializer.validated_data['email'] try: user = User.objects.get(email=email) # Create a new reset token token = PasswordResetToken.objects.create( user=user, expires_at=timezone.now() + timedelta(hours=24) ) # Send reset email reset_url = f"{settings.FRONTEND_URL}/reset-password/{token.token}" send_mail( 'Password Reset Request', f'Click the following link to reset your password: {reset_url}', settings.DEFAULT_FROM_EMAIL, [email], fail_silently=False, ) return Response({ 'detail': 'If an account exists with this email, you will receive password reset instructions.' }, status=status.HTTP_200_OK) except User.DoesNotExist: # Don't reveal that the email doesn't exist return Response({ 'detail': 'If an account exists with this email, you will receive password reset instructions.' }, status=status.HTTP_200_OK) class ResetPasswordConfirmView(generics.GenericAPIView): permission_classes = [AllowAny] serializer_class = ResetPasswordConfirmSerializer def post(self, request): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) token = serializer.validated_data['token'] new_password = serializer.validated_data['new_password'] reset_token = PasswordResetToken.objects.get(token=token) user = reset_token.user # Update password user.set_password(new_password) user.save() # Mark token as used reset_token.used = True reset_token.save() return Response({ 'detail': 'Password has been reset successfully.' }, status=status.HTTP_200_OK)