diff --git a/src/app/ipfs/[cid]/route.ts b/src/app/ipfs/[cid]/route.ts new file mode 100644 index 0000000..13f5f54 --- /dev/null +++ b/src/app/ipfs/[cid]/route.ts @@ -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 = {}; + 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 = { + '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'; +}