From 5c02bff2d284cb64c02c614016fb68e8dc819339 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Wed, 1 Apr 2026 12:38:50 +0200 Subject: [PATCH] feat(federation): POST /api/dm accepts homeUserId+homeInstance for federated DM creation --- packages/server/src/routes/dm.ts | 35 +++++++++++++++++++------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/packages/server/src/routes/dm.ts b/packages/server/src/routes/dm.ts index 236b5fcb..b36188df 100644 --- a/packages/server/src/routes/dm.ts +++ b/packages/server/src/routes/dm.ts @@ -344,24 +344,31 @@ export async function dmRoutes(app: FastifyInstance): Promise { app.post<{ Body: CreateDmRequest }>('/api/dm', { preHandler: authenticate, }, async (request, reply) => { - const { userId } = request.body; - - if (!userId || typeof userId !== 'string') { - return reply.code(400).send({ error: 'userId is required', statusCode: 400 }); - } - - if (userId === request.userId) { - return reply.code(400).send({ error: 'Cannot create DM with yourself', statusCode: 400 }); - } + const { userId, homeUserId, homeInstance } = request.body; const db = getDb(); + let targetUser: typeof schema.users.$inferSelect | undefined; + + if (homeUserId && homeInstance) { + // Federated identity: resolve or create a replicated user stub + targetUser = resolveOrCreateReplicatedUser(homeUserId, homeInstance, db); + } else if (userId && typeof userId === 'string') { + // Local ID: direct lookup (existing behavior) + targetUser = db.select().from(schema.users).where(eq(schema.users.id, userId)).get(); + } else { + return reply.code(400).send({ error: 'userId or (homeUserId + homeInstance) is required', statusCode: 400 }); + } - // Check if target user exists - const targetUser = db.select().from(schema.users).where(eq(schema.users.id, userId)).get(); if (!targetUser) { return reply.code(404).send({ error: 'User not found', statusCode: 404 }); } + const targetUserId = targetUser.id; + + if (targetUserId === request.userId) { + return reply.code(400).send({ error: 'Cannot create DM with yourself', statusCode: 400 }); + } + // Check if DM channel already exists between these two users // (both have membership rows, regardless of closed state) const myDms = db.select() @@ -374,7 +381,7 @@ export async function dmRoutes(app: FastifyInstance): Promise { .from(schema.dmMembers) .where(and( eq(schema.dmMembers.dmChannelId, myDm.dmChannelId), - eq(schema.dmMembers.userId, userId), + eq(schema.dmMembers.userId, targetUserId), )) .get(); @@ -477,7 +484,7 @@ export async function dmRoutes(app: FastifyInstance): Promise { tx.insert(schema.dmMembers).values({ dmChannelId, - userId, + userId: targetUserId, }).run(); }); @@ -495,7 +502,7 @@ export async function dmRoutes(app: FastifyInstance): Promise { }; // Broadcast dm_channel_created to the other user so their sidebar updates - connectionManager.sendToUser(userId, { + connectionManager.sendToUser(targetUserId, { type: 'dm_channel_created', dmChannel: result, });