Refactor chat system to remove E2EE and simplify messaging

Removed all E2EE chat endpoints, crypto logic, and related API routes, transitioning chat to plain text storage and transport. Updated chat send/receive endpoints to use signed actions and enforce DM privacy settings. Cleaned up Swarm chat inbox, key management, and related debug endpoints. Updated user profile to support DM privacy, improved search for handle queries, and refactored conversation/message logic to support the new model. Migrated bot settings and privacy settings to new locations.
This commit is contained in:
Christopher
2026-01-28 13:05:34 -08:00
parent b52b3f9804
commit ceae76d58f
44 changed files with 1945 additions and 3058 deletions
+5 -88
View File
@@ -9,7 +9,7 @@ import { NextRequest, NextResponse } from 'next/server';
import { db, chatConversations, chatMessages, users } from '@/db';
import { eq, desc, and, lt, isNull } from 'drizzle-orm';
import { getSession } from '@/lib/auth';
import { decryptMessage } from '@/lib/swarm/chat-crypto';
export async function GET(request: NextRequest) {
try {
@@ -43,15 +43,6 @@ export async function GET(request: NextRequest) {
return NextResponse.json({ error: 'Conversation not found' }, { status: 404 });
}
// Get user's private key for decryption
const user = await db.query.users.findFirst({
where: eq(users.id, session.user.id),
});
if (!user?.privateKeyEncrypted) {
return NextResponse.json({ error: 'Cannot decrypt messages' }, { status: 500 });
}
// Build query with cursor-based pagination
const baseCondition = eq(chatMessages.conversationId, conversationId);
const whereCondition = cursor
@@ -65,90 +56,16 @@ export async function GET(request: NextRequest) {
limit,
});
// Get recipient info for sent messages
const recipientHandle = conversation.participant2Handle;
let recipientPublicKey: string | null = null;
console.log('[Messages API] Fetching recipient key for:', recipientHandle);
// Check if this is a remote user (has @domain)
const isRemote = recipientHandle.includes('@');
if (isRemote) {
// Remote user - fetch from their node
const [handle, domain] = recipientHandle.split('@');
try {
const protocol = domain.includes('localhost') ? 'http' : 'https';
const response = await fetch(`${protocol}://${domain}/api/users/${handle}`);
if (response.ok) {
const data = await response.json();
recipientPublicKey = data.user?.chatPublicKey || null;
console.log('[Messages API] Fetched remote recipient key:', !!recipientPublicKey);
}
} catch (error) {
console.error('[Messages API] Failed to fetch remote recipient key:', error);
}
} else {
// Local user
const recipientUser = await db.query.users.findFirst({
where: eq(users.handle, recipientHandle),
});
recipientPublicKey = recipientUser?.chatPublicKey || null;
}
console.log('[Messages API] Recipient public key found:', !!recipientPublicKey);
// Get sender DID for received messages
const senderDids = new Map<string, string>();
for (const msg of messages) {
const messagesMapped = messages.map((msg) => {
const isSentByMe = msg.senderHandle === session.user.handle;
if (!isSentByMe && !senderDids.has(msg.senderHandle)) {
// Try to get DID for this sender
try {
const isRemote = msg.senderHandle.includes('@');
if (isRemote) {
const [handle, domain] = msg.senderHandle.split('@');
const protocol = domain.includes('localhost') ? 'http' : 'https';
const response = await fetch(`${protocol}://${domain}/api/users/${handle}`);
if (response.ok) {
const data = await response.json();
if (data.user?.did) {
senderDids.set(msg.senderHandle, data.user.did);
}
}
} else {
const senderUser = await db.query.users.findFirst({
where: eq(users.handle, msg.senderHandle),
});
if (senderUser?.did) {
senderDids.set(msg.senderHandle, senderUser.did);
}
}
} catch (e) {
console.error('[Messages API] Failed to resolve sender DID:', e);
}
}
}
const messagesWithDecryption = messages.map((msg) => {
const isSentByMe = msg.senderHandle === session.user.handle;
const senderPubKey = isSentByMe ? recipientPublicKey : msg.senderChatPublicKey;
console.log('[Messages API] Message:', msg.id, 'isSentByMe:', isSentByMe, 'senderPubKey:', !!senderPubKey, 'msgSenderChatPubKey:', !!msg.senderChatPublicKey);
return {
id: msg.id,
senderHandle: msg.senderHandle,
senderDisplayName: msg.senderDisplayName,
senderAvatarUrl: msg.senderAvatarUrl,
senderDid: isSentByMe ? undefined : senderDids.get(msg.senderHandle), // Add DID for received messages
// For decryption:
// - Sent messages: need recipient's public key
// - Received messages: need sender's public key
senderPublicKey: senderPubKey,
isE2E: !!msg.senderChatPublicKey || (isSentByMe && !!recipientPublicKey),
encryptedContent: msg.encryptedContent, // This is now the full envelope JSON
senderDid: msg.senderDid,
content: msg.content,
deliveredAt: msg.deliveredAt,
readAt: msg.readAt,
createdAt: msg.createdAt,
@@ -157,7 +74,7 @@ export async function GET(request: NextRequest) {
});
return NextResponse.json({
messages: messagesWithDecryption.reverse(), // Oldest first for display
messages: messagesMapped.reverse(), // Oldest first for display
nextCursor: messages.length === limit ? messages[messages.length - 1].createdAt.toISOString() : null,
});
} catch (error) {