feat(federation): capture dm_close/reopen/read_state/profile/file_rejected in mutation log

Four event types previously bypassed appendMutationLog, making
them unrecoverable via /api/federation/sync after peer inactivity:
  - queueDmCloseRelay (dm_close, dm_reopen)
  - queueReadStateRelay (read_state_update)
  - handleSizeRejection in federationWorker (file_rejected)
  - profile PATCH route (profile_update) — two call sites,
    one appendMutationLog per profile change (not per target origin)

The /api/federation/sync response builder is extended to
serialize these event types in the next task.
This commit is contained in:
Jannis Braun
2026-04-22 00:48:49 +02:00
parent 250596c0f6
commit a23e02339e
4 changed files with 175 additions and 2 deletions
+15 -1
View File
@@ -8,7 +8,7 @@ import { AVATAR_COLORS } from '@backspace/shared';
import { sanitizeUser } from '../utils/sanitize.js';
import { deleteUploadFile, deleteAttachmentByFilename } from '../utils/fileCleanup.js';
import { tombstoneUser, collectDeletionBroadcastTargets, collectProfileBroadcastTargetIds } from '../utils/userDeletion.js';
import { queueOutboxEvent, isFederationRelayEnabled } from '../utils/federationOutbox.js';
import { queueOutboxEvent, isFederationRelayEnabled, appendMutationLog } from '../utils/federationOutbox.js';
import { generateSnowflake } from '../utils/snowflake.js';
import { resizeProfileImage } from '../utils/thumbnail.js';
import { config } from '../config.js';
@@ -374,6 +374,13 @@ export async function userRoutes(app: FastifyInstance): Promise<void> {
bio: preUpdateUser.bio,
};
appendMutationLog(
preUpdateUser.id,
preUpdateUser.id,
'profile_update',
JSON.stringify({ profileUpdate: profilePayload }),
'profile',
);
for (const targetOrigin of newOrigins) {
queueOutboxEvent(
preUpdateUser.id,
@@ -529,6 +536,13 @@ export async function userRoutes(app: FastifyInstance): Promise<void> {
bio: updatedUser!.bio,
};
appendMutationLog(
updatedUser!.id,
updatedUser!.id,
'profile_update',
JSON.stringify({ profileUpdate: profilePayload }),
'profile',
);
queueOutboxEvent(
updatedUser!.id, // entityId — user's ID (coalesces rapid edits)
updatedUser!.id, // contextId — user-scoped