from rest_framework import generics, status, permissions, filters from rest_framework.decorators import api_view, permission_classes from rest_framework.response import Response from rest_framework.views import APIView from django_filters.rest_framework import DjangoFilterBackend from django.db.models import Q, Count from django.shortcuts import get_object_or_404 from django.utils import timezone from django_ratelimit.decorators import ratelimit import smtplib import imaplib import email from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.base import MIMEBase from email import encoders import os from .models import ( Email, EmailFolder, EmailAttachment, EmailThread, EmailTemplate, EmailSignature, EmailRule, EmailSearch ) from .serializers import ( EmailSerializer, EmailCreateSerializer, EmailReplySerializer, EmailForwardSerializer, EmailFolderSerializer, EmailAttachmentSerializer, EmailThreadSerializer, EmailTemplateSerializer, EmailSignatureSerializer, EmailRuleSerializer, EmailSearchSerializer, EmailBulkActionSerializer ) from .tasks import send_email_task, fetch_emails_task class EmailFolderListCreateView(generics.ListCreateAPIView): """List and create email folders.""" serializer_class = EmailFolderSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return EmailFolder.objects.filter(user=self.request.user) class EmailFolderDetailView(generics.RetrieveUpdateDestroyAPIView): """Retrieve, update, or delete email folder.""" serializer_class = EmailFolderSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return EmailFolder.objects.filter(user=self.request.user) class EmailListCreateView(generics.ListCreateAPIView): """List and create emails.""" permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] filterset_fields = ['folder', 'is_read', 'is_starred', 'is_important', 'status'] search_fields = ['subject', 'from_email', 'body_text'] ordering_fields = ['created_at', 'sent_at', 'subject'] ordering = ['-created_at'] def get_serializer_class(self): if self.request.method == 'POST': return EmailCreateSerializer return EmailSerializer def get_queryset(self): return Email.objects.filter(user=self.request.user).select_related('folder').prefetch_related('attachments') def perform_create(self, serializer): serializer.save(user=self.request.user) class EmailDetailView(generics.RetrieveUpdateDestroyAPIView): """Retrieve, update, or delete email.""" serializer_class = EmailSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return Email.objects.filter(user=self.request.user).select_related('folder').prefetch_related('attachments') def retrieve(self, request, *args, **kwargs): instance = self.get_object() # Mark as read when retrieved if not instance.is_read: instance.mark_as_read() serializer = self.get_serializer(instance) return Response(serializer.data) class EmailReplyView(generics.CreateAPIView): """Reply to an email.""" serializer_class = EmailReplySerializer permission_classes = [permissions.IsAuthenticated] def perform_create(self, serializer): serializer.save(user=self.request.user) class EmailForwardView(generics.CreateAPIView): """Forward an email.""" serializer_class = EmailForwardSerializer permission_classes = [permissions.IsAuthenticated] def perform_create(self, serializer): serializer.save(user=self.request.user) class EmailBulkActionView(APIView): """Perform bulk actions on emails.""" permission_classes = [permissions.IsAuthenticated] def post(self, request): serializer = EmailBulkActionSerializer(data=request.data, context={'request': request}) if serializer.is_valid(): email_ids = serializer.validated_data['email_ids'] action = serializer.validated_data['action'] folder_id = serializer.validated_data.get('folder_id') emails = Email.objects.filter(id__in=email_ids, user=request.user) if action == 'mark_read': emails.update(is_read=True) elif action == 'mark_unread': emails.update(is_read=False) elif action == 'mark_starred': emails.update(is_starred=True) elif action == 'mark_unstarred': emails.update(is_starred=False) elif action == 'mark_important': emails.update(is_important=True) elif action == 'mark_unimportant': emails.update(is_important=False) elif action == 'move_to_folder': folder = get_object_or_404(EmailFolder, id=folder_id, user=request.user) emails.update(folder=folder) elif action == 'delete': emails.delete() return Response({'message': f'Bulk action {action} completed successfully'}) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class EmailSearchView(generics.ListAPIView): """Search emails with advanced filters.""" serializer_class = EmailSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): queryset = Email.objects.filter(user=self.request.user) # Get search parameters query = self.request.query_params.get('q', '') folder = self.request.query_params.get('folder') is_read = self.request.query_params.get('is_read') is_starred = self.request.query_params.get('is_starred') is_important = self.request.query_params.get('is_important') date_from = self.request.query_params.get('date_from') date_to = self.request.query_params.get('date_to') # Apply filters if query: queryset = queryset.filter( Q(subject__icontains=query) | Q(from_email__icontains=query) | Q(body_text__icontains=query) ) if folder: queryset = queryset.filter(folder_id=folder) if is_read is not None: queryset = queryset.filter(is_read=is_read.lower() == 'true') if is_starred is not None: queryset = queryset.filter(is_starred=is_starred.lower() == 'true') if is_important is not None: queryset = queryset.filter(is_important=is_important.lower() == 'true') if date_from: queryset = queryset.filter(created_at__gte=date_from) if date_to: queryset = queryset.filter(created_at__lte=date_to) return queryset.select_related('folder').prefetch_related('attachments') class EmailThreadView(generics.RetrieveAPIView): """Get email thread.""" serializer_class = EmailThreadSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return EmailThread.objects.filter(emails__user=self.request.user).distinct() class EmailTemplateListCreateView(generics.ListCreateAPIView): """List and create email templates.""" serializer_class = EmailTemplateSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return EmailTemplate.objects.filter( Q(user=self.request.user) | Q(is_public=True) ).order_by('name') def perform_create(self, serializer): serializer.save(user=self.request.user) class EmailTemplateDetailView(generics.RetrieveUpdateDestroyAPIView): """Retrieve, update, or delete email template.""" serializer_class = EmailTemplateSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return EmailTemplate.objects.filter(user=self.request.user) class EmailSignatureListCreateView(generics.ListCreateAPIView): """List and create email signatures.""" serializer_class = EmailSignatureSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return EmailSignature.objects.filter(user=self.request.user).order_by('name') def perform_create(self, serializer): serializer.save(user=self.request.user) class EmailSignatureDetailView(generics.RetrieveUpdateDestroyAPIView): """Retrieve, update, or delete email signature.""" serializer_class = EmailSignatureSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return EmailSignature.objects.filter(user=self.request.user) class EmailRuleListCreateView(generics.ListCreateAPIView): """List and create email rules.""" serializer_class = EmailRuleSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return EmailRule.objects.filter(user=self.request.user).order_by('name') def perform_create(self, serializer): serializer.save(user=self.request.user) class EmailRuleDetailView(generics.RetrieveUpdateDestroyAPIView): """Retrieve, update, or delete email rule.""" serializer_class = EmailRuleSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return EmailRule.objects.filter(user=self.request.user) class EmailSearchListCreateView(generics.ListCreateAPIView): """List and create saved email searches.""" serializer_class = EmailSearchSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return EmailSearch.objects.filter(user=self.request.user).order_by('name') def perform_create(self, serializer): serializer.save(user=self.request.user) class EmailSearchDetailView(generics.RetrieveUpdateDestroyAPIView): """Retrieve, update, or delete saved email search.""" serializer_class = EmailSearchSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return EmailSearch.objects.filter(user=self.request.user) @api_view(['POST']) @permission_classes([permissions.IsAuthenticated]) @ratelimit(key='user', rate='10/m', method=['POST']) def send_email(request): """Send email endpoint.""" serializer = EmailCreateSerializer(data=request.data, context={'request': request}) if serializer.is_valid(): email = serializer.save() # Send email asynchronously send_email_task.delay(email.id) return Response({ 'message': 'Email queued for sending', 'email': EmailSerializer(email).data }, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @api_view(['POST']) @permission_classes([permissions.IsAuthenticated]) @ratelimit(key='user', rate='5/m', method=['POST']) def fetch_emails(request): """Fetch emails from IMAP server.""" user = request.user # Check if user has IMAP settings configured if not user.imap_host or not user.imap_username: return Response( {'error': 'IMAP settings not configured'}, status=status.HTTP_400_BAD_REQUEST ) # Fetch emails asynchronously fetch_emails_task.delay(user.id) return Response({'message': 'Email fetch started'}, status=status.HTTP_200_OK) @api_view(['GET']) @permission_classes([permissions.IsAuthenticated]) def email_stats(request): """Get email statistics.""" user = request.user stats = { 'total_emails': Email.objects.filter(user=user).count(), 'unread_emails': Email.objects.filter(user=user, is_read=False).count(), 'starred_emails': Email.objects.filter(user=user, is_starred=True).count(), 'important_emails': Email.objects.filter(user=user, is_important=True).count(), 'sent_emails': Email.objects.filter(user=user, status='sent').count(), 'draft_emails': Email.objects.filter(user=user, status='draft').count(), 'folder_stats': EmailFolder.objects.filter(user=user).annotate( email_count=Count('emails'), unread_count=Count('emails', filter=Q(emails__is_read=False)) ).values('name', 'email_count', 'unread_count'), } return Response(stats) @api_view(['POST']) @permission_classes([permissions.IsAuthenticated]) def test_email_settings(request): """Test email server settings.""" user = request.user settings_type = request.data.get('type', 'smtp') # smtp or imap try: if settings_type == 'smtp': # Test SMTP connection if not user.smtp_host or not user.smtp_username: return Response( {'error': 'SMTP settings not configured'}, status=status.HTTP_400_BAD_REQUEST ) server = smtplib.SMTP(user.smtp_host, user.smtp_port) if user.smtp_use_tls: server.starttls() server.login(user.smtp_username, user.get_smtp_password()) server.quit() elif settings_type == 'imap': # Test IMAP connection if not user.imap_host or not user.imap_username: return Response( {'error': 'IMAP settings not configured'}, status=status.HTTP_400_BAD_REQUEST ) if user.imap_use_ssl: server = imaplib.IMAP4_SSL(user.imap_host, user.imap_port) else: server = imaplib.IMAP4(user.imap_host, user.imap_port) server.login(user.imap_username, user.get_imap_password()) server.logout() return Response({'message': f'{settings_type.upper()} connection successful'}) except Exception as e: return Response( {'error': f'{settings_type.upper()} connection failed: {str(e)}'}, status=status.HTTP_400_BAD_REQUEST )