feat: Add email verification, owner shift deletion, Coolify Docker compose setup, and fixes

This commit is contained in:
Richard
2026-07-31 10:16:22 +02:00
parent a69f31649c
commit 6ffcc58a4c
13 changed files with 458 additions and 334 deletions
+44 -18
View File
@@ -3,7 +3,7 @@ from rest_framework.response import Response
from rest_framework.authtoken.models import Token
from django.contrib.auth import authenticate, get_user_model
from django.shortcuts import get_object_or_404
from .models import RegistrationDomainRule, RegistrationRestrictionSetting, GuestClaimToken
from .models import RegistrationDomainRule, RegistrationRestrictionSetting, GuestClaimToken, EmailVerificationToken
from .serializers import (
UserSerializer, AdminUserSerializer, RegisterSerializer,
RegistrationDomainRuleSerializer, RegistrationRestrictionSettingSerializer
@@ -24,6 +24,11 @@ class RegisterView(views.APIView):
serializer = RegisterSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
user.is_approved = False
user.is_email_verified = False
user.is_active = True
user.save()
setting = RegistrationRestrictionSetting.get_solo()
claim_token_str = request.data.get('claim_token')
@@ -65,27 +70,48 @@ class RegisterView(views.APIView):
signup.save()
assigned_count += 1
if setting.require_admin_approval:
user.is_approved = False
user.is_active = False
user.save()
return Response({
'requires_approval': True,
'claimed_shifts_count': assigned_count,
'message': 'Konto erfolgreich registriert! Ein Administrator muss dein Konto vor der ersten Anmeldung freischalten.'
}, status=status.HTTP_201_CREATED)
v_token = EmailVerificationToken.objects.create(user=user)
print("==================================================")
print(f"[E-MAIL BESTÄTIGUNG]: http://localhost:3000/?verify_email={v_token.token}")
print("==================================================")
token, _ = Token.objects.get_or_create(user=user)
user_data = UserSerializer(user).data
return Response({
'token': token.key,
'user': user_data,
'requires_verification': True,
'verification_token': str(v_token.token),
'claimed_shifts_count': assigned_count,
'message': f'Konto erfolgreich erstellt! {assigned_count} Gast-Schichten wurden deinem Konto zugewiesen.' if assigned_count > 0 else 'Konto erfolgreich erstellt!'
'message': 'Konto erfolgreich erstellt! Bitte bestätige deine E-Mail-Adresse über den Link in der Bestätigungs-E-Mail oder warte auf die Admin-Freischaltung.'
}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class VerifyEmailView(views.APIView):
permission_classes = [permissions.AllowAny]
def post(self, request):
token_str = request.data.get('token')
if not token_str:
return Response({'error': 'Token erforderlich.'}, status=status.HTTP_400_BAD_REQUEST)
v_token = EmailVerificationToken.objects.filter(token=token_str, is_used=False).first()
if not v_token:
return Response({'error': 'Ungültiger oder bereits verwendeter Bestätigungslink.'}, status=status.HTTP_400_BAD_REQUEST)
v_token.is_used = True
v_token.save()
user = v_token.user
user.is_email_verified = True
user.is_approved = True
user.is_active = True
user.save()
token, _ = Token.objects.get_or_create(user=user)
return Response({
'token': token.key,
'user': UserSerializer(user).data,
'message': f'E-Mail-Adresse für {user.get_display_name()} wurde erfolgreich bestätigt! Du bist jetzt angemeldet.'
}, status=status.HTTP_200_OK)
class GenerateClaimLinkView(views.APIView):
permission_classes = [permissions.IsAuthenticated]
@@ -147,9 +173,9 @@ class LoginView(views.APIView):
pass
if user_obj:
if not user_obj.is_approved or not user_obj.is_active:
if not (user_obj.is_approved or user_obj.is_email_verified or user_obj.is_superuser or user_obj.is_admin_user):
return Response({
'error': 'Dein Konto wurde noch nicht von einem Administrator freigeschaltet. Bitte gedulde dich.'
'error': 'Anmeldung nicht möglich: Deine E-Mail-Adresse wurde noch nicht bestätigt und dein Konto wurde noch nicht vom Admin freigeschaltet.'
}, status=status.HTTP_403_FORBIDDEN)
user = authenticate(username=username, password=password)
@@ -211,7 +237,7 @@ class PendingUsersView(views.APIView):
permission_classes = [permissions.IsAdminUser]
def get(self, request):
pending_users = User.objects.filter(is_approved=False).order_by('-date_joined')
pending_users = User.objects.filter(is_approved=False, is_email_verified=False, is_superuser=False).order_by('-date_joined')
return Response(UserSerializer(pending_users, many=True).data)
class ApproveUserView(views.APIView):