feat: add IPFS gateway route /ipfs/:cid

This commit is contained in:
maikrolf
2026-09-20 03:30:13 +02:00
parent 841f89b1d5
commit d0632d4dcc
+83
View File
@@ -0,0 +1,83 @@
/**
* IPFS Gateway Route — /ipfs/[cid]
*
* Haalt content op van KUBO via /api/v0/cat en served het als gateway.
* Ondersteunt:
* GET /ipfs/:cid — content ophalen
* GET /ipfs/:cid?filename= — met content-disposition header
*/
import { NextRequest, NextResponse } from 'next/server';
import { kuboApiUrl, kuboBasicAuth } from '@/lib/config';
export const dynamic = 'force-dynamic';
export async function GET(
req: NextRequest,
{ params }: { params: Promise<{ cid: string }> },
) {
const { cid } = await params;
// Valideer CID formaat (Qm... of bafy...)
if (!cid || (!cid.startsWith('Qm') && !cid.startsWith('bafy') && !cid.startsWith('bafk'))) {
return NextResponse.json({ error: 'Ongeldig CID formaat' }, { status: 400 });
}
const kuboSvc = kuboApiUrl();
if (!kuboSvc) {
return NextResponse.json({ error: 'KUBO niet geconfigureerd' }, { status: 501 });
}
try {
const headers: Record<string, string> = {};
const auth = kuboBasicAuth();
if (auth) {
headers['Authorization'] = 'Basic ' + Buffer.from(auth).toString('base64');
}
// Fetch content via KUBO /api/v0/cat
const response = await fetch(`${kuboSvc}/api/v0/cat?arg=${encodeURIComponent(cid)}`, {
method: 'POST',
headers,
signal: AbortSignal.timeout(30000),
});
if (!response.ok) {
const text = await response.text().catch(() => '');
return NextResponse.json(
{ error: `KUBO cat gefaald: ${response.status}`, detail: text.slice(0, 200) },
{ status: response.status },
);
}
// Bepaal content type op basis van CID prefix
const contentType = detectContentType(cid);
// Check of filename is opgegeven
const filename = req.nextUrl.searchParams.get('filename');
const headersOut: Record<string, string> = {
'Content-Type': contentType,
'Content-Disposition': filename
? `inline; filename="${filename}"`
: 'inline',
'Cache-Control': 'public, max-age=31536000, immutable',
};
const body = await response.arrayBuffer();
return new NextResponse(body, { headers: headersOut });
} catch (err) {
const msg = err instanceof Error ? err.message : 'Gateway fetch failed';
console.error(`[ipfs-gateway] Error fetching ${cid}:`, msg);
return NextResponse.json({ error: msg }, { status: 502 });
}
}
/**
* Detecteer content type op basis van CID prefix.
* IPFS content is meestal JSON, tenzij het een bekend formaat is.
*/
function detectContentType(cid: string): string {
// Voor nu: standaard octet-stream, KUBO bepaalt het echte type
return 'application/octet-stream';
}