/* ── IPFS Portal API — IPFS Explorer ── */ import { apiFetch, API_BASE } from './client'; export interface IPFSEntry { name: string; type: 'file' | 'dir'; size: number; hash: string; } /** Raw Kubo API /api/v0/ls response */ interface KuboLink { Name: string; Type: number; // 1 = directory, 2 = file Size: number; Hash: string; } interface KuboObject { Hash: string; Links: KuboLink[]; } interface KuboLsResponse { Objects: KuboObject[]; } /** Transform Kubo response to IPFSEntry[] */ function parseKuboLs(raw: unknown): IPFSEntry[] { // Already an array — return as-is if (Array.isArray(raw)) { return raw as IPFSEntry[]; } // Kubo format: { Objects: [{ Links: [...] }] } const data = raw as KuboLsResponse; if (data?.Objects?.[0]?.Links) { return data.Objects[0].Links.map((link) => ({ name: link.Name, type: link.Type === 1 ? 'dir' as const : 'file' as const, size: link.Size, hash: link.Hash, })); } // Single file — Kubo wraps it in Objects but no Links if (data?.Objects?.[0]?.Hash) { const obj = data.Objects[0]; return [{ name: obj.Hash.slice(0, 12) + '…', type: 'file', size: 0, hash: obj.Hash, }]; } // Empty or unrecognizable response return []; } export async function explorerLs(cid: string): Promise { const raw = await apiFetch(`/explorer/ls/${encodeURIComponent(cid)}`, { method: 'POST' }); return parseKuboLs(raw); } export async function explorerCat(cid: string): Promise { const res = await fetch(`${API_BASE}/api/explorer/cat/${encodeURIComponent(cid)}`, { method: 'POST', credentials: 'include', }); if (!res.ok) { const text = await res.text().catch(() => ''); throw new Error(`${res.status} ${res.statusText}: ${text.slice(0, 200)}`); } return res.text(); } export async function explorerStat(cid: string): Promise> { return apiFetch(`/explorer/stat/${encodeURIComponent(cid)}`, { method: 'POST' }); }