feat(database): UserDeletionAuditLog + cascade FKs for admin user delete
This commit is contained in:
+35
@@ -0,0 +1,35 @@
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "public"."MfaBackupCode" DROP CONSTRAINT "MfaBackupCode_userId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "public"."PersonalAccessToken" DROP CONSTRAINT "PersonalAccessToken_userId_fkey";
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."UserDeletionAuditLog" (
|
||||
"id" TEXT NOT NULL,
|
||||
"adminUserId" TEXT NOT NULL,
|
||||
"adminEmail" TEXT NOT NULL,
|
||||
"targetUserId" TEXT NOT NULL,
|
||||
"targetEmail" TEXT NOT NULL,
|
||||
"softDeletedOrgIds" TEXT[],
|
||||
"reason" TEXT,
|
||||
"ipAddress" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "UserDeletionAuditLog_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "UserDeletionAuditLog_adminUserId_idx" ON "public"."UserDeletionAuditLog"("adminUserId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "UserDeletionAuditLog_targetUserId_idx" ON "public"."UserDeletionAuditLog"("targetUserId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "UserDeletionAuditLog_createdAt_idx" ON "public"."UserDeletionAuditLog"("createdAt");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."MfaBackupCode" ADD CONSTRAINT "MfaBackupCode_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."PersonalAccessToken" ADD CONSTRAINT "PersonalAccessToken_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -74,7 +74,7 @@ model MfaBackupCode {
|
||||
/// Hash of the actual code
|
||||
code String
|
||||
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
userId String
|
||||
|
||||
usedAt DateTime?
|
||||
@@ -131,7 +131,7 @@ model PersonalAccessToken {
|
||||
/// This is used to find the token in the database
|
||||
hashedToken String @unique
|
||||
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
userId String
|
||||
|
||||
revokedAt DateTime?
|
||||
@@ -2590,6 +2590,32 @@ model ImpersonationAuditLog {
|
||||
@@index([createdAt])
|
||||
}
|
||||
|
||||
model UserDeletionAuditLog {
|
||||
id String @id @default(cuid())
|
||||
|
||||
/// Denormalized — audit row must survive after the target user is deleted.
|
||||
/// No FKs to User; store IDs and emails as plain text.
|
||||
adminUserId String
|
||||
adminEmail String
|
||||
targetUserId String
|
||||
targetEmail String
|
||||
|
||||
/// Organization IDs that were soft-deleted as part of this user delete
|
||||
/// (orgs where the deleted user was the sole member).
|
||||
softDeletedOrgIds String[]
|
||||
|
||||
/// Optional: ticket link / GDPR request id / free-text SE note.
|
||||
reason String?
|
||||
|
||||
ipAddress String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([adminUserId])
|
||||
@@index([targetUserId])
|
||||
@@index([createdAt])
|
||||
}
|
||||
|
||||
enum CustomerQuerySource {
|
||||
DASHBOARD
|
||||
API
|
||||
|
||||
Reference in New Issue
Block a user