Enhance chat key lookup with remote and cache fallback
The GET /api/chat/keys endpoint now checks a remote identity cache and, if necessary, fetches keys from remote nodes or Swarm user profiles when a local key is not found. Results from remote lookups are cached for 24 hours to improve performance and reliability.
This commit is contained in:
+118
-11
@@ -1,8 +1,8 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db } from '@/db';
|
||||
import { chatDeviceBundles } from '@/db/schema';
|
||||
import { chatDeviceBundles, handleRegistry, remoteIdentityCache } from '@/db/schema';
|
||||
import { requireAuth } from '@/lib/auth';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import { eq, and, gt } from 'drizzle-orm';
|
||||
|
||||
/**
|
||||
* GET /api/chat/keys?did=<did>
|
||||
@@ -23,7 +23,7 @@ export async function GET(request: NextRequest) {
|
||||
where: eq(chatDeviceBundles.did, did),
|
||||
});
|
||||
|
||||
console.log('[Chat Keys GET] Found bundle:', bundle ? 'YES' : 'NO');
|
||||
console.log('[Chat Keys GET] Found local bundle:', bundle ? 'YES' : 'NO');
|
||||
if (bundle) {
|
||||
console.log('[Chat Keys GET] Bundle data:', {
|
||||
userId: bundle.userId,
|
||||
@@ -31,16 +31,123 @@ export async function GET(request: NextRequest) {
|
||||
deviceId: bundle.deviceId,
|
||||
hasIdentityKey: !!bundle.identityKey,
|
||||
});
|
||||
|
||||
// For libsodium, we just need the public key
|
||||
return NextResponse.json({
|
||||
publicKey: bundle.identityKey,
|
||||
});
|
||||
}
|
||||
|
||||
if (!bundle) {
|
||||
return NextResponse.json({ error: 'No keys found' }, { status: 404 });
|
||||
}
|
||||
|
||||
// For libsodium, we just need the public key
|
||||
return NextResponse.json({
|
||||
publicKey: bundle.identityKey, // Reusing identityKey field for libsodium public key
|
||||
// If not found locally, check remote identity cache
|
||||
const cached = await db.query.remoteIdentityCache.findFirst({
|
||||
where: and(
|
||||
eq(remoteIdentityCache.did, did),
|
||||
gt(remoteIdentityCache.expiresAt, new Date())
|
||||
),
|
||||
});
|
||||
|
||||
if (cached) {
|
||||
console.log('[Chat Keys GET] Found cached remote key');
|
||||
return NextResponse.json({
|
||||
publicKey: cached.publicKey,
|
||||
});
|
||||
}
|
||||
|
||||
// If not in cache, try to resolve DID to a node
|
||||
console.log('[Chat Keys GET] resolving DID to node...');
|
||||
const handleEntry = await db.query.handleRegistry.findFirst({
|
||||
where: eq(handleRegistry.did, did),
|
||||
});
|
||||
|
||||
if (handleEntry) {
|
||||
const { nodeDomain } = handleEntry;
|
||||
console.log('[Chat Keys GET] Resolved to node:', nodeDomain);
|
||||
|
||||
// Fetch from remote node
|
||||
const remoteUrl = `https://${nodeDomain}/api/chat/keys?did=${encodeURIComponent(did)}`;
|
||||
console.log('[Chat Keys GET] Fetching from remote:', remoteUrl);
|
||||
|
||||
try {
|
||||
const remoteRes = await fetch(remoteUrl);
|
||||
if (remoteRes.ok) {
|
||||
const data = await remoteRes.json();
|
||||
if (data.publicKey) {
|
||||
console.log('[Chat Keys GET] Successfully fetched remote key');
|
||||
|
||||
// Cache it
|
||||
await db.insert(remoteIdentityCache).values({
|
||||
did: did,
|
||||
publicKey: data.publicKey,
|
||||
fetchedAt: new Date(),
|
||||
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours
|
||||
}).onConflictDoUpdate({
|
||||
target: remoteIdentityCache.did,
|
||||
set: {
|
||||
publicKey: data.publicKey,
|
||||
fetchedAt: new Date(),
|
||||
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
publicKey: data.publicKey,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.warn('[Chat Keys GET] Remote fetch failed:', remoteRes.status, remoteRes.statusText);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Chat Keys GET] Remote fetch error:', err);
|
||||
}
|
||||
|
||||
// FALLBACK: Try fetching from Swarm User Profile (standard endpoint)
|
||||
if (!cached) {
|
||||
console.log('[Chat Keys GET] Trying fallback to Swarm Profile...');
|
||||
const swarmUrl = `https://${nodeDomain}/api/swarm/users/${handleEntry.handle}`;
|
||||
console.log('[Chat Keys GET] Fetching Swarm Profile:', swarmUrl);
|
||||
|
||||
try {
|
||||
const swarmRes = await fetch(swarmUrl);
|
||||
if (swarmRes.ok) {
|
||||
const data = await swarmRes.json();
|
||||
const chatKey = data.profile?.chatPublicKey;
|
||||
|
||||
if (chatKey) {
|
||||
console.log('[Chat Keys GET] Found key in Swarm Profile');
|
||||
|
||||
// Cache it
|
||||
await db.insert(remoteIdentityCache).values({
|
||||
did: did,
|
||||
publicKey: chatKey,
|
||||
fetchedAt: new Date(),
|
||||
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours
|
||||
}).onConflictDoUpdate({
|
||||
target: remoteIdentityCache.did,
|
||||
set: {
|
||||
publicKey: chatKey,
|
||||
fetchedAt: new Date(),
|
||||
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
publicKey: chatKey,
|
||||
});
|
||||
} else {
|
||||
console.warn('[Chat Keys GET] Swarm Profile found but no chatPublicKey');
|
||||
}
|
||||
} else {
|
||||
console.warn('[Chat Keys GET] Swarm Profile fetch failed:', swarmRes.status);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Chat Keys GET] Swarm Profile fetch error:', err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('[Chat Keys GET] DID not found in handle registry');
|
||||
}
|
||||
|
||||
return NextResponse.json({ error: 'No keys found' }, { status: 404 });
|
||||
} catch (error: any) {
|
||||
console.error('[Chat Keys] Failed to fetch keys:', error);
|
||||
return NextResponse.json({ error: error.message }, { status: 500 });
|
||||
@@ -58,7 +165,7 @@ export async function GET(request: NextRequest) {
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const user = await requireAuth();
|
||||
|
||||
|
||||
console.log('[Chat Keys POST] User:', user.handle, 'DID:', user.did, 'UserID:', user.id);
|
||||
|
||||
const body = await request.json();
|
||||
|
||||
Reference in New Issue
Block a user