feat: add IPFS gateway route /ipfs/:cid
This commit is contained in:
@@ -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';
|
||||
}
|
||||
Reference in New Issue
Block a user