mirror of
https://github.com/eliasstepanik/core.git
synced 2026-01-11 09:18:26 +00:00
refactor: add cascade delete for user and workspace relations to simplify deletion logic (#122)
This commit is contained in:
parent
ef1c8eac52
commit
3a10ee53e8
@ -241,12 +241,9 @@ export async function grantUserCloudAccess({
|
||||
}
|
||||
|
||||
export async function deleteUser(id: User["id"]) {
|
||||
// Get user's workspace
|
||||
// Get user to verify they exist
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
Workspace: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@ -270,112 +267,16 @@ export async function deleteUser(id: User["id"]) {
|
||||
// Continue with deletion even if graph cleanup fails
|
||||
}
|
||||
|
||||
// If workspace exists, delete all workspace-related data
|
||||
// Most models DON'T have onDelete: Cascade, so we must delete manually
|
||||
if (user.Workspace) {
|
||||
const workspaceId = user.Workspace.id;
|
||||
|
||||
// 1. Delete nested conversation data
|
||||
await prisma.conversationExecutionStep.deleteMany({
|
||||
where: {
|
||||
conversationHistory: {
|
||||
conversation: { workspaceId },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.conversationHistory.deleteMany({
|
||||
where: {
|
||||
conversation: { workspaceId },
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.conversation.deleteMany({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
// 2. Delete space patterns (nested under Space)
|
||||
await prisma.spacePattern.deleteMany({
|
||||
where: {
|
||||
space: { workspaceId },
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.space.deleteMany({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
// 3. Delete webhook delivery logs (nested under WebhookConfiguration)
|
||||
await prisma.webhookDeliveryLog.deleteMany({
|
||||
where: {
|
||||
webhookConfiguration: { workspaceId },
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.webhookConfiguration.deleteMany({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
// 4. Delete ingestion data
|
||||
await prisma.ingestionQueue.deleteMany({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
await prisma.ingestionRule.deleteMany({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
// 5. Delete integration accounts
|
||||
await prisma.integrationAccount.deleteMany({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
await prisma.integrationDefinitionV2.deleteMany({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
// 6. Delete recall logs
|
||||
await prisma.recallLog.deleteMany({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
// 7. Delete activities
|
||||
await prisma.activity.deleteMany({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
// 8. Delete MCP sessions
|
||||
await prisma.mCPSession.deleteMany({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
// 9. Delete billing history (nested under Subscription)
|
||||
await prisma.billingHistory.deleteMany({
|
||||
where: {
|
||||
subscription: { workspaceId },
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.subscription.deleteMany({
|
||||
where: { workspaceId },
|
||||
});
|
||||
|
||||
// 10. Delete the workspace (this will CASCADE delete OAuth models automatically)
|
||||
await prisma.workspace.delete({
|
||||
where: { id: workspaceId },
|
||||
});
|
||||
}
|
||||
|
||||
// Delete user-specific data
|
||||
await prisma.personalAccessToken.deleteMany({
|
||||
where: { userId: id },
|
||||
});
|
||||
|
||||
await prisma.userUsage.deleteMany({
|
||||
where: { userId: id },
|
||||
});
|
||||
|
||||
// Finally, delete the user
|
||||
// Delete the user - cascade deletes will handle all related data:
|
||||
// - Workspace (and all workspace-related data via cascade)
|
||||
// - PersonalAccessToken
|
||||
// - UserUsage
|
||||
// - Conversations, ConversationHistory
|
||||
// - IngestionRules
|
||||
// - IntegrationAccounts
|
||||
// - RecallLogs
|
||||
// - WebhookConfigurations
|
||||
// - All OAuth models
|
||||
return prisma.user.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
@ -0,0 +1,83 @@
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Activity" DROP CONSTRAINT "Activity_workspaceId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Conversation" DROP CONSTRAINT "Conversation_userId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Conversation" DROP CONSTRAINT "Conversation_workspaceId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "IngestionRule" DROP CONSTRAINT "IngestionRule_workspaceId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "IntegrationAccount" DROP CONSTRAINT "IntegrationAccount_integratedById_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "IntegrationAccount" DROP CONSTRAINT "IntegrationAccount_workspaceId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "IntegrationDefinitionV2" DROP CONSTRAINT "IntegrationDefinitionV2_workspaceId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "MCPSession" DROP CONSTRAINT "MCPSession_workspaceId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "OAuthClientInstallation" DROP CONSTRAINT "OAuthClientInstallation_installedById_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "RecallLog" DROP CONSTRAINT "RecallLog_workspaceId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Space" DROP CONSTRAINT "Space_workspaceId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Subscription" DROP CONSTRAINT "Subscription_workspaceId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "WebhookConfiguration" DROP CONSTRAINT "WebhookConfiguration_workspaceId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Workspace" DROP CONSTRAINT "Workspace_userId_fkey";
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Activity" ADD CONSTRAINT "Activity_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Conversation" ADD CONSTRAINT "Conversation_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Conversation" ADD CONSTRAINT "Conversation_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "IngestionRule" ADD CONSTRAINT "IngestionRule_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "IntegrationAccount" ADD CONSTRAINT "IntegrationAccount_integratedById_fkey" FOREIGN KEY ("integratedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "IntegrationAccount" ADD CONSTRAINT "IntegrationAccount_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "IntegrationDefinitionV2" ADD CONSTRAINT "IntegrationDefinitionV2_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "MCPSession" ADD CONSTRAINT "MCPSession_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "OAuthClientInstallation" ADD CONSTRAINT "OAuthClientInstallation_installedById_fkey" FOREIGN KEY ("installedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "RecallLog" ADD CONSTRAINT "RecallLog_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Space" ADD CONSTRAINT "Space_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "WebhookConfiguration" ADD CONSTRAINT "WebhookConfiguration_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Subscription" ADD CONSTRAINT "Subscription_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Workspace" ADD CONSTRAINT "Workspace_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@ -0,0 +1,23 @@
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "ConversationHistory" DROP CONSTRAINT "ConversationHistory_userId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "IngestionRule" DROP CONSTRAINT "IngestionRule_userId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "RecallLog" DROP CONSTRAINT "RecallLog_userId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "UserUsage" DROP CONSTRAINT "UserUsage_userId_fkey";
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ConversationHistory" ADD CONSTRAINT "ConversationHistory_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "IngestionRule" ADD CONSTRAINT "IngestionRule_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "RecallLog" ADD CONSTRAINT "RecallLog_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "UserUsage" ADD CONSTRAINT "UserUsage_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@ -0,0 +1,5 @@
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "IngestionQueue" DROP CONSTRAINT "IngestionQueue_workspaceId_fkey";
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "IngestionQueue" ADD CONSTRAINT "IngestionQueue_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@ -25,7 +25,7 @@ model Activity {
|
||||
|
||||
rejectionReason String?
|
||||
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
workspaceId String
|
||||
|
||||
WebhookDeliveryLog WebhookDeliveryLog[]
|
||||
@ -55,10 +55,10 @@ model Conversation {
|
||||
unread Boolean @default(false)
|
||||
|
||||
title String?
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String
|
||||
|
||||
workspace Workspace? @relation(fields: [workspaceId], references: [id])
|
||||
workspace Workspace? @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
workspaceId String?
|
||||
|
||||
status String @default("pending") // Can be "pending", "running", "completed", "failed", "need_attention"
|
||||
@ -102,7 +102,7 @@ model ConversationHistory {
|
||||
context Json?
|
||||
|
||||
thoughts Json?
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String?
|
||||
|
||||
conversation Conversation @relation(fields: [conversationId], references: [id])
|
||||
@ -122,7 +122,7 @@ model IngestionQueue {
|
||||
type String?
|
||||
|
||||
workspaceId String
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
|
||||
activity Activity? @relation(fields: [activityId], references: [id])
|
||||
activityId String?
|
||||
@ -148,10 +148,10 @@ model IngestionRule {
|
||||
source String // Source/integration this rule applies to (mandatory)
|
||||
isActive Boolean @default(true) // Enable/disable rule (mandatory)
|
||||
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
workspaceId String
|
||||
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String
|
||||
}
|
||||
|
||||
@ -166,11 +166,11 @@ model IntegrationAccount {
|
||||
settings Json?
|
||||
isActive Boolean @default(true)
|
||||
|
||||
integratedBy User @relation(references: [id], fields: [integratedById])
|
||||
integratedBy User @relation(references: [id], fields: [integratedById], onDelete: Cascade)
|
||||
integratedById String
|
||||
integrationDefinition IntegrationDefinitionV2 @relation(references: [id], fields: [integrationDefinitionId])
|
||||
integrationDefinitionId String
|
||||
workspace Workspace @relation(references: [id], fields: [workspaceId])
|
||||
workspace Workspace @relation(references: [id], fields: [workspaceId], onDelete: Cascade)
|
||||
workspaceId String
|
||||
Activity Activity[]
|
||||
oauthIntegrationGrants OAuthIntegrationGrant[]
|
||||
@ -193,7 +193,7 @@ model IntegrationDefinitionV2 {
|
||||
version String?
|
||||
url String?
|
||||
|
||||
workspace Workspace? @relation(references: [id], fields: [workspaceId])
|
||||
workspace Workspace? @relation(references: [id], fields: [workspaceId], onDelete: Cascade)
|
||||
workspaceId String?
|
||||
|
||||
IntegrationAccount IntegrationAccount[]
|
||||
@ -213,7 +213,7 @@ model MCPSession {
|
||||
source String
|
||||
integrations String[]
|
||||
|
||||
workspace Workspace? @relation(references: [id], fields: [workspaceId])
|
||||
workspace Workspace? @relation(references: [id], fields: [workspaceId], onDelete: Cascade)
|
||||
workspaceId String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
@ -304,7 +304,7 @@ model OAuthClient {
|
||||
workspaceId String?
|
||||
|
||||
// Created by user (for audit trail)
|
||||
createdBy User? @relation(fields: [createdById], references: [id])
|
||||
createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull)
|
||||
createdById String?
|
||||
|
||||
// Relations
|
||||
@ -330,7 +330,7 @@ model OAuthClientInstallation {
|
||||
workspaceId String
|
||||
|
||||
// Installation metadata
|
||||
installedBy User @relation(fields: [installedById], references: [id])
|
||||
installedBy User @relation(fields: [installedById], references: [id], onDelete: Cascade)
|
||||
installedById String
|
||||
installedAt DateTime @default(now())
|
||||
uninstalledAt DateTime?
|
||||
@ -454,10 +454,10 @@ model RecallLog {
|
||||
responseTimeMs Int? // Response time in milliseconds
|
||||
|
||||
// Relations
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String
|
||||
|
||||
workspace Workspace? @relation(fields: [workspaceId], references: [id])
|
||||
workspace Workspace? @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
workspaceId String?
|
||||
|
||||
conversation Conversation? @relation(fields: [conversationId], references: [id])
|
||||
@ -485,7 +485,7 @@ model Space {
|
||||
contextCountAtLastTrigger Int? // Context count when pattern was last triggered
|
||||
|
||||
// Relations
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
workspaceId String
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
@ -584,7 +584,7 @@ model UserUsage {
|
||||
searchCreditsUsed Int @default(0)
|
||||
chatCreditsUsed Int @default(0)
|
||||
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String @unique
|
||||
}
|
||||
|
||||
@ -596,7 +596,7 @@ model WebhookConfiguration {
|
||||
eventTypes String[] // List of event types this webhook is interested in, e.g. ["activity.created"]
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
userId String?
|
||||
workspace Workspace? @relation(fields: [workspaceId], references: [id])
|
||||
workspace Workspace? @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
workspaceId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@ -652,7 +652,7 @@ model Subscription {
|
||||
overageAmount Float @default(0)
|
||||
|
||||
// Relations
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
workspaceId String @unique
|
||||
BillingHistory BillingHistory[]
|
||||
}
|
||||
@ -697,7 +697,7 @@ model Workspace {
|
||||
integrations String[]
|
||||
|
||||
userId String? @unique
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
IngestionQueue IngestionQueue[]
|
||||
IntegrationAccount IntegrationAccount[]
|
||||
IntegrationDefinitionV2 IntegrationDefinitionV2[]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user