406 lines
14 KiB
Python
406 lines
14 KiB
Python
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
|
|
)
|