feat: Add PrimeProgress, PrimeSelect, and PrimeSkeleton components with customizable styles and props

- Implemented PrimeProgress component with support for labels, helper text, and various styles (size, variant, color).
- Created PrimeSelect component with dropdown functionality, custom templates, and validation states.
- Developed PrimeSkeleton component for loading placeholders with different shapes and animations.
- Updated index.ts to export new components for easy import.
- Enhanced PrimeVueTest.vue to include tests for new components and their functionalities.
- Introduced a custom ThrillWiki theme for PrimeVue with tailored color schemes and component styles.
- Added ambient type declarations for various components to improve TypeScript support.
This commit is contained in:
pacnpal
2025-08-27 21:00:02 -04:00
parent 6125c4ee44
commit 08a4a2d034
164 changed files with 73094 additions and 11001 deletions

View File

@@ -5,7 +5,10 @@ This module contains all authentication-related API endpoints including
login, signup, logout, password management, and social authentication.
"""
from django.contrib.auth import authenticate, login, logout, get_user_model
# type: ignore[misc,attr-defined,arg-type,call-arg,index,assignment]
from typing import TYPE_CHECKING, Type, Any
from django.contrib.auth import login, logout, get_user_model
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import ValidationError
from rest_framework import status
@@ -15,56 +18,21 @@ from rest_framework.response import Response
from rest_framework.permissions import AllowAny, IsAuthenticated
from drf_spectacular.utils import extend_schema, extend_schema_view
# Import serializers inside methods to avoid Django initialization issues
# Placeholder classes for schema decorators
class LoginInputSerializer:
pass
class LoginOutputSerializer:
pass
class SignupInputSerializer:
pass
class SignupOutputSerializer:
pass
class LogoutOutputSerializer:
pass
class UserOutputSerializer:
pass
class PasswordResetInputSerializer:
pass
class PasswordResetOutputSerializer:
pass
class PasswordChangeInputSerializer:
pass
class PasswordChangeOutputSerializer:
pass
class SocialProviderOutputSerializer:
pass
class AuthStatusOutputSerializer:
pass
# Import serializers from the auth serializers module
from ..serializers.auth import (
LoginInputSerializer,
LoginOutputSerializer,
SignupInputSerializer,
SignupOutputSerializer,
LogoutOutputSerializer,
UserOutputSerializer,
PasswordResetInputSerializer,
PasswordResetOutputSerializer,
PasswordChangeInputSerializer,
PasswordChangeOutputSerializer,
SocialProviderOutputSerializer,
AuthStatusOutputSerializer,
)
# Handle optional dependencies with fallback classes
@@ -73,7 +41,8 @@ class AuthStatusOutputSerializer:
class FallbackTurnstileMixin:
"""Fallback mixin if TurnstileMixin is not available."""
def validate_turnstile(self, request):
def validate_turnstile(self, request: Any) -> None:
"""Fallback validation method that does nothing."""
pass
@@ -83,6 +52,14 @@ try:
except ImportError:
TurnstileMixin = FallbackTurnstileMixin
# Type hint for the mixin
if TYPE_CHECKING:
from typing import Union
TurnstileMixinType = Union[Type[FallbackTurnstileMixin], Any]
else:
TurnstileMixinType = TurnstileMixin
UserModel = get_user_model()
@@ -98,7 +75,7 @@ UserModel = get_user_model()
tags=["Authentication"],
),
)
class LoginAPIView(TurnstileMixin, APIView):
class LoginAPIView(TurnstileMixin, APIView): # type: ignore[misc]
"""API endpoint for user login."""
permission_classes = [AllowAny]
@@ -106,90 +83,33 @@ class LoginAPIView(TurnstileMixin, APIView):
serializer_class = LoginInputSerializer
def post(self, request: Request) -> Response:
from ..serializers import LoginInputSerializer, LoginOutputSerializer
try:
# Validate Turnstile if configured
self.validate_turnstile(request)
except ValidationError as e:
return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
serializer = LoginInputSerializer(data=request.data)
serializer = LoginInputSerializer(
data=request.data, context={"request": request}
)
if serializer.is_valid():
# type: ignore[index]
email_or_username = serializer.validated_data["username"]
password = serializer.validated_data["password"] # type: ignore[index]
# The serializer handles authentication validation
user = serializer.validated_data["user"] # type: ignore[index]
# Optimized user lookup: single query using Q objects
from django.db.models import Q
from django.contrib.auth import get_user_model
login(request._request, user) # type: ignore[attr-defined]
# Optimized token creation - get_or_create is atomic
from rest_framework.authtoken.models import Token
User = get_user_model()
user = None
token, created = Token.objects.get_or_create(user=user)
# Single query to find user by email OR username
try:
if "@" in email_or_username:
# Email-like input: try email first, then username as fallback
user_obj = (
User.objects.select_related()
.filter(
Q(email=email_or_username) | Q(username=email_or_username)
)
.first()
)
else:
# Username-like input: try username first, then email as fallback
user_obj = (
User.objects.select_related()
.filter(
Q(username=email_or_username) | Q(email=email_or_username)
)
.first()
)
if user_obj:
user = authenticate(
# type: ignore[attr-defined]
request._request,
username=user_obj.username,
password=password,
)
except Exception:
# Fallback to original behavior
user = authenticate(
# type: ignore[attr-defined]
request._request,
username=email_or_username,
password=password,
)
if user:
if user.is_active:
login(request._request, user) # type: ignore[attr-defined]
# Optimized token creation - get_or_create is atomic
from rest_framework.authtoken.models import Token
token, created = Token.objects.get_or_create(user=user)
response_serializer = LoginOutputSerializer(
{
"token": token.key,
"user": user,
"message": "Login successful",
}
)
return Response(response_serializer.data)
else:
return Response(
{"error": "Account is disabled"},
status=status.HTTP_400_BAD_REQUEST,
)
else:
return Response(
{"error": "Invalid credentials"},
status=status.HTTP_400_BAD_REQUEST,
)
response_serializer = LoginOutputSerializer(
{
"token": token.key,
"user": user,
"message": "Login successful",
}
)
return Response(response_serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@@ -206,7 +126,7 @@ class LoginAPIView(TurnstileMixin, APIView):
tags=["Authentication"],
),
)
class SignupAPIView(TurnstileMixin, APIView):
class SignupAPIView(TurnstileMixin, APIView): # type: ignore[misc]
"""API endpoint for user registration."""
permission_classes = [AllowAny]
@@ -385,9 +305,9 @@ class SocialProvidersAPIView(APIView):
site = get_current_site(request._request) # type: ignore[attr-defined]
# Cache key based on site and request host
cache_key = (
f"social_providers:{getattr(site, 'id', site.pk)}:{request.get_host()}"
)
# Use pk for Site objects, domain for RequestSite objects
site_identifier = getattr(site, "pk", site.domain)
cache_key = f"social_providers:{site_identifier}:{request.get_host()}"
# Try to get from cache first (cache for 15 minutes)
cached_providers = cache.get(cache_key)