refactor: implement user deletion functionality and enhance settings management
This commit is contained in:
@@ -3,23 +3,191 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlmodel import col, select
|
||||
|
||||
from app.core.auth import AuthContext, get_auth_context
|
||||
from app.core.auth import AuthContext, delete_clerk_user, get_auth_context
|
||||
from app.db import crud
|
||||
from app.db.session import get_session
|
||||
from app.models.activity_events import ActivityEvent
|
||||
from app.models.agents import Agent
|
||||
from app.models.approvals import Approval
|
||||
from app.models.board_group_memory import BoardGroupMemory
|
||||
from app.models.board_groups import BoardGroup
|
||||
from app.models.board_memory import BoardMemory
|
||||
from app.models.board_onboarding import BoardOnboardingSession
|
||||
from app.models.boards import Board
|
||||
from app.models.gateways import Gateway
|
||||
from app.models.organization_board_access import OrganizationBoardAccess
|
||||
from app.models.organization_invite_board_access import OrganizationInviteBoardAccess
|
||||
from app.models.organization_invites import OrganizationInvite
|
||||
from app.models.organization_members import OrganizationMember
|
||||
from app.models.organizations import Organization
|
||||
from app.models.task_dependencies import TaskDependency
|
||||
from app.models.task_fingerprints import TaskFingerprint
|
||||
from app.models.tasks import Task
|
||||
from app.models.users import User
|
||||
from app.schemas.common import OkResponse
|
||||
from app.schemas.users import UserRead, UserUpdate
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
from app.models.users import User
|
||||
|
||||
router = APIRouter(prefix="/users", tags=["users"])
|
||||
AUTH_CONTEXT_DEP = Depends(get_auth_context)
|
||||
SESSION_DEP = Depends(get_session)
|
||||
|
||||
|
||||
async def _delete_organization_tree(
|
||||
session: AsyncSession,
|
||||
*,
|
||||
organization_id: UUID,
|
||||
) -> None:
|
||||
"""Delete an organization and dependent rows without committing."""
|
||||
board_ids = select(Board.id).where(col(Board.organization_id) == organization_id)
|
||||
task_ids = select(Task.id).where(col(Task.board_id).in_(board_ids))
|
||||
agent_ids = select(Agent.id).where(col(Agent.board_id).in_(board_ids))
|
||||
member_ids = select(OrganizationMember.id).where(
|
||||
col(OrganizationMember.organization_id) == organization_id,
|
||||
)
|
||||
invite_ids = select(OrganizationInvite.id).where(
|
||||
col(OrganizationInvite.organization_id) == organization_id,
|
||||
)
|
||||
group_ids = select(BoardGroup.id).where(
|
||||
col(BoardGroup.organization_id) == organization_id,
|
||||
)
|
||||
|
||||
await crud.delete_where(
|
||||
session,
|
||||
ActivityEvent,
|
||||
col(ActivityEvent.task_id).in_(task_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
ActivityEvent,
|
||||
col(ActivityEvent.agent_id).in_(agent_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
TaskDependency,
|
||||
col(TaskDependency.board_id).in_(board_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
TaskFingerprint,
|
||||
col(TaskFingerprint.board_id).in_(board_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
Approval,
|
||||
col(Approval.board_id).in_(board_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
BoardMemory,
|
||||
col(BoardMemory.board_id).in_(board_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
BoardOnboardingSession,
|
||||
col(BoardOnboardingSession.board_id).in_(board_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
OrganizationBoardAccess,
|
||||
col(OrganizationBoardAccess.board_id).in_(board_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
OrganizationInviteBoardAccess,
|
||||
col(OrganizationInviteBoardAccess.board_id).in_(board_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
OrganizationBoardAccess,
|
||||
col(OrganizationBoardAccess.organization_member_id).in_(member_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
OrganizationInviteBoardAccess,
|
||||
col(OrganizationInviteBoardAccess.organization_invite_id).in_(invite_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
Task,
|
||||
col(Task.board_id).in_(board_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
Agent,
|
||||
col(Agent.board_id).in_(board_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
Board,
|
||||
col(Board.organization_id) == organization_id,
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
BoardGroupMemory,
|
||||
col(BoardGroupMemory.board_group_id).in_(group_ids),
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
BoardGroup,
|
||||
col(BoardGroup.organization_id) == organization_id,
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
Gateway,
|
||||
col(Gateway.organization_id) == organization_id,
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
OrganizationInvite,
|
||||
col(OrganizationInvite.organization_id) == organization_id,
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
OrganizationMember,
|
||||
col(OrganizationMember.organization_id) == organization_id,
|
||||
commit=False,
|
||||
)
|
||||
await crud.update_where(
|
||||
session,
|
||||
User,
|
||||
col(User.active_organization_id) == organization_id,
|
||||
active_organization_id=None,
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
Organization,
|
||||
col(Organization.id) == organization_id,
|
||||
commit=False,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/me", response_model=UserRead)
|
||||
async def get_me(auth: AuthContext = AUTH_CONTEXT_DEP) -> UserRead:
|
||||
"""Return the authenticated user's current profile payload."""
|
||||
@@ -45,3 +213,71 @@ async def update_me(
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
return UserRead.model_validate(user)
|
||||
|
||||
|
||||
@router.delete("/me", response_model=OkResponse)
|
||||
async def delete_me(
|
||||
session: AsyncSession = SESSION_DEP,
|
||||
auth: AuthContext = AUTH_CONTEXT_DEP,
|
||||
) -> OkResponse:
|
||||
"""Delete the authenticated account and any personal-only organizations."""
|
||||
if auth.actor_type != "user" or auth.user is None:
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
user: User = auth.user
|
||||
await delete_clerk_user(user.clerk_user_id)
|
||||
memberships = await OrganizationMember.objects.filter_by(user_id=user.id).all(session)
|
||||
|
||||
await crud.update_where(
|
||||
session,
|
||||
OrganizationInvite,
|
||||
col(OrganizationInvite.created_by_user_id) == user.id,
|
||||
created_by_user_id=None,
|
||||
commit=False,
|
||||
)
|
||||
await crud.update_where(
|
||||
session,
|
||||
OrganizationInvite,
|
||||
col(OrganizationInvite.accepted_by_user_id) == user.id,
|
||||
accepted_by_user_id=None,
|
||||
commit=False,
|
||||
)
|
||||
await crud.update_where(
|
||||
session,
|
||||
Task,
|
||||
col(Task.created_by_user_id) == user.id,
|
||||
created_by_user_id=None,
|
||||
commit=False,
|
||||
)
|
||||
|
||||
for member in memberships:
|
||||
org_members = await OrganizationMember.objects.filter_by(
|
||||
organization_id=member.organization_id,
|
||||
).all(session)
|
||||
if len(org_members) <= 1:
|
||||
await _delete_organization_tree(
|
||||
session,
|
||||
organization_id=member.organization_id,
|
||||
)
|
||||
continue
|
||||
await crud.delete_where(
|
||||
session,
|
||||
OrganizationBoardAccess,
|
||||
col(OrganizationBoardAccess.organization_member_id) == member.id,
|
||||
commit=False,
|
||||
)
|
||||
await crud.delete_where(
|
||||
session,
|
||||
OrganizationMember,
|
||||
col(OrganizationMember.id) == member.id,
|
||||
commit=False,
|
||||
)
|
||||
|
||||
await crud.delete_where(
|
||||
session,
|
||||
User,
|
||||
col(User.id) == user.id,
|
||||
commit=False,
|
||||
)
|
||||
await session.commit()
|
||||
return OkResponse()
|
||||
|
||||
Reference in New Issue
Block a user