feat: SIWE auth, auto-pin, pin search/share, explorer improvements

- SIWE server-side auth (siwe + jose JWT)
- Wallet provider fallback (MAOS -> MetaMask)
- Auto-pin after upload (quick + crypto)
- Pin search/filter in explorer
- Share button (gateway link) per pin
- Copy CID button per pin
- Breadcrumb root navigation fix
- Hydration mismatch fix (mounted state)
- Defensive truncateCid for empty values
- CSP fix (template literal)
- Error boundaries (upload, profile, usage)
- 191 tests passing
This commit is contained in:
maikrolf
2026-09-19 12:25:36 +02:00
parent f72b775379
commit 7c7854e08f
24 changed files with 1349 additions and 125 deletions
+52 -1
View File
@@ -9,8 +9,59 @@ export interface IPFSEntry {
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<IPFSEntry[]> {
return apiFetch(`/explorer/ls/${encodeURIComponent(cid)}`, { method: 'POST' });
const raw = await apiFetch<unknown>(`/explorer/ls/${encodeURIComponent(cid)}`, { method: 'POST' });
return parseKuboLs(raw);
}
export async function explorerCat(cid: string): Promise<string> {