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:
@@ -1,7 +1,20 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
import { kuboApiUrl, kuboBasicAuth } from '@/lib/config';
|
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> {
|
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.
|
// In dev, the Kubo API is behind nginx with Basic Auth.
|
||||||
// If no KUBO_API_URL is configured, return a clear error.
|
// If no KUBO_API_URL is configured, return a clear error.
|
||||||
const kuboSvc = kuboApiUrl();
|
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 {
|
export function mapToKuboAPI(path: string, method: string): string {
|
||||||
// Normalize: /api/node/info → /api/v0/id, etc.
|
// Normalize: /api/node/info → /api/v0/id, etc.
|
||||||
const p = path.replace(/^\/+/, '').replace(/\/+$/, '');
|
const p = path.replace(/^\/+/, '').replace(/\/+$/, '');
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* Local pin storage — JSON file based
|
||||||
|
* Gebruikt wanneer KUBO_API_URL niet beschikbaar is (development/local).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { readFile, writeFile, mkdir } from 'fs/promises';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
const PIN_FILE = join(process.cwd(), '.data', 'pins.json');
|
||||||
|
|
||||||
|
interface PinRecord {
|
||||||
|
cid: string;
|
||||||
|
name: string;
|
||||||
|
size: number;
|
||||||
|
created: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureDir() {
|
||||||
|
const dir = join(process.cwd(), '.data');
|
||||||
|
await mkdir(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readPins(): Promise<Record<string, PinRecord>> {
|
||||||
|
try {
|
||||||
|
const data = await readFile(PIN_FILE, 'utf-8');
|
||||||
|
return JSON.parse(data);
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function writePins(pins: Record<string, PinRecord>): Promise<void> {
|
||||||
|
await ensureDir();
|
||||||
|
await writeFile(PIN_FILE, JSON.stringify(pins, null, 2), 'utf-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function localListPins(): Promise<PinRecord[]> {
|
||||||
|
const pins = await readPins();
|
||||||
|
return Object.values(pins);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function localAddPin(cid: string, name?: string, size: number = 0): Promise<{ cid: string }> {
|
||||||
|
const pins = await readPins();
|
||||||
|
pins[cid] = {
|
||||||
|
cid,
|
||||||
|
name: name || '',
|
||||||
|
size,
|
||||||
|
created: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
await writePins(pins);
|
||||||
|
return { cid };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function localRemovePin(cid: string): Promise<void> {
|
||||||
|
const pins = await readPins();
|
||||||
|
delete pins[cid];
|
||||||
|
await writePins(pins);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function localGetPinCount(): Promise<number> {
|
||||||
|
const pins = await readPins();
|
||||||
|
return Object.keys(pins).length;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user