diff --git a/apps/worker/src/jobs/cron.delete-projects.ts b/apps/worker/src/jobs/cron.delete-projects.ts index be1d8499..0f4a3af5 100644 --- a/apps/worker/src/jobs/cron.delete-projects.ts +++ b/apps/worker/src/jobs/cron.delete-projects.ts @@ -1,9 +1,10 @@ import { logger } from '@/utils/logger'; -import { generateSalt } from '@openpanel/common/server'; -import { TABLE_NAMES, ch, chQuery, db } from '@openpanel/db'; +import { TABLE_NAMES, ch, db } from '@openpanel/db'; +import type { CronQueuePayload } from '@openpanel/queue'; +import type { Job } from 'bullmq'; import { escape } from 'sqlstring'; -export async function deleteProjects() { +export async function deleteProjects(job: Job) { const projects = await db.project.findMany({ where: { deleteAt: { @@ -24,16 +25,33 @@ export async function deleteProjects() { }); } - if (process.env.SELF_HOSTED) { + logger.info('Deleting projects', { + projects, + }); + + projects.forEach((project) => { + job.log(`Delete project: "${project.id}"`); + }); + + const where = `project_id IN (${projects.map((project) => escape(project.id)).join(',')})`; + const tables = [ + TABLE_NAMES.events, + TABLE_NAMES.profiles, + TABLE_NAMES.events_bots, + TABLE_NAMES.sessions, + TABLE_NAMES.cohort_events_mv, + TABLE_NAMES.dau_mv, + TABLE_NAMES.event_names_mv, + TABLE_NAMES.event_property_values_mv, + ]; + + for (const table of tables) { + const query = process.env.SELF_HOSTED + ? `ALTER TABLE ${table} DELETE WHERE ${where};` + : `ALTER TABLE ${table}_replicated ON CLUSTER '{cluster}' DELETE WHERE ${where};`; + await ch.command({ - query: `DELETE FROM ${TABLE_NAMES.events} WHERE project_id IN (${projects.map((project) => escape(project.id)).join(',')});`, - clickhouse_settings: { - lightweight_deletes_sync: 0, - }, - }); - } else { - await ch.command({ - query: `DELETE FROM ${TABLE_NAMES.events}_replicated ON CLUSTER '{cluster}' WHERE project_id IN (${projects.map((project) => escape(project.id)).join(',')});`, + query, clickhouse_settings: { lightweight_deletes_sync: 0, }, diff --git a/apps/worker/src/jobs/cron.ts b/apps/worker/src/jobs/cron.ts index 28908076..f4ccf9bd 100644 --- a/apps/worker/src/jobs/cron.ts +++ b/apps/worker/src/jobs/cron.ts @@ -25,7 +25,7 @@ export async function cronJob(job: Job) { return await ping(); } case 'deleteProjects': { - return await deleteProjects(); + return await deleteProjects(job); } } } diff --git a/packages/db/prisma/migrations/20250331190933_more_cascade/migration.sql b/packages/db/prisma/migrations/20250331190933_more_cascade/migration.sql new file mode 100644 index 00000000..a2f02a93 --- /dev/null +++ b/packages/db/prisma/migrations/20250331190933_more_cascade/migration.sql @@ -0,0 +1,47 @@ +-- DropForeignKey +ALTER TABLE "dashboards" DROP CONSTRAINT "dashboards_organizationId_fkey"; + +-- DropForeignKey +ALTER TABLE "integrations" DROP CONSTRAINT "integrations_organizationId_fkey"; + +-- DropForeignKey +ALTER TABLE "invites" DROP CONSTRAINT "invites_createdById_fkey"; + +-- DropForeignKey +ALTER TABLE "notifications" DROP CONSTRAINT "notifications_integrationId_fkey"; + +-- DropForeignKey +ALTER TABLE "notifications" DROP CONSTRAINT "notifications_notificationRuleId_fkey"; + +-- DropForeignKey +ALTER TABLE "reports" DROP CONSTRAINT "reports_dashboardId_fkey"; + +-- DropForeignKey +ALTER TABLE "reset_password" DROP CONSTRAINT "reset_password_accountId_fkey"; + +-- DropForeignKey +ALTER TABLE "shares" DROP CONSTRAINT "shares_organizationId_fkey"; + +-- AddForeignKey +ALTER TABLE "invites" ADD CONSTRAINT "invites_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dashboards" ADD CONSTRAINT "dashboards_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "reports" ADD CONSTRAINT "reports_dashboardId_fkey" FOREIGN KEY ("dashboardId") REFERENCES "dashboards"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "shares" ADD CONSTRAINT "shares_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "notifications" ADD CONSTRAINT "notifications_integrationId_fkey" FOREIGN KEY ("integrationId") REFERENCES "integrations"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "notifications" ADD CONSTRAINT "notifications_notificationRuleId_fkey" FOREIGN KEY ("notificationRuleId") REFERENCES "notification_rules"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "integrations" ADD CONSTRAINT "integrations_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "reset_password" ADD CONSTRAINT "reset_password_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES "accounts"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index a5b393ee..76789b5b 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -36,7 +36,7 @@ model Organization { projects Project[] members Member[] createdByUserId String? - createdBy User? @relation(name: "organizationCreatedBy", fields: [createdByUserId], references: [id]) + createdBy User? @relation(name: "organizationCreatedBy", fields: [createdByUserId], references: [id], onDelete: SetNull) ProjectAccess ProjectAccess[] Client Client[] Dashboard Dashboard[] @@ -129,7 +129,7 @@ model Member { userId String? user User? @relation(fields: [userId], references: [id], onDelete: Cascade) invitedById String? - invitedBy User? @relation("invitedBy", fields: [invitedById], references: [id]) + invitedBy User? @relation("invitedBy", fields: [invitedById], references: [id], onDelete: SetNull) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) meta Json? @@ -142,7 +142,7 @@ model Member { model Invite { id String @id email String - createdBy User @relation(fields: [createdById], references: [id]) + createdBy User @relation(fields: [createdById], references: [id], onDelete: Cascade) createdById String organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) @@ -263,7 +263,7 @@ enum ChartType { model Dashboard { id String @id @default(dbgenerated("gen_random_uuid()")) name String - organization Organization @relation(fields: [organizationId], references: [id]) + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) organizationId String projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) @@ -302,7 +302,7 @@ model Report { funnelWindow Float? dashboardId String - dashboard Dashboard @relation(fields: [dashboardId], references: [id]) + dashboard Dashboard @relation(fields: [dashboardId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@ -314,7 +314,7 @@ model ShareOverview { id String @unique projectId String @unique project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) - organization Organization @relation(fields: [organizationId], references: [id]) + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) organizationId String public Boolean @default(false) password String? @@ -389,10 +389,10 @@ model Notification { updatedAt DateTime @default(now()) @updatedAt sendToApp Boolean @default(false) sendToEmail Boolean @default(false) - integration Integration? @relation(fields: [integrationId], references: [id]) + integration Integration? @relation(fields: [integrationId], references: [id], onDelete: Cascade) integrationId String? @db.Uuid notificationRuleId String? @db.Uuid - notificationRule NotificationRule? @relation(fields: [notificationRuleId], references: [id]) + notificationRule NotificationRule? @relation(fields: [notificationRuleId], references: [id], onDelete: Cascade) /// [IPrismaNotificationPayload] payload Json? @@ -404,7 +404,7 @@ model Integration { name String /// [IPrismaIntegrationConfig] config Json - organization Organization @relation(fields: [organizationId], references: [id]) + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) organizationId String notificationRules NotificationRule[] notifications Notification[] @@ -417,7 +417,7 @@ model Integration { model ResetPassword { id String @id accountId String - account Account @relation(fields: [accountId], references: [id]) + account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) expiresAt DateTime createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt