fix: orphaned DM cleanup runs in both modes, nuke deletes space messages
- Orphaned DM channels (zero members) are unreachable garbage — clean them up regardless of purgeContent mode, not just in full/nuke mode. - Full/nuke mode now also deletes the user's space messages, their attachments, and embeds. This is the meaningful distinction: "Delete User" preserves all content as "Deleted User", "Nuke" removes it.
This commit is contained in:
@@ -3,7 +3,7 @@ import { eq, or, and, inArray } from 'drizzle-orm';
|
|||||||
import { getDb, schema } from '../db/index.js';
|
import { getDb, schema } from '../db/index.js';
|
||||||
|
|
||||||
export interface TombstoneOptions {
|
export interface TombstoneOptions {
|
||||||
/** When false, skip reaction deletion and orphaned DM purge (soft-delete mode). Default: true */
|
/** When false, skip space message/reaction deletion (soft-delete mode). Orphaned DM cleanup always runs. Default: true */
|
||||||
purgeContent?: boolean;
|
purgeContent?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +85,8 @@ export function tombstoneUser(uid: string, options?: TombstoneOptions): string[]
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (purge) {
|
// Clean up orphaned DM channels (zero members after our removal) — always runs,
|
||||||
// Clean up orphaned DM channels (zero members after our removal)
|
// orphaned channels are unreachable garbage regardless of purge mode
|
||||||
const orphanedDmIds = tx.select({ id: schema.dmChannels.id })
|
const orphanedDmIds = tx.select({ id: schema.dmChannels.id })
|
||||||
.from(schema.dmChannels)
|
.from(schema.dmChannels)
|
||||||
.all()
|
.all()
|
||||||
@@ -119,6 +119,28 @@ export function tombstoneUser(uid: string, options?: TombstoneOptions): string[]
|
|||||||
}
|
}
|
||||||
tx.delete(schema.dmChannels).where(eq(schema.dmChannels.id, dmId)).run();
|
tx.delete(schema.dmChannels).where(eq(schema.dmChannels.id, dmId)).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Purge mode: delete the user's space messages and their attachments
|
||||||
|
if (purge) {
|
||||||
|
const userMessageIds = tx.select({ id: schema.messages.id })
|
||||||
|
.from(schema.messages)
|
||||||
|
.where(eq(schema.messages.userId, uid))
|
||||||
|
.all()
|
||||||
|
.map(m => m.id);
|
||||||
|
|
||||||
|
if (userMessageIds.length > 0) {
|
||||||
|
// Collect attachment files for disk cleanup
|
||||||
|
const msgAttachments = tx.select({ filename: schema.attachments.filename })
|
||||||
|
.from(schema.attachments)
|
||||||
|
.where(inArray(schema.attachments.messageId, userMessageIds))
|
||||||
|
.all();
|
||||||
|
for (const att of msgAttachments) filesToDelete.push(att.filename);
|
||||||
|
|
||||||
|
// Delete attachments, embeds, then messages (FK order)
|
||||||
|
tx.delete(schema.attachments).where(inArray(schema.attachments.messageId, userMessageIds)).run();
|
||||||
|
tx.delete(schema.embeds).where(inArray(schema.embeds.messageId, userMessageIds)).run();
|
||||||
|
tx.delete(schema.messages).where(eq(schema.messages.userId, uid)).run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tombstone user row — rename username to free it for reuse
|
// Tombstone user row — rename username to free it for reuse
|
||||||
|
|||||||
Reference in New Issue
Block a user