from apps.orgs.models import Organization, OrganizationMembership, OrganizationInvitation, Role, RoleMembership from apps.orgs.serializers import ModelSerializer, OrganizationSerializer, OrganizationMembershipSerializer, OrganizationInvitationSerializer, RoleSerializer, RoleMembershipSerializer from rest_framework.viewsets import ModelViewSet from rest_framework.permissions import IsAuthenticated from django.db.models import Q from rest_framework.response import Response from rest_framework.status import HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND, HTTP_400_BAD_REQUEST from rest_framework.decorators import action from django.utils import timezone class OrganizationViewSet(ModelViewSet): queryset = Organization.objects.all() serializer_class = OrganizationSerializer permission_classes = [IsAuthenticated] lookup_field = 'uuid' def get_queryset(self): return Organization.objects.filter(Q(memberships__user = self.request.user) | Q(owner = self.request.user)).distinct() def perform_create(self, serializer): organization = serializer.save(owner=self.request.user) OrganizationMembership.objects.create(user = self.request.user, organization = organization) def update(self, request, *args, **kwargs): if not request.user.is_manager: return Response({'error': 'Only managers can update organization details'}, status=HTTP_403_FORBIDDEN) return super().update(request, *args, **kwargs) @action(detail=True, methods=['post'], url_path='create-invite') def create_invite(self, request, uuid = None): organization = self.get_object() if not request.user.is_manager: return Response({'error': 'Only managers can create invites'}, status = HTTP_403_FORBIDDEN) max_uses = request.query_params.get('max_uses') max_uses = int(max_uses) if max_uses and max_uses.isdigit() and int(max_uses) > 0 else 1 invitation = OrganizationInvitation.objects.create( organization = organization, created_by = request.user, max_uses = max_uses ) return Response(OrganizationInvitationSerializer(invitation).data) @action(detail=True, methods=['post'], url_path='join/(?P[0-9a-f-]{36})') def join(self, request, uuid = None, token = None): try: organization = Organization.objects.get(uuid=uuid) except Organization.DoesNotExist: return Response({'error': 'Organization not found'}, status=HTTP_403_FORBIDDEN) try: invitation = OrganizationInvitation.objects.get(token = token, organization = organization) except OrganizationInvitation.DoesNotExist: return Response({'error': 'Invalid invitation token'}, status = HTTP_404_NOT_FOUND) if not invitation.is_active or invitation.expires_at < timezone.now(): return Response({'error': 'Invitation token is no longer valid'}, status = HTTP_400_BAD_REQUEST) if OrganizationMembership.objects.filter(user = request.user, organization = organization).exists(): return Response({'error': 'You are already a member of this organization'}, status = HTTP_403_FORBIDDEN) OrganizationMembership.objects.create(user = request.user, organization = organization) invitation.max_uses -= 1 if invitation.max_uses <= 0: invitation.is_active = False invitation.used_by.add(request.user) invitation.save() return Response({'message': 'Successfully joined the organization'}) @action(detail=True, methods=['post'], url_path='leave') def leave(self, request, uuid = None): organization = self.get_object() try: membership = OrganizationMembership.objects.get(user = request.user, organization = organization) except OrganizationMembership.DoesNotExist: return Response({'error': 'You are not a member of this organization'}, status = HTTP_403_FORBIDDEN) if organization.owner == request.user: return Response({'error': 'The owner cannot leave the organization. Please transfer ownership or delete the organization.'}, status = HTTP_403_FORBIDDEN) membership.delete() return Response({'message': 'Successfully left the organization'}) @action(detail=True, methods=['get'], url_path='invite') def list_invites(self, request, uuid = None): if not request.user.is_manager: return Response({'error': 'Only managers can view invites'}, status = HTTP_403_FORBIDDEN) organization = self.get_object() invites = OrganizationInvitation.objects.filter(organization = organization) serializer = OrganizationInvitationSerializer(invites, many = True) return Response(serializer.data) @action(detail=True, methods=['get'], url_path='invite/(?P[0-9a-f-]{36})') def invite_detail(self, request, uuid = None, token = None): if not request.user.is_manager: return Response({'error': 'Only managers can view invite details'}, status = HTTP_403_FORBIDDEN) organization = self.get_object() try: invitation = OrganizationInvitation.objects.get(token = token, organization = organization) except OrganizationInvitation.DoesNotExist: return Response({'error': 'Invalid invitation token'}, status = HTTP_403_FORBIDDEN) serializer = OrganizationInvitationSerializer(invitation) return Response(serializer.data) @action(detail=True, methods=['get'], url_path='member') def list_members(self, request, uuid = None): if not request.user.is_manager: return Response({'error': 'Only managers can view members'}, status = HTTP_403_FORBIDDEN) organization = self.get_object() memberships = OrganizationMembership.objects.filter(organization = organization) serializer = OrganizationMembershipSerializer(memberships, many = True) return Response(serializer.data) @action(detail=True, methods=['post'], url_path='member/(?P\d+)/remove') def remove_member(self, request, uuid = None, user_id = None): if not request.user.is_manager: return Response({'error': 'Only managers can remove members'}, status = HTTP_403_FORBIDDEN) organization = self.get_object() try: membership = OrganizationMembership.objects.get(user__id = user_id, organization = organization) except OrganizationMembership.DoesNotExist: return Response({'error': 'User is not a member of this organization'}, status = HTTP_403_FORBIDDEN) if membership.user == organization.owner: return Response({'error': 'Cannot remove the owner from the organization'}, status = HTTP_403_FORBIDDEN) membership.delete() return Response({'message': 'Member successfully removed from the organization'}) @action(detail=True, methods=['get'], url_path='role') def list_roles(self, request, uuid = None): organization = self.get_object() roles = Role.objects.filter(organization = organization) serializer = RoleSerializer(roles, many = True) return Response(serializer.data) @action(detail=True, methods=['post'], url_path='role') def create_role(self, request, uuid = None): organization = self.get_object() if not request.user.is_manager: return Response({'error': 'Only managers can create roles'}, status = HTTP_403_FORBIDDEN) name = request.data.get('name') if not name: return Response({'error': 'Role name is required'}, status = HTTP_403_FORBIDDEN) role = Role.objects.create(name = name, organization = organization) serializer = RoleSerializer(role) return Response(serializer.data)