diff --git a/src/app/api/posts/route.ts b/src/app/api/posts/route.ts index 2602507..4c46f80 100644 --- a/src/app/api/posts/route.ts +++ b/src/app/api/posts/route.ts @@ -133,6 +133,8 @@ export async function POST(request: Request) { handle: user.handle, displayName: user.displayName || user.handle, avatarUrl: user.avatarUrl || undefined, + did: user.did, + publicKey: user.publicKey, }, nodeDomain, mediaUrls: attachedMedia.map(m => m.url), diff --git a/src/app/api/swarm/interactions/follow/route.ts b/src/app/api/swarm/interactions/follow/route.ts index 3dd1cbe..c440694 100644 --- a/src/app/api/swarm/interactions/follow/route.ts +++ b/src/app/api/swarm/interactions/follow/route.ts @@ -13,7 +13,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { db, users, notifications, remoteFollowers } from '@/db'; import { eq, and, sql } from 'drizzle-orm'; import { z } from 'zod'; -import { verifyUserInteraction } from '@/lib/swarm/signature'; +import { verifySwarmRequest } from '@/lib/swarm/signature'; import { localHandleSchema, nodeDomainSchema } from '@/lib/utils/federation'; const swarmFollowSchema = z.object({ @@ -46,12 +46,7 @@ export async function POST(request: NextRequest) { // SECURITY: Verify the signature const { signature, ...payload } = data; - const isValid = await verifyUserInteraction( - payload, - signature, - data.follow.followerHandle, - data.follow.followerNodeDomain - ); + const isValid = await verifySwarmRequest(payload, signature, data.follow.followerNodeDomain); if (!isValid) { console.warn(`[Swarm] Invalid signature for follow from ${data.follow.followerHandle}@${data.follow.followerNodeDomain}`); diff --git a/src/app/api/swarm/interactions/like/route.ts b/src/app/api/swarm/interactions/like/route.ts index fb18152..e498958 100644 --- a/src/app/api/swarm/interactions/like/route.ts +++ b/src/app/api/swarm/interactions/like/route.ts @@ -10,7 +10,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { db, posts, users, notifications, remoteLikes } from '@/db'; import { eq, and } from 'drizzle-orm'; import { z } from 'zod'; -import { verifyUserInteraction } from '@/lib/swarm/signature'; +import { verifySwarmRequest } from '@/lib/swarm/signature'; import { localHandleSchema, nodeDomainSchema } from '@/lib/utils/federation'; const swarmLikeSchema = z.object({ @@ -42,12 +42,7 @@ export async function POST(request: NextRequest) { // SECURITY: Verify the signature const { signature, ...payload } = data; - const isValid = await verifyUserInteraction( - payload, - signature, - data.like.actorHandle, - data.like.actorNodeDomain - ); + const isValid = await verifySwarmRequest(payload, signature, data.like.actorNodeDomain); if (!isValid) { console.warn(`[Swarm] Invalid signature for like from ${data.like.actorHandle}@${data.like.actorNodeDomain}`); diff --git a/src/app/api/swarm/interactions/mention/route.ts b/src/app/api/swarm/interactions/mention/route.ts index a718f04..1c2faa2 100644 --- a/src/app/api/swarm/interactions/mention/route.ts +++ b/src/app/api/swarm/interactions/mention/route.ts @@ -10,7 +10,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { db, users, notifications } from '@/db'; import { eq } from 'drizzle-orm'; import { z } from 'zod'; -import { verifyUserInteraction } from '@/lib/swarm/signature'; +import { verifySwarmRequest } from '@/lib/swarm/signature'; import { localHandleSchema, nodeDomainSchema } from '@/lib/utils/federation'; const swarmMentionSchema = z.object({ @@ -44,12 +44,7 @@ export async function POST(request: NextRequest) { // SECURITY: Verify the signature const { signature, ...payload } = data; - const isValid = await verifyUserInteraction( - payload, - signature, - data.mention.actorHandle, - data.mention.actorNodeDomain - ); + const isValid = await verifySwarmRequest(payload, signature, data.mention.actorNodeDomain); if (!isValid) { console.warn(`[Swarm] Invalid signature for mention from ${data.mention.actorHandle}@${data.mention.actorNodeDomain}`); diff --git a/src/app/api/swarm/interactions/repost/route.ts b/src/app/api/swarm/interactions/repost/route.ts index 8729cc0..fb480a6 100644 --- a/src/app/api/swarm/interactions/repost/route.ts +++ b/src/app/api/swarm/interactions/repost/route.ts @@ -10,7 +10,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { db, posts, users, notifications } from '@/db'; import { eq, sql } from 'drizzle-orm'; import { z } from 'zod'; -import { verifyUserInteraction } from '@/lib/swarm/signature'; +import { verifySwarmRequest } from '@/lib/swarm/signature'; import { localHandleSchema, nodeDomainSchema } from '@/lib/utils/federation'; const swarmRepostSchema = z.object({ @@ -43,12 +43,7 @@ export async function POST(request: NextRequest) { // SECURITY: Verify the signature const { signature, ...payload } = data; - const isValid = await verifyUserInteraction( - payload, - signature, - data.repost.actorHandle, - data.repost.actorNodeDomain - ); + const isValid = await verifySwarmRequest(payload, signature, data.repost.actorNodeDomain); if (!isValid) { console.warn(`[Swarm] Invalid signature for repost from ${data.repost.actorHandle}@${data.repost.actorNodeDomain}`); diff --git a/src/app/api/swarm/interactions/unfollow/route.ts b/src/app/api/swarm/interactions/unfollow/route.ts index f439517..cc11e39 100644 --- a/src/app/api/swarm/interactions/unfollow/route.ts +++ b/src/app/api/swarm/interactions/unfollow/route.ts @@ -10,7 +10,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { db, users, remoteFollowers } from '@/db'; import { eq, and, sql } from 'drizzle-orm'; import { z } from 'zod'; -import { verifyUserInteraction } from '@/lib/swarm/signature'; +import { verifySwarmRequest } from '@/lib/swarm/signature'; import { localHandleSchema, nodeDomainSchema } from '@/lib/utils/federation'; const swarmUnfollowSchema = z.object({ @@ -40,12 +40,7 @@ export async function POST(request: NextRequest) { // SECURITY: Verify the signature const { signature, ...payload } = data; - const isValid = await verifyUserInteraction( - payload, - signature, - data.unfollow.followerHandle, - data.unfollow.followerNodeDomain - ); + const isValid = await verifySwarmRequest(payload, signature, data.unfollow.followerNodeDomain); if (!isValid) { console.warn(`[Swarm] Invalid signature for unfollow from ${data.unfollow.followerHandle}@${data.unfollow.followerNodeDomain}`); diff --git a/src/app/api/swarm/interactions/unlike/route.ts b/src/app/api/swarm/interactions/unlike/route.ts index 36032dc..241674d 100644 --- a/src/app/api/swarm/interactions/unlike/route.ts +++ b/src/app/api/swarm/interactions/unlike/route.ts @@ -10,7 +10,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { db, posts, remoteLikes } from '@/db'; import { eq, and } from 'drizzle-orm'; import { z } from 'zod'; -import { verifyUserInteraction } from '@/lib/swarm/signature'; +import { verifySwarmRequest } from '@/lib/swarm/signature'; import { localHandleSchema, nodeDomainSchema } from '@/lib/utils/federation'; const swarmUnlikeSchema = z.object({ @@ -40,12 +40,7 @@ export async function POST(request: NextRequest) { // SECURITY: Verify the signature const { signature, ...payload } = data; - const isValid = await verifyUserInteraction( - payload, - signature, - data.unlike.actorHandle, - data.unlike.actorNodeDomain - ); + const isValid = await verifySwarmRequest(payload, signature, data.unlike.actorNodeDomain); if (!isValid) { console.warn(`[Swarm] Invalid signature for unlike from ${data.unlike.actorHandle}@${data.unlike.actorNodeDomain}`); diff --git a/src/app/api/swarm/interactions/unrepost/route.ts b/src/app/api/swarm/interactions/unrepost/route.ts index 2e61b96..ae0c915 100644 --- a/src/app/api/swarm/interactions/unrepost/route.ts +++ b/src/app/api/swarm/interactions/unrepost/route.ts @@ -10,7 +10,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { db, posts } from '@/db'; import { eq, sql } from 'drizzle-orm'; import { z } from 'zod'; -import { verifyUserInteraction } from '@/lib/swarm/signature'; +import { verifySwarmRequest } from '@/lib/swarm/signature'; import { localHandleSchema, nodeDomainSchema } from '@/lib/utils/federation'; const swarmUnrepostSchema = z.object({ @@ -40,12 +40,7 @@ export async function POST(request: NextRequest) { // SECURITY: Verify the signature const { signature, ...payload } = data; - const isValid = await verifyUserInteraction( - payload, - signature, - data.unrepost.actorHandle, - data.unrepost.actorNodeDomain - ); + const isValid = await verifySwarmRequest(payload, signature, data.unrepost.actorNodeDomain); if (!isValid) { console.warn(`[Swarm] Invalid signature for unrepost from ${data.unrepost.actorHandle}@${data.unrepost.actorNodeDomain}`); diff --git a/src/app/api/swarm/replies/route.ts b/src/app/api/swarm/replies/route.ts index 8ea4b56..e1a9ced 100644 --- a/src/app/api/swarm/replies/route.ts +++ b/src/app/api/swarm/replies/route.ts @@ -23,6 +23,8 @@ const swarmReplySchema = z.object({ handle: z.string(), displayName: z.string().optional().nullable(), avatarUrl: z.string().optional(), + did: z.string().optional(), + publicKey: z.string().optional(), }), nodeDomain: z.string(), mediaUrls: z.array(z.string()).optional(), @@ -79,13 +81,14 @@ export async function POST(request: NextRequest) { } const remoteHandle = `${data.reply.author.handle}@${sourceDomain}`; - const remoteDid = `did:swarm:${sourceDomain}:${data.reply.author.handle}`; + const remoteDid = data.reply.author.did || `did:swarm:${sourceDomain}:${data.reply.author.handle}`; await upsertRemoteUser({ handle: remoteHandle, displayName: data.reply.author.displayName || data.reply.author.handle, avatarUrl: data.reply.author.avatarUrl || null, did: remoteDid, + publicKey: data.reply.author.publicKey, }); const remoteUser = await db.query.users.findFirst({ diff --git a/src/lib/swarm/user-cache.ts b/src/lib/swarm/user-cache.ts index ef9da06..4fecef2 100644 --- a/src/lib/swarm/user-cache.ts +++ b/src/lib/swarm/user-cache.ts @@ -1,5 +1,5 @@ import { db, users } from '@/db'; -import { eq } from 'drizzle-orm'; +import { eq, or } from 'drizzle-orm'; export interface RemoteProfile { handle: string; @@ -21,7 +21,10 @@ export async function upsertRemoteUser(profile: RemoteProfile): Promise { try { // Check if user already exists const existing = await db.query.users.findFirst({ - where: eq(users.did, profile.did), + where: or( + eq(users.did, profile.did), + eq(users.handle, profile.handle), + ), }); if (existing) { @@ -31,10 +34,12 @@ export async function upsertRemoteUser(profile: RemoteProfile): Promise { await db.update(users) .set({ + did: existing.did || profile.did, + handle: existing.handle || profile.handle, displayName: profile.displayName || existing.displayName, avatarUrl: profile.avatarUrl || existing.avatarUrl, isBot: profile.isBot ?? existing.isBot, - publicKey: shouldUpdateKey ? profile.publicKey : undefined, // Only update if needed + publicKey: shouldUpdateKey ? profile.publicKey : existing.publicKey, updatedAt: new Date(), }) .where(eq(users.id, existing.id));