Add V2 chat protocol support and improve error handling
Introduces support for V2 encrypted chat envelopes in the swarm inbox route, allowing storage and processing of new message formats. Enhances error handling and logging in both chat send and encryption hooks, including detection of remote nodes that do not support the V2 protocol and providing clearer error messages for users and developers.
This commit is contained in:
@@ -102,6 +102,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const remoteUrl = `https://${targetNodeDomain}/api/swarm/chat/inbox`;
|
const remoteUrl = `https://${targetNodeDomain}/api/swarm/chat/inbox`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log('[Chat Send] Forwarding to remote:', remoteUrl, 'Envelope:', JSON.stringify(body, null, 2));
|
||||||
const response = await fetch(remoteUrl, {
|
const response = await fetch(remoteUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -109,8 +110,30 @@ export async function POST(request: NextRequest) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => ({}));
|
const errorText = await response.text();
|
||||||
console.error('[Chat Send] Remote delivery failed:', errorData);
|
let errorData;
|
||||||
|
try {
|
||||||
|
errorData = JSON.parse(errorText);
|
||||||
|
} catch {
|
||||||
|
errorData = { raw: errorText };
|
||||||
|
}
|
||||||
|
console.error('[Chat Send] Remote delivery failed:', response.status, errorData);
|
||||||
|
|
||||||
|
// Check if remote node doesn't support V2 (returns V1 validation errors)
|
||||||
|
const isV1ValidationError = errorData.details &&
|
||||||
|
Array.isArray(errorData.details) &&
|
||||||
|
errorData.details.some((d: any) =>
|
||||||
|
d.path && ['messageId', 'senderHandle', 'senderNodeDomain', 'recipientHandle', 'encryptedContent', 'timestamp'].includes(d.path[0])
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isV1ValidationError) {
|
||||||
|
return NextResponse.json({
|
||||||
|
error: 'Remote node needs update',
|
||||||
|
details: 'The recipient node is running an older version that does not support the V2 chat protocol. Please ask the node administrator to update to the latest version.',
|
||||||
|
remoteError: errorData
|
||||||
|
}, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
error: 'Remote delivery failed',
|
error: 'Remote delivery failed',
|
||||||
details: errorData
|
details: errorData
|
||||||
|
|||||||
@@ -29,6 +29,49 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
|
console.log('[Swarm Inbox] Received body keys:', Object.keys(body), 'action:', body.action);
|
||||||
|
|
||||||
|
// Check if this is a V2 encrypted envelope (has 'action' and 'data' fields)
|
||||||
|
// MUST check BEFORE Zod validation to avoid validation errors
|
||||||
|
if (body.action === 'chat.deliver' && body.data) {
|
||||||
|
// V2 E2EE Message - store in chatInbox
|
||||||
|
const { recipientDid, recipientDeviceId, ciphertext } = body.data;
|
||||||
|
|
||||||
|
if (!recipientDid || !ciphertext) {
|
||||||
|
return NextResponse.json({ error: 'Invalid V2 payload' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find recipient by DID
|
||||||
|
const recipient = await db.query.users.findFirst({
|
||||||
|
where: eq(users.did, recipientDid)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!recipient) {
|
||||||
|
return NextResponse.json({ error: 'Recipient not found' }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import chatInbox schema
|
||||||
|
const { chatInbox } = await import('@/db/schema');
|
||||||
|
|
||||||
|
// Store in V2 inbox
|
||||||
|
await db.insert(chatInbox).values({
|
||||||
|
senderDid: body.did, // From signed envelope
|
||||||
|
recipientDid,
|
||||||
|
recipientDeviceId: recipientDeviceId || null,
|
||||||
|
envelope: JSON.stringify(body),
|
||||||
|
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`[Swarm Chat V2] Received encrypted message for ${recipientDid}`);
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
message: 'V2 message received',
|
||||||
|
version: 2
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// V1 Legacy Message Format - validate with Zod
|
||||||
const data = chatMessageSchema.parse(body) as SwarmChatMessagePayload;
|
const data = chatMessageSchema.parse(body) as SwarmChatMessagePayload;
|
||||||
|
|
||||||
// Find the recipient (local user)
|
// Find the recipient (local user)
|
||||||
|
|||||||
@@ -412,8 +412,14 @@ export function useChatEncryption() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!sendRes.ok) {
|
if (!sendRes.ok) {
|
||||||
const errorData = await sendRes.json().catch(() => ({ error: 'Unknown error' }));
|
const errorText = await sendRes.text();
|
||||||
console.error('[Chat] Send failed:', errorData);
|
let errorData;
|
||||||
|
try {
|
||||||
|
errorData = JSON.parse(errorText);
|
||||||
|
} catch {
|
||||||
|
errorData = { error: errorText };
|
||||||
|
}
|
||||||
|
console.error('[Chat] Send failed:', sendRes.status, errorData);
|
||||||
throw new Error(`Failed to send message: ${errorData.error || sendRes.statusText}`);
|
throw new Error(`Failed to send message: ${errorData.error || sendRes.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user