This commit is contained in:
Iliyan Angelov
2025-09-14 23:24:25 +03:00
commit c67067a2a4
71311 changed files with 6800714 additions and 0 deletions

0
accounts/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

64
accounts/admin.py Normal file
View File

@@ -0,0 +1,64 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .models import User, UserProfile, LoginAttempt, EmailVerification
@admin.register(User)
class UserAdmin(BaseUserAdmin):
"""Custom User admin."""
list_display = ('email', 'username', 'first_name', 'last_name', 'is_verified', 'is_active', 'date_joined')
list_filter = ('is_verified', 'is_active', 'is_staff', 'is_superuser', 'date_joined')
search_fields = ('email', 'username', 'first_name', 'last_name')
ordering = ('-date_joined',)
fieldsets = (
(None, {'fields': ('email', 'username', 'password')}),
('Personal info', {'fields': ('first_name', 'last_name', 'avatar')}),
('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
('Email Settings', {'fields': (
'smtp_host', 'smtp_port', 'smtp_username', 'smtp_use_tls',
'imap_host', 'imap_port', 'imap_username', 'imap_use_ssl'
)}),
('Important dates', {'fields': ('last_login', 'date_joined')}),
('Security', {'fields': ('is_verified', 'last_login_ip')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'username', 'first_name', 'last_name', 'password1', 'password2'),
}),
)
@admin.register(UserProfile)
class UserProfileAdmin(admin.ModelAdmin):
"""User Profile admin."""
list_display = ('user', 'timezone', 'language', 'theme', 'auto_reply_enabled')
list_filter = ('timezone', 'language', 'theme', 'auto_reply_enabled')
search_fields = ('user__email', 'user__username')
raw_id_fields = ('user',)
@admin.register(LoginAttempt)
class LoginAttemptAdmin(admin.ModelAdmin):
"""Login Attempt admin."""
list_display = ('email', 'ip_address', 'success', 'failure_reason', 'timestamp')
list_filter = ('success', 'timestamp')
search_fields = ('email', 'ip_address')
readonly_fields = ('timestamp',)
ordering = ('-timestamp',)
@admin.register(EmailVerification)
class EmailVerificationAdmin(admin.ModelAdmin):
"""Email Verification admin."""
list_display = ('user', 'token', 'is_used', 'created_at', 'expires_at')
list_filter = ('is_used', 'created_at', 'expires_at')
search_fields = ('user__email', 'token')
readonly_fields = ('token', 'created_at')
raw_id_fields = ('user',)

6
accounts/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'accounts'

View File

