routes/federation.ts had grown to 7.6k lines, spanning HTTP route registration, federated identity resolution, ~30 inbound relay event processors, DM reconciliation, and rate-limiting internals — too large to review or hold in context, and awkward to change safely. Split the implementation into 18 focused modules under routes/federation/ (helpers, events/, handlers/) and keep routes/federation.ts as a thin barrel that re-exports the public API and composes the HTTP registrars into federationRoutes(). No import paths change anywhere else. Pure move, no behavior change: - 61/61 named functions byte-identical; only deltas are 2 dynamic-import paths adjusted for the new directory depth - public export surface unchanged (barrel re-exports all 22 symbols) - all 30 endpoints preserved (identical verb+path set) - typecheck, build, and full server suite (790 tests) green Docs: update federation.md source-file map; add split design doc.
125 lines
5.7 KiB
TypeScript
125 lines
5.7 KiB
TypeScript
import { getDb } from '../../../db/index.js';
|
|
import { and } from 'drizzle-orm';
|
|
import type { FederationRelayEvent } from '@backspace/shared';
|
|
import { processDmCallAcceptEvent, processDmCallEndEvent, processDmCallRejectEvent, processDmCallStartEvent, processDmTypingStartEvent, processDmTypingStopEvent } from './calls.js';
|
|
import { processCreateEvent, processDeleteEvent, processReactionAddEvent, processReactionRemoveEvent, processUpdateEvent } from './dmMessages.js';
|
|
import { processDmCloseEvent, processDmReopenEvent, processFileRejectedEvent, processPresenceUpdateEvent, processReadStateUpdateEvent } from './dmState.js';
|
|
import { processFriendAddEvent, processFriendRemoveEvent, processFriendRequestCancelEvent, processFriendRequestCreateEvent, processFriendRequestUpdateEvent } from './friends.js';
|
|
import { processGroupMetadataUpdateEvent, processMemberAddEvent, processMemberRemoveEvent, processOwnershipTransferEvent } from './membership.js';
|
|
import { processProfileUpdateEvent } from '../profile.js';
|
|
|
|
/**
|
|
* Process an array of federation relay events. Used by the HTTP relay endpoint
|
|
* and directly by the initial-sync worker (which skips the HTTP round-trip).
|
|
*/
|
|
export async function processRelayEvents(
|
|
events: FederationRelayEvent[],
|
|
sourceInstance: string,
|
|
peerOrigin: string,
|
|
db: ReturnType<typeof getDb>,
|
|
): Promise<{
|
|
accepted: string[];
|
|
rejected: Array<{ messageId: string; reason: string }>;
|
|
undeliverable: Array<{ messageId: string; reason: string }>;
|
|
}> {
|
|
const accepted: string[] = [];
|
|
const rejected: Array<{ messageId: string; reason: string }> = [];
|
|
const undeliverable: Array<{ messageId: string; reason: string }> = [];
|
|
|
|
for (const event of events) {
|
|
try {
|
|
switch (event.eventType) {
|
|
case 'create':
|
|
await processCreateEvent(event, sourceInstance, peerOrigin, db, accepted, rejected);
|
|
break;
|
|
case 'update':
|
|
processUpdateEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'delete':
|
|
processDeleteEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'reaction_add':
|
|
processReactionAddEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'reaction_remove':
|
|
processReactionRemoveEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'member_add':
|
|
await processMemberAddEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'member_remove':
|
|
processMemberRemoveEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'ownership_transfer':
|
|
processOwnershipTransferEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'friend_request_create':
|
|
await processFriendRequestCreateEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'friend_request_update':
|
|
processFriendRequestUpdateEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'friend_request_cancel':
|
|
processFriendRequestCancelEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'friend_add':
|
|
await processFriendAddEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'friend_remove':
|
|
processFriendRemoveEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'file_rejected':
|
|
processFileRejectedEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'dm_call_start':
|
|
processDmCallStartEvent(event, sourceInstance, db, accepted, rejected, undeliverable);
|
|
break;
|
|
case 'dm_call_accept':
|
|
processDmCallAcceptEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'dm_call_reject':
|
|
processDmCallRejectEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'dm_call_end':
|
|
processDmCallEndEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'dm_typing_start':
|
|
processDmTypingStartEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'dm_typing_stop':
|
|
processDmTypingStopEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'profile_update':
|
|
await processProfileUpdateEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'group_metadata_update':
|
|
await processGroupMetadataUpdateEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'presence_update':
|
|
processPresenceUpdateEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'read_state_update':
|
|
processReadStateUpdateEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'dm_close':
|
|
processDmCloseEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
case 'dm_reopen':
|
|
processDmReopenEvent(event, sourceInstance, db, accepted, rejected);
|
|
break;
|
|
default:
|
|
rejected.push({ messageId: event.messageId, reason: 'unknown_event_type' });
|
|
break;
|
|
}
|
|
} catch (err) {
|
|
const errMsg = err instanceof Error ? err.message : 'unknown_error';
|
|
console.error(`[federation-relay] Error processing event ${event.messageId}:`, errMsg);
|
|
rejected.push({ messageId: event.messageId, reason: 'processing_error' });
|
|
}
|
|
}
|
|
|
|
return { accepted, rejected, undeliverable };
|
|
}
|
|
|
|
// ─── Helpers ────────────────────────────────────────────────────────────────
|