ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: self-service workspace invite approval

This commit is contained in:
DCCONSTRUCTIONS
2026-05-09 22:57:07 +03:00
parent 42bc0fb0e6
commit 0be8f01283
22 changed files with 707 additions and 77 deletions
@@ -21,9 +21,15 @@ from rest_framework.response import Response
# Module imports
from plane.app.permissions import WorkSpaceAdminPermission
from plane.authentication.nodedc_workspace_policy import (
get_nodedc_workspace_creation_policy,
is_nodedc_launcher_managed_workspace,
is_nodedc_workspace_invite_approval_required,
nodedc_launcher_managed_workspace_response,
)
from plane.authentication.nodedc_workspace_invites import (
cancel_nodedc_workspace_invite_approval,
request_nodedc_workspace_invite_approval,
)
from plane.app.serializers import (
WorkSpaceMemberInviteSerializer,
WorkSpaceMemberSerializer,
@@ -56,8 +62,10 @@ class WorkspaceInvitationsViewset(BaseViewSet):
)
def create(self, request, slug):
workspace_policy = get_nodedc_workspace_creation_policy(request.user, workspace_slug=slug)
if is_nodedc_launcher_managed_workspace(request.user, slug):
return Response(nodedc_launcher_managed_workspace_response(), status=status.HTTP_403_FORBIDDEN)
requires_nodedc_approval = is_nodedc_workspace_invite_approval_required(workspace_policy)
emails = request.data.get("emails", [])
# Check if email is provided
@@ -119,6 +127,11 @@ class WorkspaceInvitationsViewset(BaseViewSet):
),
role=email.get("role", 5),
created_by=request.user,
nodedc_approval_status=(
WorkspaceMemberInvite.NODEDC_APPROVAL_PENDING
if requires_nodedc_approval
else WorkspaceMemberInvite.NODEDC_APPROVAL_NOT_REQUIRED
),
)
)
except ValidationError:
@@ -135,6 +148,33 @@ class WorkspaceInvitationsViewset(BaseViewSet):
current_site = base_host(request=request, is_app=True)
if requires_nodedc_approval:
approved_requests = []
try:
for invitation in workspace_invitations:
approval_response = request_nodedc_workspace_invite_approval(request, workspace, invitation)
approval_request = approval_response.get("taskerInviteRequest") if isinstance(approval_response, dict) else None
approval_request_id = approval_request.get("id") if isinstance(approval_request, dict) else None
if approval_request_id:
invitation.nodedc_approval_request_id = approval_request_id
invitation.save(update_fields=["nodedc_approval_request_id", "updated_at"])
approved_requests.append(approval_request_id)
except Exception:
WorkspaceMemberInvite.objects.filter(id__in=[invitation.id for invitation in workspace_invitations]).delete()
return Response(
{"error": "NODE.DC approval request failed"},
status=status.HTTP_502_BAD_GATEWAY,
)
return Response(
{
"message": "NODE.DC approval requested",
"approval": "nodedc",
"requests": approved_requests,
},
status=status.HTTP_200_OK,
)
# Send invitations
for invitation in workspace_invitations:
workspace_invitation.delay(
@@ -165,6 +205,15 @@ class WorkspaceInvitationsViewset(BaseViewSet):
return Response(nodedc_launcher_managed_workspace_response(), status=status.HTTP_403_FORBIDDEN)
workspace_member_invite = WorkspaceMemberInvite.objects.get(pk=pk, workspace__slug=slug)
if workspace_member_invite.nodedc_approval_status != WorkspaceMemberInvite.NODEDC_APPROVAL_NOT_REQUIRED:
try:
cancel_nodedc_workspace_invite_approval(request, workspace_member_invite)
except Exception:
return Response(
{"error": "NODE.DC invite cancellation failed"},
status=status.HTTP_502_BAD_GATEWAY,
)
workspace_member_invite.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
@@ -187,6 +236,18 @@ class WorkspaceJoinEndpoint(BaseAPIView):
token = request.data.get("token", "")
if workspace_invite.nodedc_approval_status == WorkspaceMemberInvite.NODEDC_APPROVAL_PENDING:
return Response(
{"error": "NODE.DC has not approved this workspace invitation yet"},
status=status.HTTP_403_FORBIDDEN,
)
if workspace_invite.nodedc_approval_status == WorkspaceMemberInvite.NODEDC_APPROVAL_REJECTED:
return Response(
{"error": "NODE.DC rejected this workspace invitation"},
status=status.HTTP_403_FORBIDDEN,
)
# Validate the token to verify the user received the invitation email
if not token or workspace_invite.token != token:
return Response(
@@ -13,9 +13,12 @@ from rest_framework.response import Response
from plane.app.permissions import WorkspaceEntityPermission, allow_permission, ROLE
from plane.authentication.nodedc_workspace_policy import (
get_nodedc_workspace_creation_policy,
is_nodedc_launcher_managed_workspace,
is_nodedc_workspace_invite_approval_required,
nodedc_launcher_managed_workspace_response,
)
from plane.authentication.nodedc_workspace_invites import cancel_nodedc_workspace_member_access
# Module imports
from plane.app.serializers import (
@@ -150,6 +153,19 @@ class WorkSpaceMemberViewSet(BaseViewSet):
status=status.HTTP_400_BAD_REQUEST,
)
workspace_policy = get_nodedc_workspace_creation_policy(request.user, workspace_slug=slug)
if is_nodedc_workspace_invite_approval_required(workspace_policy):
try:
cancel_nodedc_workspace_member_access(request, workspace_member)
except Exception as exc:
return Response(
{
"error": "nodedc_workspace_member_cancel_failed",
"detail": str(exc),
},
status=status.HTTP_502_BAD_GATEWAY,
)
# Deactivate the users from the projects where the user is part of
_ = ProjectMember.objects.filter(
workspace__slug=slug, member_id=workspace_member.member_id, is_active=True