feat(federation): send file_rejected reverse relay on size limit exceeded

This commit is contained in:
Jannis Braun
2026-03-27 04:37:27 +01:00
parent 7125c62a1f
commit 1990b90022
+97 -1
View File
@@ -2,7 +2,7 @@ import { getDb } from '../db/index.js';
import * as schema from '../db/schema.js'; import * as schema from '../db/schema.js';
import { eq, and, lte, asc, inArray } from 'drizzle-orm'; import { eq, and, lte, asc, inArray } from 'drizzle-orm';
import { config } from '../config.js'; import { config } from '../config.js';
import { isFederationRelayEnabled } from './federationOutbox.js'; import { isFederationRelayEnabled, queueOutboxEvent } from './federationOutbox.js';
import { runFederationJanitor } from './storageJanitor.js'; import { runFederationJanitor } from './storageJanitor.js';
import { buildFederationHeaders, getOurOrigin } from './federationAuth.js'; import { buildFederationHeaders, getOurOrigin } from './federationAuth.js';
import { generateSnowflake } from './snowflake.js'; import { generateSnowflake } from './snowflake.js';
@@ -355,6 +355,100 @@ async function processFileQueueTick(): Promise<void> {
} }
} }
function handleSizeRejection(
db: ReturnType<typeof getDb>,
entry: typeof schema.federationFileQueue.$inferSelect,
maxUploadSize: number,
now: number,
): void {
// Look up the local DM message to find the sender and channel info
const localMsg = db.select()
.from(schema.dmMessages)
.where(eq(schema.dmMessages.id, entry.dmMessageId))
.get();
if (!localMsg || !localMsg.sourceInstance || !localMsg.sourceMessageId) return;
// Find the attachment row to get its ID
const att = db.select()
.from(schema.attachments)
.where(
and(
eq(schema.attachments.dmMessageId, entry.dmMessageId),
eq(schema.attachments.sourceUrl, entry.sourceUrl),
),
)
.get();
// Resolve the sender's username from their local replicated user stub
const senderUser = db.select()
.from(schema.users)
.where(eq(schema.users.id, localMsg.userId))
.get();
const sourceUsername = senderUser?.displayName || senderUser?.username || 'unknown';
// Update attachment federation status
if (att) {
db.update(schema.attachments)
.set({
federationStatus: 'remote',
federationMeta: JSON.stringify({
sourceInstance: localMsg.sourceInstance,
sourceUserId: localMsg.userId,
sourceUsername,
}),
})
.where(eq(schema.attachments.id, att.id))
.run();
}
// Find local DM members native to THIS instance
const ourOrigin = getOurOrigin();
const dmMembers = db.select()
.from(schema.dmMembers)
.where(eq(schema.dmMembers.dmChannelId, localMsg.dmChannelId))
.all();
const affectedUserIds: string[] = [];
for (const member of dmMembers) {
const user = db.select()
.from(schema.users)
.where(eq(schema.users.id, member.userId))
.get();
if (user && (!user.homeInstance || user.homeInstance === ourOrigin)) {
affectedUserIds.push(user.homeUserId || user.id);
}
}
// Queue a file_rejected reverse relay event to the sender's instance
const event: FederationRelayEvent = {
eventType: 'file_rejected',
messageId: localMsg.sourceMessageId,
encryptionVersion: 0,
timestamp: now,
attachmentId: att?.id ?? entry.sourceUrl,
rejectionReason: 'size_limit_exceeded',
rejectionLimit: maxUploadSize,
affectedUserIds,
};
queueOutboxEvent(
localMsg.sourceMessageId,
localMsg.dmChannelId,
'file_rejected',
JSON.stringify(event),
[localMsg.sourceInstance],
);
// Broadcast updated message to local clients so they see the 'remote' badge
const updatedMsg = getDmMessageWithUser(entry.dmMessageId);
if (updatedMsg) {
connectionManager.sendToDmMembers(updatedMsg.dmChannelId, {
type: 'dm_message_updated',
message: updatedMsg,
});
}
}
async function processFileQueueEntry( async function processFileQueueEntry(
db: ReturnType<typeof getDb>, db: ReturnType<typeof getDb>,
entry: typeof schema.federationFileQueue.$inferSelect, entry: typeof schema.federationFileQueue.$inferSelect,
@@ -398,6 +492,7 @@ async function processFileQueueEntry(
}) })
.where(eq(schema.federationFileQueue.id, entry.id)) .where(eq(schema.federationFileQueue.id, entry.id))
.run(); .run();
handleSizeRejection(db, entry, maxUploadSize, now);
return; return;
} }
@@ -450,6 +545,7 @@ async function processFileQueueEntry(
}) })
.where(eq(schema.federationFileQueue.id, entry.id)) .where(eq(schema.federationFileQueue.id, entry.id))
.run(); .run();
handleSizeRejection(db, entry, maxUploadSize, now);
return; return;
} }