Files
AdventureLog/backend/server/users/serializers.py
Sean Morley 1dcf99be7d feat: add API key management to settings page
- Implemented API key creation, deletion, and display functionality.
- Updated the settings page to fetch and show existing API keys.
- Added UI elements for creating new API keys and copying them to clipboard.
- Enhanced request handling to ensure proper trailing slashes for API endpoints.
2026-03-16 15:14:32 -04:00

153 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from rest_framework import serializers
from django.contrib.auth import get_user_model
from adventures.models import Collection
User = get_user_model()
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
class ChangeEmailSerializer(serializers.Serializer):
new_email = serializers.EmailField(required=True)
def validate_new_email(self, value):
user = self.context['request'].user
if User.objects.filter(email=value).exclude(pk=user.pk).exists():
raise serializers.ValidationError("This email is already in use.")
return value
from django.conf import settings
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
UserModel = get_user_model()
# from dj_rest_auth.serializers import UserDetailsSerializer
from .models import CustomUser
from rest_framework import serializers
from django.conf import settings
import os
class UserDetailsSerializer(serializers.ModelSerializer):
"""
User model without exposing the password.
"""
@staticmethod
def validate_username(username):
if 'allauth.account' not in settings.INSTALLED_APPS:
return username
from allauth.account.adapter import get_adapter
username = get_adapter().clean_username(username.lower()) # Convert username to lowercase
return username
class Meta:
model = CustomUser
extra_fields = ['profile_pic', 'uuid', 'public_profile']
if hasattr(UserModel, 'USERNAME_FIELD'):
extra_fields.append(UserModel.USERNAME_FIELD)
if hasattr(UserModel, 'EMAIL_FIELD'):
extra_fields.append(UserModel.EMAIL_FIELD)
if hasattr(UserModel, 'first_name'):
extra_fields.append('first_name')
if hasattr(UserModel, 'last_name'):
extra_fields.append('last_name')
if hasattr(UserModel, 'date_joined'):
extra_fields.append('date_joined')
if hasattr(UserModel, 'is_staff'):
extra_fields.append('is_staff')
if hasattr(UserModel, 'disable_password'):
extra_fields.append('disable_password')
if hasattr(UserModel, 'measurement_system'):
extra_fields.append('measurement_system')
if hasattr(UserModel, 'default_currency'):
extra_fields.append('default_currency')
fields = ['pk', *extra_fields]
read_only_fields = ('email', 'date_joined', 'is_staff', 'is_superuser', 'is_active', 'pk', 'disable_password')
def handle_public_profile_change(self, instance, validated_data):
"""
Remove user from `shared_with` if public profile is set to False.
"""
if 'public_profile' in validated_data and not validated_data['public_profile']:
for collection in Collection.objects.filter(shared_with=instance):
collection.shared_with.remove(instance)
def update(self, instance, validated_data):
self.handle_public_profile_change(instance, validated_data)
return super().update(instance, validated_data)
def partial_update(self, instance, validated_data):
self.handle_public_profile_change(instance, validated_data)
return super().partial_update(instance, validated_data)
class CustomUserDetailsSerializer(UserDetailsSerializer):
"""
Custom serializer to add additional fields and logic for the user details.
"""
has_password = serializers.SerializerMethodField()
class Meta(UserDetailsSerializer.Meta):
model = CustomUser
fields = UserDetailsSerializer.Meta.fields + ['has_password', 'disable_password']
read_only_fields = UserDetailsSerializer.Meta.read_only_fields + ('uuid', 'has_password', 'disable_password')
@staticmethod
def get_has_password(instance):
"""
Computes whether the user has a usable password set.
"""
return instance.has_usable_password()
def to_representation(self, instance):
"""
Customizes the serialized output to modify `profile_pic` URL and add computed fields.
"""
representation = super().to_representation(instance)
# Construct profile picture URL if it exists
if instance.profile_pic:
public_url = os.environ.get('PUBLIC_URL', 'http://127.0.0.1:8000').rstrip('/')
public_url = public_url.replace("'", "") # Sanitize URL
representation['profile_pic'] = f"{public_url}/media/{instance.profile_pic.name}"
# Remove `pk` field from the response
representation.pop('pk', None)
# Remove the email field
representation.pop('email', None)
return representation
from .models import APIKey
class APIKeySerializer(serializers.ModelSerializer):
"""
Read serializer for APIKey never exposes the key_hash or the raw token.
The raw token is injected by the view only at creation time via an extra
``key`` field that is *not* part of the model serializer.
"""
class Meta:
model = APIKey
fields = ['id', 'name', 'key_prefix', 'created_at', 'last_used_at']
read_only_fields = ['id', 'key_prefix', 'created_at', 'last_used_at']
class APIKeyCreateSerializer(serializers.Serializer):
"""Write serializer only accepts a ``name`` for the new key."""
name = serializers.CharField(max_length=100, required=True)