feat(federation): add soft-delete GC for empty group DMs with 24h grace period
Replace the hard-delete in the leave handler with a soft-delete (sets deleted_at timestamp) when the last member leaves a group DM. A new janitor sweep in the federation worker runs hourly and purges channels whose grace period has expired, cascading through reactions, embeds, attachments, messages, members, outbox/mutation-log/file-queue entries, and finally the channel itself. All client-facing dm_channels queries now filter on deleted_at IS NULL to hide soft-deleted channels from the REST API and WebSocket ready payload.
This commit is contained in:
@@ -2,7 +2,7 @@ import type { FastifyInstance } from 'fastify';
|
||||
import type { WebSocket } from 'ws';
|
||||
import { verifyJwt } from '../utils/auth.js';
|
||||
import { getDb, schema } from '../db/index.js';
|
||||
import { eq, and, inArray, desc, sql } from 'drizzle-orm';
|
||||
import { eq, and, inArray, isNull, desc, sql } from 'drizzle-orm';
|
||||
import { handleClientEvent } from './events.js';
|
||||
import { computePermissions, PermissionBits, permissionsToString } from '../utils/permissions.js';
|
||||
import type {
|
||||
@@ -1050,10 +1050,10 @@ function buildReadyPayload(userId: string): {
|
||||
const dmChannels: DmChannel[] = [];
|
||||
|
||||
if (dmChannelIds.length > 0) {
|
||||
// Batch: all DM channels (1 query)
|
||||
// Batch: all DM channels (1 query, exclude soft-deleted)
|
||||
const allDmChannelRows = batchInArray(
|
||||
dmChannelIds,
|
||||
ids => db.select().from(schema.dmChannels).where(inArray(schema.dmChannels.id, ids)).all(),
|
||||
ids => db.select().from(schema.dmChannels).where(and(inArray(schema.dmChannels.id, ids), isNull(schema.dmChannels.deletedAt))).all(),
|
||||
);
|
||||
const dmChannelMap = new Map(allDmChannelRows.map(c => [c.id, c]));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user