Files
AdventureLog/backend/server/users/serializers.py
Sean Morley 5291c8ad3a API Key, Webkit Fixes, Rate Limit Support & Other Misc. Fixes (#1094)
* Refactor AdventureLog Bot workflow to improve issue validation handling and encapsulate comment and close logic

* 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.

* feat: add API Keys documentation and update contributing guidelines

* fix: update appVersion to reflect the latest build

* fix: update @tailwindcss/typography to version 0.5.19

* fix: update @tailwindcss/typography to version 0.5.19

* chore: update dependencies in pnpm-lock.yaml

- dompurify: upgraded from 3.3.1 to 3.3.3
- emoji-picker-element: upgraded from 1.29.0 to 1.29.1
- @sveltejs/adapter-node: updated to use @sveltejs/kit@2.55.0
- @sveltejs/adapter-vercel: updated to use @sveltejs/kit@2.55.0
- @sveltejs/kit: upgraded from 2.53.3 to 2.55.0
- @types/node: upgraded from 22.19.13 to 22.19.15
- autoprefixer: updated postcss version from 8.5.6 to 8.5.8
- baseline-browser-mapping: upgraded from 2.10.0 to 2.10.8
- daisyui: updated postcss version from 8.5.6 to 8.5.8
- prettier-plugin-svelte: upgraded from 3.5.0 to 3.5.1
- svelte-check: updated postcss version from 8.5.6 to 8.5.8
- devalue: upgraded from 5.6.3 to 5.6.4
- electron-to-chromium: upgraded from 1.5.302 to 1.5.313
- caniuse-lite: upgraded from 1.0.30001774 to 1.0.30001780
- mlly: upgraded from 1.8.0 to 1.8.1
- node-releases: upgraded from 2.0.27 to 2.0.36
- tar: upgraded from 7.5.9 to 7.5.11
- tinyexec: upgraded from 1.0.2 to 1.0.4

* fix: update appVersion to include the latest build identifier

* fix: enhance authentication fallback for protected media access

* feat(auth): add 'mobile-qr' to trailing slash list for URL handling

* Translated using Weblate (French)

Currently translated at 99.9% (1091 of 1092 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/fr/

* Translated using Weblate (Korean)

Currently translated at 100.0% (1092 of 1092 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/ko/

* Translated using Weblate (German)

Currently translated at 100.0% (1092 of 1092 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/de/

* Translated using Weblate (Swedish)

Currently translated at 100.0% (1092 of 1092 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/sv/

* Added translation using Weblate (Catalan)

* Translated using Weblate (Catalan)

Currently translated at 1.2% (14 of 1092 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/ca/

* Docs: Reorder immich API permissions to natural order (#1086)

* Refactor AdventureLog Bot workflow to improve issue validation handling and encapsulate comment and close logic (#1068)

* Reorder immich API permissions to natural order

---------

Co-authored-by: Sean Morley <git@seanmorley.com>

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1093 of 1093 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/tr/

* Translated using Weblate (Swedish)

Currently translated at 100.0% (1093 of 1093 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/sv/

* Translated using Weblate (German)

Currently translated at 100.0% (1093 of 1093 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/de/

* Add ENABLE_RATE_LIMITS configuration for backend rate limiting

* Set tabindex to -1 for dropdown menus to improve accessibility

* Refactor settings page: Simplify HTML structure and improve date formatting for API keys

* Update DEFAULT_SCHEMA_CLASS to use OpenAPI schema in REST framework settings

* fix: update error message for key copying and enhance usage instructions for API key

* Implement feature X to enhance user experience and fix bug Y in module Z

* feat: add .dockerignore and update Dockerfile for improved build process

* fix: add missing svelte-i18n>esbuild override in pnpm-lock and pnpm-workspace files

* refactor: update frontend CI workflow for improved quality checks and dependency management

* Refactor code structure for improved readability and maintainability

* fix: add vite>esbuild override in pnpm-lock and pnpm-workspace files

* refactor: enhance accessibility and semantics of button elements across multiple components

* feat: update API key deletion confirmation messages in multiple languages and improve server URL configuration

* fix: update djangorestframework version constraint and drf-yasg version in requirements

* fix: update appVersion to v0.12.0-main-040426 and refactor button elements to improve accessibility in CollectionCard and CollectionItineraryPlanner components

* feat: implement developer unlock feature for mobile login in Avatar component

---------

Co-authored-by: lesensei <alain-gh@lespeps.eu>
Co-authored-by: Hosted Weblate user 141821 <clearstripe@users.noreply.hosted.weblate.org>
Co-authored-by: Alex <div@alexe.at>
Co-authored-by: AntonPalmqvist <apq@users.noreply.hosted.weblate.org>
Co-authored-by: Marc Llopart <marc@medullar.com>
Co-authored-by: Stephan Zwicknagl <64196842+stephanzwicknagl@users.noreply.github.com>
Co-authored-by: Orhun <orhunavcu@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
2026-04-04 21:00:02 -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)