from rest_framework import serializers from django.contrib.auth import authenticate from django.contrib.auth.password_validation import validate_password from django.core.exceptions import ValidationError from .models import User, UserProfile, LoginAttempt, EmailVerification import uuid from datetime import timedelta from django.utils import timezone class UserRegistrationSerializer(serializers.ModelSerializer): """Serializer for user registration.""" password = serializers.CharField(write_only=True, validators=[validate_password]) password_confirm = serializers.CharField(write_only=True) class Meta: model = User fields = ('email', 'username', 'first_name', 'last_name', 'password', 'password_confirm') extra_kwargs = { 'email': {'required': True}, 'username': {'required': True}, 'first_name': {'required': True}, 'last_name': {'required': True}, } def validate(self, attrs): if attrs['password'] != attrs['password_confirm']: raise serializers.ValidationError("Passwords don't match.") return attrs def validate_email(self, value): if User.objects.filter(email=value).exists(): raise serializers.ValidationError("A user with this email already exists.") return value def validate_username(self, value): if User.objects.filter(username=value).exists(): raise serializers.ValidationError("A user with this username already exists.") return value def create(self, validated_data): validated_data.pop('password_confirm') user = User.objects.create_user(**validated_data) # Create user profile UserProfile.objects.create(user=user) # Create email verification token token = str(uuid.uuid4()) EmailVerification.objects.create( user=user, token=token, expires_at=timezone.now() + timedelta(hours=24) ) return user class UserLoginSerializer(serializers.Serializer): """Serializer for user login.""" email = serializers.EmailField() password = serializers.CharField() def validate(self, attrs): email = attrs.get('email') password = attrs.get('password') if email and password: user = authenticate(username=email, password=password) if not user: raise serializers.ValidationError('Invalid email or password.') if not user.is_active: raise serializers.ValidationError('User account is disabled.') attrs['user'] = user else: raise serializers.ValidationError('Must include email and password.') return attrs class UserSerializer(serializers.ModelSerializer): """Serializer for user data.""" full_name = serializers.CharField(source='get_full_name', read_only=True) profile = serializers.SerializerMethodField() class Meta: model = User fields = ( 'id', 'email', 'username', 'first_name', 'last_name', 'full_name', 'avatar', 'is_verified', 'created_at', 'updated_at', 'profile' ) read_only_fields = ('id', 'created_at', 'updated_at', 'is_verified') def get_profile(self, obj): try: profile = obj.profile return UserProfileSerializer(profile).data except UserProfile.DoesNotExist: return None class UserProfileSerializer(serializers.ModelSerializer): """Serializer for user profile.""" class Meta: model = UserProfile fields = '__all__' read_only_fields = ('user', 'created_at', 'updated_at') class EmailSettingsSerializer(serializers.ModelSerializer): """Serializer for email server settings.""" smtp_password = serializers.CharField(write_only=True, required=False) imap_password = serializers.CharField(write_only=True, required=False) class Meta: model = User fields = ( 'smtp_host', 'smtp_port', 'smtp_username', 'smtp_password', 'smtp_use_tls', 'imap_host', 'imap_port', 'imap_username', 'imap_password', 'imap_use_ssl' ) def update(self, instance, validated_data): smtp_password = validated_data.pop('smtp_password', None) imap_password = validated_data.pop('imap_password', None) if smtp_password: instance.set_smtp_password(smtp_password) if imap_password: instance.set_imap_password(imap_password) for attr, value in validated_data.items(): setattr(instance, attr, value) instance.save() return instance class PasswordChangeSerializer(serializers.Serializer): """Serializer for password change.""" old_password = serializers.CharField() new_password = serializers.CharField(validators=[validate_password]) new_password_confirm = serializers.CharField() def validate_old_password(self, value): user = self.context['request'].user if not user.check_password(value): raise serializers.ValidationError('Old password is incorrect.') return value def validate(self, attrs): if attrs['new_password'] != attrs['new_password_confirm']: raise serializers.ValidationError("New passwords don't match.") return attrs def save(self): user = self.context['request'].user user.set_password(self.validated_data['new_password']) user.save() return user class EmailVerificationSerializer(serializers.Serializer): """Serializer for email verification.""" token = serializers.CharField() def validate_token(self, value): try: verification = EmailVerification.objects.get(token=value) if verification.is_used: raise serializers.ValidationError('Verification token has already been used.') if verification.is_expired(): raise serializers.ValidationError('Verification token has expired.') return verification except EmailVerification.DoesNotExist: raise serializers.ValidationError('Invalid verification token.') def save(self): verification = self.validated_data['token'] verification.is_used = True verification.save() user = verification.user user.is_verified = True user.save() return user class LoginAttemptSerializer(serializers.ModelSerializer): """Serializer for login attempts.""" class Meta: model = LoginAttempt fields = '__all__' read_only_fields = ('timestamp',)