@@ -0,0 +1,112 @@
# Generated by Django 4.2.7 on 2025-09-14 20:10
from django.conf import settings
import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('email', models.EmailField(max_length=254, unique=True)),
('first_name', models.CharField(max_length=30)),
('last_name', models.CharField(max_length=30)),
('avatar', models.ImageField(blank=True, null=True, upload_to='avatars/')),
('is_verified', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('last_login_ip', models.GenericIPAddressField(blank=True, null=True)),
('smtp_host', models.CharField(blank=True, max_length=255)),
('smtp_port', models.IntegerField(default=587)),
('smtp_username', models.CharField(blank=True, max_length=255)),
('smtp_password', models.BinaryField(blank=True)),
('smtp_use_tls', models.BooleanField(default=True)),
('imap_host', models.CharField(blank=True, max_length=255)),
('imap_port', models.IntegerField(default=993)),
('imap_username', models.CharField(blank=True, max_length=255)),
('imap_password', models.BinaryField(blank=True)),
('imap_use_ssl', models.BooleanField(default=True)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
'db_table': 'users',
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('signature', models.TextField(blank=True)),
('auto_reply_enabled', models.BooleanField(default=False)),
('auto_reply_message', models.TextField(blank=True)),
('timezone', models.CharField(default='UTC', max_length=50)),
('language', models.CharField(default='en', max_length=10)),
('theme', models.CharField(default='light', max_length=20)),
('emails_per_page', models.IntegerField(default=20)),
('auto_save_drafts', models.BooleanField(default=True)),
('show_images', models.BooleanField(default=True)),
('mark_as_read_delay', models.IntegerField(default=3)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'user_profiles',
},
),
migrations.CreateModel(
name='LoginAttempt',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.EmailField(max_length=254)),
('ip_address', models.GenericIPAddressField()),
('user_agent', models.TextField()),
('success', models.BooleanField(default=False)),
('failure_reason', models.CharField(blank=True, max_length=100)),
('timestamp', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'login_attempts',
'ordering': ['-timestamp'],
},
),
migrations.CreateModel(
name='EmailVerification',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('token', models.CharField(max_length=100, unique=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('expires_at', models.DateTimeField()),
('is_used', models.BooleanField(default=False)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'email_verifications',
},
),
]

View File

142
accounts/models.py Normal file
View File

@@ -0,0 +1,142 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils import timezone
from cryptography.fernet import Fernet
import os
class User(AbstractUser):
"""Custom User model with additional fields for email functionality."""
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
is_verified = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
last_login_ip = models.GenericIPAddressField(null=True, blank=True)
# Email server settings
smtp_host = models.CharField(max_length=255, blank=True)
smtp_port = models.IntegerField(default=587)
smtp_username = models.CharField(max_length=255, blank=True)
smtp_password = models.BinaryField(blank=True) # Encrypted
smtp_use_tls = models.BooleanField(default=True)
imap_host = models.CharField(max_length=255, blank=True)
imap_port = models.IntegerField(default=993)
imap_username = models.CharField(max_length=255, blank=True)
imap_password = models.BinaryField(blank=True) # Encrypted
imap_use_ssl = models.BooleanField(default=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'first_name', 'last_name']
class Meta:
db_table = 'users'
def __str__(self):
return self.email
def get_full_name(self):
return f"{self.first_name} {self.last_name}".strip()
def get_short_name(self):
return self.first_name
def encrypt_password(self, password):
"""Encrypt password for storage."""
key = os.environ.get('ENCRYPTION_KEY', Fernet.generate_key())
f = Fernet(key)
return f.encrypt(password.encode())
def decrypt_password(self, encrypted_password):
"""Decrypt password for use."""
if not encrypted_password:
return None
key = os.environ.get('ENCRYPTION_KEY', Fernet.generate_key())
f = Fernet(key)
return f.decrypt(encrypted_password).decode()
def set_smtp_password(self, password):
"""Set encrypted SMTP password."""
self.smtp_password = self.encrypt_password(password)
def get_smtp_password(self):
"""Get decrypted SMTP password."""
return self.decrypt_password(self.smtp_password)
def set_imap_password(self, password):
"""Set encrypted IMAP password."""
self.imap_password = self.encrypt_password(password)
def get_imap_password(self):
"""Get decrypted IMAP password."""
return self.decrypt_password(self.imap_password)
class UserProfile(models.Model):
"""Extended user profile information."""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
signature = models.TextField(blank=True)
auto_reply_enabled = models.BooleanField(default=False)
auto_reply_message = models.TextField(blank=True)
timezone = models.CharField(max_length=50, default='UTC')
language = models.CharField(max_length=10, default='en')
theme = models.CharField(max_length=20, default='light')
# Email preferences
emails_per_page = models.IntegerField(default=20)
auto_save_drafts = models.BooleanField(default=True)
show_images = models.BooleanField(default=True)
mark_as_read_delay = models.IntegerField(default=3) # seconds
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'user_profiles'
def __str__(self):
return f"{self.user.email} Profile"
class LoginAttempt(models.Model):
"""Track login attempts for security."""
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
email = models.EmailField()
ip_address = models.GenericIPAddressField()
user_agent = models.TextField()
success = models.BooleanField(default=False)
failure_reason = models.CharField(max_length=100, blank=True)
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'login_attempts'
ordering = ['-timestamp']
def __str__(self):
status = "Success" if self.success else "Failed"
return f"{self.email} - {status} - {self.timestamp}"
class EmailVerification(models.Model):
"""Email verification tokens."""
user = models.ForeignKey(User, on_delete=models.CASCADE)
token = models.CharField(max_length=100, unique=True)
created_at = models.DateTimeField(auto_now_add=True)
expires_at = models.DateTimeField()
is_used = models.BooleanField(default=False)
class Meta:
db_table = 'email_verifications'
def __str__(self):
return f"{self.user.email} - {self.token}"
def is_expired(self):
return timezone.now() > self.expires_at

204
accounts/serializers.py Normal file
View File

@@ -0,0 +1,204 @@
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',)

16
accounts/urls.py Normal file
View File

@@ -0,0 +1,16 @@
from django.urls import path
from . import views
urlpatterns = [
path('register/', views.UserRegistrationView.as_view(), name='user-register'),
path('login/', views.UserLoginView.as_view(), name='user-login'),
path('logout/', views.LogoutView.as_view(), name='user-logout'),
path('profile/', views.UserProfileView.as_view(), name='user-profile'),
path('profile/settings/', views.UserProfileSettingsView.as_view(), name='user-profile-settings'),
path('email-settings/', views.EmailSettingsView.as_view(), name='email-settings'),
path('change-password/', views.PasswordChangeView.as_view(), name='change-password'),
path('verify-email/', views.EmailVerificationView.as_view(), name='verify-email'),
path('resend-verification/', views.ResendVerificationView.as_view(), name='resend-verification'),
path('login-attempts/', views.LoginAttemptsView.as_view(), name='login-attempts'),
path('stats/', views.user_stats, name='user-stats'),
]

230
accounts/views.py Normal file
View File

@@ -0,0 +1,230 @@
from rest_framework import status, generics, permissions
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.tokens import RefreshToken
from django.contrib.auth import login
from django.utils import timezone
from django_ratelimit.decorators import ratelimit
from django.db import transaction
import uuid
from .models import User, UserProfile, LoginAttempt, EmailVerification
from .serializers import (
UserRegistrationSerializer, UserLoginSerializer, UserSerializer,
UserProfileSerializer, EmailSettingsSerializer, PasswordChangeSerializer,
EmailVerificationSerializer, LoginAttemptSerializer
)
class UserRegistrationView(APIView):
"""User registration endpoint."""
permission_classes = [permissions.AllowAny]
def post(self, request):
serializer = UserRegistrationSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
# Generate tokens
refresh = RefreshToken.for_user(user)
access_token = refresh.access_token
return Response({
'message': 'User registered successfully. Please verify your email.',
'user': UserSerializer(user).data,
'tokens': {
'refresh': str(refresh),
'access': str(access_token),
}
}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class UserLoginView(APIView):
"""User login endpoint."""
permission_classes = [permissions.AllowAny]
def post(self, request):
serializer = UserLoginSerializer(data=request.data)
if serializer.is_valid():
user = serializer.validated_data['user']
# Log login attempt
LoginAttempt.objects.create(
user=user,
email=user.email,
ip_address=self.get_client_ip(request),
user_agent=request.META.get('HTTP_USER_AGENT', ''),
success=True
)
# Generate tokens
refresh = RefreshToken.for_user(user)
access_token = refresh.access_token
# Update last login IP
user.last_login_ip = self.get_client_ip(request)
user.save()
return Response({
'message': 'Login successful',
'user': UserSerializer(user).data,
'tokens': {
'refresh': str(refresh),
'access': str(access_token),
}
}, status=status.HTTP_200_OK)
# Log failed login attempt
email = request.data.get('email', '')
LoginAttempt.objects.create(
email=email,
ip_address=self.get_client_ip(request),
user_agent=request.META.get('HTTP_USER_AGENT', ''),
success=False,
failure_reason='Invalid credentials'
)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def get_client_ip(self, request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
class UserProfileView(generics.RetrieveUpdateAPIView):
"""User profile management."""
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
def get_object(self):
return self.request.user
class UserProfileSettingsView(generics.RetrieveUpdateAPIView):
"""User profile settings management."""
serializer_class = UserProfileSerializer
permission_classes = [permissions.IsAuthenticated]
def get_object(self):
profile, created = UserProfile.objects.get_or_create(user=self.request.user)
return profile
class EmailSettingsView(generics.RetrieveUpdateAPIView):
"""Email server settings management."""
serializer_class = EmailSettingsSerializer
permission_classes = [permissions.IsAuthenticated]
def get_object(self):
return self.request.user
class PasswordChangeView(APIView):
"""Password change endpoint."""
permission_classes = [permissions.IsAuthenticated]
@ratelimit(key='user', rate='5/m', method=['POST'])
def post(self, request):
serializer = PasswordChangeSerializer(data=request.data, context={'request': request})
if serializer.is_valid():
serializer.save()
return Response({'message': 'Password changed successfully'}, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class EmailVerificationView(APIView):
"""Email verification endpoint."""
permission_classes = [permissions.AllowAny]
def post(self, request):
serializer = EmailVerificationSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
return Response({
'message': 'Email verified successfully',
'user': UserSerializer(user).data
}, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class ResendVerificationView(APIView):
"""Resend email verification."""
permission_classes = [permissions.IsAuthenticated]
@ratelimit(key='user', rate='3/m', method=['POST'])
def post(self, request):
user = request.user
if user.is_verified:
return Response({'message': 'Email is already verified'}, status=status.HTTP_400_BAD_REQUEST)
# Create new verification token
token = str(uuid.uuid4())
EmailVerification.objects.create(
user=user,
token=token,
expires_at=timezone.now() + timezone.timedelta(hours=24)
)
# TODO: Send verification email
return Response({'message': 'Verification email sent'}, status=status.HTTP_200_OK)
class LoginAttemptsView(generics.ListAPIView):
"""View login attempts for security monitoring."""
serializer_class = LoginAttemptSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return LoginAttempt.objects.filter(user=self.request.user).order_by('-timestamp')[:50]
class LogoutView(APIView):
"""Logout endpoint."""
permission_classes = [permissions.IsAuthenticated]
def post(self, request):
try:
refresh_token = request.data["refresh"]
token = RefreshToken(refresh_token)
token.blacklist()
return Response({'message': 'Logout successful'}, status=status.HTTP_200_OK)
except Exception as e:
return Response({'error': 'Invalid token'}, status=status.HTTP_400_BAD_REQUEST)
@api_view(['GET'])
@permission_classes([permissions.IsAuthenticated])
def user_stats(request):
"""Get user statistics."""
user = request.user
stats = {
'total_emails': 0, # Will be implemented with emails app
'unread_emails': 0, # Will be implemented with emails app
'sent_emails': 0, # Will be implemented with emails app
'draft_emails': 0, # Will be implemented with emails app
'last_login': user.last_login,
'is_verified': user.is_verified,
'account_created': user.date_joined,
}
return Response(stats)