feat: lokale pin opslag zonder Kubo

- JSON-file opslag in .data/pins.json
- Gebruikt wanneer KUBO_API_URL niet beschikbaar is
- Lokale pins voor development, Kubo voor productie
- 191 tests passing
This commit is contained in:
maikrolf
2026-09-19 17:04:44 +02:00
parent 361f11033d
commit c64ca2b8c2
2 changed files with 114 additions and 0 deletions
+51
View File
@@ -1,7 +1,20 @@
import { NextRequest, NextResponse } from 'next/server';
import { kuboApiUrl, kuboBasicAuth } from '@/lib/config';
import { localListPins, localAddPin, localRemovePin } from '@/lib/local-pins';
export async function proxyIPFS(path: string, method: string, req: NextRequest): Promise<Response> {
// Normalize path
const p = path.replace(/^\/+/, '').replace(/\/+$/, '');
// ── Lokale pin opslag (zonder Kubo) ──
// Wanneer KUBO_API_URL niet beschikbaar is, gebruik lokale JSON file
if (p === 'pins' || p.startsWith('pins/')) {
const kuboSvc = kuboApiUrl();
if (!kuboSvc) {
return handleLocalPins(p, method, req);
}
}
// In dev, the Kubo API is behind nginx with Basic Auth.
// If no KUBO_API_URL is configured, return a clear error.
const kuboSvc = kuboApiUrl();
@@ -75,6 +88,44 @@ export async function proxyIPFS(path: string, method: string, req: NextRequest):
});
}
/** Lokale pin handler — werkt zonder Kubo API */
async function handleLocalPins(path: string, method: string, req: NextRequest): Promise<Response> {
const p = path.replace(/^\/+/, '').replace(/\/+$/, '');
// GET /api/pins — lijst alle pins
if (p === 'pins' && method === 'GET') {
const pins = await localListPins();
// Retourneer in Kubo-formaat voor backwards compatibiliteit
const Keys: Record<string, { Type: string }> = {};
for (const pin of pins) {
Keys[pin.cid] = { Type: pin.created || 'indirect' };
}
return NextResponse.json({ Keys });
}
// POST /api/pins — pin een CID
if (p === 'pins' && method === 'POST') {
try {
const body = await req.json();
const { cid, name, size } = body;
if (!cid) return NextResponse.json({ error: 'cid required' }, { status: 400 });
await localAddPin(cid, name, size || 0);
return NextResponse.json({ cid });
} catch {
return NextResponse.json({ error: 'Invalid request' }, { status: 400 });
}
}
// DELETE /api/pins/<cid> — verwijder pin
if (p.startsWith('pins/') && method === 'DELETE') {
const cid = p.slice(5);
await localRemovePin(cid);
return NextResponse.json({ ok: true });
}
return NextResponse.json({ error: 'Not found' }, { status: 404 });
}
export function mapToKuboAPI(path: string, method: string): string {
// Normalize: /api/node/info → /api/v0/id, etc.
const p = path.replace(/^\/+/, '').replace(/\/+$/, '');