from django.shortcuts import render # Create your views here. from rest_framework.views import APIView from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import authentication, permissions from rest_framework.decorators import api_view, authentication_classes, permission_classes from rest_framework.authtoken.models import Token from rest_framework.permissions import IsAuthenticated from django.contrib.auth import get_user_model from users_app.serializers import * from django.core.mail import EmailMessage # converts html template to a string message for emails from django.template.loader import render_to_string from django.http import HttpResponse from django.conf import settings from main_project import settings from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode from django.utils.encoding import force_str from django.utils.http import urlsafe_base64_decode from django.contrib.auth.tokens import default_token_generator from rest_framework import permissions, status # for saving profilepic from PIL import Image from django.core.files import File from io import BytesIO from django.core.files.uploadedfile import InMemoryUploadedFile from rest_framework.permissions import AllowAny # set up logger import logging import os logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # create file handler and set level to INFO log_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'user_logs.txt') fh = logging.FileHandler(log_file) fh.setLevel(logging.INFO) # create formatter formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') fh.setFormatter(formatter) logger.addHandler(fh) user_obj = get_user_model() # email stuff for sending email notifications EMAIL_ON = False URL = settings.env('DEV_DOMAIN') if settings.env('DEV_MODE') == 'True' else None # save user pfp on signup # SaveUserProfilePicture API view class SaveCustomBasicUser(APIView): permission_classes = [AllowAny] def post(self, request, *args, **kwargs): serializer = CustomUserCreateSerializer(data=request.data) if serializer.is_valid(): profile_pic = request.FILES.get('user_profile_pic', None) if profile_pic: profile_pic.file.seek(0) image = Image.open(BytesIO(profile_pic.read())) if image.mode == 'RGBA': image = image.convert('RGB') pfp_name = serializer.validated_data['username'] + '_pfp' buffer = BytesIO() image.save(buffer, format='JPEG') image_file = InMemoryUploadedFile( buffer, None, pfp_name + '.jpg', 'image/jpeg', buffer.getbuffer().nbytes, None ) # Call create_user method of custom user manager to create user instance user = Users_CustomUserManager().create_user( email=serializer.validated_data['email'], username=serializer.validated_data['username'], password=serializer.validated_data['password'], user_profile_pic=image_file, user_first_name=serializer.validated_data['user_first_name'], user_last_name=serializer.validated_data['user_last_name'], user_favorite_bible_verse=serializer.validated_data['user_favorite_bible_verse'] ) return Response(status=status.HTTP_200_OK) else: # save user without profile pic user = Users_CustomUserManager().create_user( email=serializer.validated_data['email'], username=serializer.validated_data['username'], password=serializer.validated_data['password'], user_profile_pic=image_file, user_first_name=serializer.validated_data['user_first_name'], user_last_name=serializer.validated_data['user_last_name'], user_favorite_bible_verse=serializer.validated_data['user_favorite_bible_verse'] ) return Response(status=status.HTTP_200_OK) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # get user pfp on login @api_view(['GET']) @authentication_classes([authentication.TokenAuthentication]) @permission_classes([permissions.IsAuthenticated]) def get_user_pfp(request): current_user = user_obj.objects.get(pk=request.user.pk) user_pfp_serializer = GetUserPfpSerializer(current_user) response = Response(user_pfp_serializer.data, status=status.HTTP_200_OK) # added this to prevent vuejs from caching cart data response['Cache-Control'] = 'no-cache, no-store, must-revalidate' response['Expires'] = '0' return response # delete user account @api_view(['POST']) @authentication_classes([authentication.TokenAuthentication]) @permission_classes([permissions.IsAuthenticated]) def delete_user_account_data(request): # use first to prevent exception from being raised try: user_to_be_deleted = user_obj.objects.get(pk=request.user.pk) user_to_be_deleted.delete() # return no content, everything worked return Response(status=status.HTTP_204_NO_CONTENT) except: return Response({'error': 'Invalid token or user'}, status=status.HTTP_400_BAD_REQUEST) # update user account info @api_view(['POST']) @authentication_classes([authentication.TokenAuthentication]) @permission_classes([permissions.IsAuthenticated]) def update_user_account_data(request): # get the user current_user = user_obj.objects.get(pk=request.user.pk) # Deserialize incoming data # serializer = UpdateUserAccountDataSerializer(data=request.data) serializer = UpdateUserAccountDataSerializer(data=request.data, context={'request': request}) if serializer.is_valid(): # get the img file profile_pic = request.FILES.get('user_profile_pic', None) if profile_pic: profile_pic.file.seek(0) # Open the uploaded image file image = Image.open(BytesIO(profile_pic.read())) # Convert RGBA to RGB mode if it exists if image.mode == 'RGBA': image = image.convert('RGB') pfp_name = current_user.username + '_pfp' buffer = BytesIO() image.save(buffer, format='JPEG') image_file = InMemoryUploadedFile( buffer, None, pfp_name + '.jpg', 'image/jpeg', buffer.getbuffer().nbytes, None ) current_user.user_profile_pic.save(pfp_name + '.jpg', image_file) else: print('\n\n Image does not exist') # save the rest of the text fields # Update user fields # validated_data is a dictionary that holds validated data ready to be saved to db current_user.username = serializer.validated_data.get('username', current_user.username) current_user.email = serializer.validated_data.get('email', current_user.email) current_user.user_first_name = serializer.validated_data.get('user_first_name', current_user.first_name) current_user.user_last_name = serializer.validated_data.get('user_last_name', current_user.last_name) current_user.user_favorite_bible_verse = serializer.validated_data.get('user_favorite_bible_verse', current_user.favorite_color) current_user.save() return Response(status=status.HTTP_200_OK) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # get user account data @api_view(['GET']) @authentication_classes([authentication.TokenAuthentication]) @permission_classes([permissions.IsAuthenticated]) def get_user_account_data(request): current_user = user.objects.get(pk=request.user.pk) user_serializer = GetUserSerializer(current_user) response = Response(user_serializer.data, status=status.HTTP_200_OK) # added this to prevent frontend from caching cart data response['Cache-Control'] = 'no-cache, no-store, must-revalidate' response['Expires'] = '0' return response ''' # reset password @api_view(['POST']) @permission_classes([permissions.AllowAny]) def reset_password(request): try: uid = force_str(urlsafe_base64_decode(request.data.get('uidb64'))) current_user = user.objects.get(pk=uid) except (TypeError, ValueError, OverflowError, user.DoesNotExist): current_user = None # Check if the token is valid if current_user is not None and default_token_generator.check_token(current_user, request.data.get('token')): # Set the new password for the user password = request.data.get('password') current_user.set_password(password) current_user.save() template = render_to_string('../templates/changed_account_notice_email.html', {'name':current_user.first_name}) email = EmailMessage( # email subject title default is 'subject' 'There was a change to your account -- アカウント情報変更のお知らせ', # email template default is 'body' template, settings.EMAIL_HOST_USER, # recipient list [current_user.email], ) email.fail_silently=False # eonly send email if this flag is true if EMAIL_ON: email.send() return Response({'success': 'Password reset successful'}, status=status.HTTP_200_OK) else: return Response({'error': 'Invalid token or user'}, status=status.HTTP_400_BAD_REQUEST) # send password reset link @api_view(['POST']) @permission_classes([permissions.AllowAny]) def send_password_reset_link(request): # get the email address from the POST request email = request.data.get('potential_email_address') # check if the email address is valid try: get_user = user.objects.get(email=email) # creating a password reset url unique for each user token_generator = PasswordResetTokenGenerator() uidb64 = urlsafe_base64_encode(force_bytes(get_user.pk)) token = token_generator.make_token(get_user) # create the password reset URL using the generated token password_reset_url = f'{URL}{request.data.get("password_reset_url")}/{uidb64}/{token}/' EMAIL_ON = True template = render_to_string('../templates/password-reset-email.html', {'name':get_user.first_name, 'password_reset_url': password_reset_url}) email = EmailMessage( # email subject title default is 'subject' 'Password reset -- パスワードのリセット', # email template default is 'body' template, settings.EMAIL_HOST_USER, # recipient list [get_user.email], ) email.fail_silently=False # only send email if this flag is true if EMAIL_ON: email.send() # just return a 200 response return HttpResponse(status=200) except user.DoesNotExist: # handle the case where the user does not exist return Response({'error': 'User does not exist'}, status=200) except Exception as e: return Response({'error': 'Unknown error occurred'}, status=500) ''' # get user data @api_view(['GET']) def get_user_device(request): user_agent = request.META.get('HTTP_USER_AGENT', None) # do something with usr's device return Response({'message': 'success'}) # checking username in form validation # need to include the request or else there will be an error @api_view(['GET']) def check_username(request, username): username_available = not user.objects.filter(username=username).exists() response = Response({'available': username_available}) # added this to prevent vuejs from caching cart data response['Cache-Control'] = 'no-cache, no-store, must-revalidate' response['Expires'] = '0' return response # checking username in form validation @api_view(['GET']) def check_email(request, email): email_available = not user.objects.filter(email=email).exists() response = Response({'available': email_available}) # added this to prevent vuejs from caching cart data response['Cache-Control'] = 'no-cache, no-store, must-revalidate' response['Expires'] = '0' return response # de-authenticate user by deleting auth token, and storing/updating and then saving the user's cart data class LogoutView(APIView): permission_classes = (IsAuthenticated,) def post(self, request, format=None): Token.objects.filter(user=user).delete() return Response({'success': 'Logged out successfully.'})