fix(federation): compute relay targets before member deletion in leave handler

getGroupDmTargetOrigins() was called after the leaving user's dm_members row
was deleted, causing their instance to be excluded from member_remove and
ownership_transfer relay fan-out if they were the only member from that peer.
Move the target origins and leaving user lookups to before the deletion.
This commit is contained in:
Jannis Braun
2026-03-26 20:19:55 +01:00
parent f072568b8a
commit 6a83b6c6a5
2 changed files with 16 additions and 12 deletions
+15 -12
View File
@@ -797,6 +797,14 @@ export async function dmRoutes(app: FastifyInstance): Promise<void> {
// Check DM channel ownership before leaving // Check DM channel ownership before leaving
const dmChannel = db.select().from(schema.dmChannels).where(eq(schema.dmChannels.id, id)).get(); const dmChannel = db.select().from(schema.dmChannels).where(eq(schema.dmChannels.id, id)).get();
// Compute federation targets BEFORE member deletion so the leaving user's peer is included
let fedTargetOrigins: string[] | undefined;
let leavingUser: typeof schema.users.$inferSelect | undefined;
if (isFederationRelayEnabled() && dmChannel?.federatedId) {
fedTargetOrigins = getGroupDmTargetOrigins(id);
leavingUser = db.select().from(schema.users).where(eq(schema.users.id, request.userId)).get() ?? undefined;
}
// Delete dm_members row // Delete dm_members row
db.delete(schema.dmMembers) db.delete(schema.dmMembers)
.where(and( .where(and(
@@ -812,18 +820,14 @@ export async function dmRoutes(app: FastifyInstance): Promise<void> {
)).run(); )).run();
// Federation: relay member_remove (leave) to peers // Federation: relay member_remove (leave) to peers
const dmChannelForFed = db.select().from(schema.dmChannels).where(eq(schema.dmChannels.id, id)).get(); if (isFederationRelayEnabled() && dmChannel?.federatedId) {
let leavingUser: typeof schema.users.$inferSelect | undefined;
let targetOrigins: string[] | undefined;
if (isFederationRelayEnabled() && dmChannelForFed?.federatedId) {
const domainOrigin = getOurOrigin(); const domainOrigin = getOurOrigin();
leavingUser = db.select().from(schema.users).where(eq(schema.users.id, request.userId)).get();
const memberRemovePayload: FederationRelayEvent = { const memberRemovePayload: FederationRelayEvent = {
eventType: 'member_remove', eventType: 'member_remove',
dmChannelId: id, dmChannelId: id,
messageId: `member_remove:${request.userId}:${Date.now()}`, messageId: `member_remove:${request.userId}:${Date.now()}`,
federatedId: dmChannelForFed.federatedId, federatedId: dmChannel.federatedId,
encryptionVersion: 0, encryptionVersion: 0,
timestamp: Date.now(), timestamp: Date.now(),
membership: { membership: {
@@ -839,7 +843,6 @@ export async function dmRoutes(app: FastifyInstance): Promise<void> {
}, },
}; };
targetOrigins = getGroupDmTargetOrigins(id);
appendMutationLog( appendMutationLog(
memberRemovePayload.messageId, memberRemovePayload.messageId,
id, id,
@@ -851,7 +854,7 @@ export async function dmRoutes(app: FastifyInstance): Promise<void> {
id, id,
'member_remove', 'member_remove',
JSON.stringify(memberRemovePayload), JSON.stringify(memberRemovePayload),
targetOrigins, fedTargetOrigins,
); );
} }
@@ -871,10 +874,10 @@ export async function dmRoutes(app: FastifyInstance): Promise<void> {
.run(); .run();
// Federation: relay ownership transfer // Federation: relay ownership transfer
if (isFederationRelayEnabled() && dmChannelForFed?.federatedId) { if (isFederationRelayEnabled() && dmChannel?.federatedId) {
const domainOrigin = getOurOrigin(); const domainOrigin = getOurOrigin();
const newOwnerUser = db.select().from(schema.users).where(eq(schema.users.id, nextOwner.userId)).get(); const newOwnerUser = db.select().from(schema.users).where(eq(schema.users.id, nextOwner.userId)).get();
const prevOwnerUser = leavingUser; // Already queried above in step D const prevOwnerUser = leavingUser; // Already queried above before member deletion
// Update federated owner columns // Update federated owner columns
db.update(schema.dmChannels) db.update(schema.dmChannels)
@@ -889,7 +892,7 @@ export async function dmRoutes(app: FastifyInstance): Promise<void> {
eventType: 'ownership_transfer', eventType: 'ownership_transfer',
dmChannelId: id, dmChannelId: id,
messageId: `ownership_transfer:${nextOwner.userId}:${Date.now()}`, messageId: `ownership_transfer:${nextOwner.userId}:${Date.now()}`,
federatedId: dmChannelForFed.federatedId, federatedId: dmChannel.federatedId,
encryptionVersion: 0, encryptionVersion: 0,
timestamp: Date.now(), timestamp: Date.now(),
ownership: { ownership: {
@@ -915,7 +918,7 @@ export async function dmRoutes(app: FastifyInstance): Promise<void> {
id, id,
'ownership_transfer', 'ownership_transfer',
JSON.stringify(transferPayload), JSON.stringify(transferPayload),
targetOrigins, fedTargetOrigins,
); );
} }
} }
@@ -286,6 +286,7 @@ export function KeybindsPanel() {
return ( return (
<div className="space-y-5"> <div className="space-y-5">
<h2 className="text-lg font-semibold text-txt-primary mb-6">Keybinds</h2>
{/* macOS Accessibility Warning */} {/* macOS Accessibility Warning */}
{isElectronMac() && accessibilityTrusted === false && ( {isElectronMac() && accessibilityTrusted === false && (
<div className="rounded-lg bg-amber-500/10 border border-amber-500/20 p-3.5"> <div className="rounded-lg bg-amber-500/10 border border-amber-500/20 p-3.5">