From bac719723d763945d3926881d9e878e6eb2d0f18 Mon Sep 17 00:00:00 2001 From: maikrolf <40924921+maikrolf@users.noreply.github.com> Date: Thu, 1 Oct 2026 21:53:08 +0200 Subject: [PATCH] docs: ARCHITECTURE.md bijwerken naar de huidige generatie - Gateway-host op regel 18 en in Configuration: maos.dedyn.io -> ipfs.maos.dedyn.io (config.ts gatewayUrl() en de CSP in next.config.ts) - Upload-flow (quick + crypto): proxyIPFS -> handleLocalUpload() -> .data/files/ met auto-pin, i.p.v. Kubo /api/v0/add - Nieuwe sectie 'Lokale opslag (.data/) en de gateway-route': endpoint-tabel voor pins/files, opslaglayout, het feit dat een lokale CID geen echte IPFS-CID is, en GET /ipfs/ inclusief content-type, Content-Disposition en foutpaden - src/lib/api.ts (bestaat niet meer) -> src/lib/api/ als module-tabel; het achtergebleven api.ts.bak benoemd - route.ts: ~320 -> 121 regels, met verwijzing naar proxy-ipfs.ts en write-auth.ts - mapToKuboAPI-lijst: dode mappings /pins en /files/upload weg, met de reden - Pages-tabel en API-flow noemden pins/files nog als Kubo-afhankelijk - Schema en het bestaande write-auth-gedeelte ongemoeid - Warnt expliciet dat .data/ nog niet in .gitignore staat --- ARCHITECTURE.md | 119 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 26 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index c50f444..e07b8b5 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -15,7 +15,7 @@ IPFS Portal is a **Next.js SPA** (Single Page Application) that provides a brows | Blockchain | zkSync Local (chain 270) | | IPFS | Kubo RPC API (port 5001) | | User Mgmt | Python backend (port 8444) | -| IPFS Gateway | `https://maos.dedyn.io/ipfs/` | +| IPFS Gateway | `https://ipfs.maos.dedyn.io/ipfs/` | --- @@ -41,6 +41,7 @@ IPFS Portal is a **Next.js SPA** (Single Page Application) that provides a brows │ 'health' → inline JSON │ │ 'users' → proxyUserAPI() → :8444 (Python) │ │ default → proxyIPFS() → :5001 (Kubo) │ + │ pins/files → lokale .data-opslag (nooit Kubo) │ │ │ │ mapToKuboAPI(path) → /api/v0/* translation │ └─────────────────────┬───────────────────────────────┘ @@ -80,13 +81,13 @@ IPFS Portal is a **Next.js SPA** (Single Page Application) that provides a brows | Page | Route | Lines | Backend Dependencies | Description | |---|---|---|---|---| | Landing | `/` | ~130 | Kubo (health) | Hero + connect wallet + quick stats | -| Dashboard | `/dashboard` | 253 | Kubo (peers, pins, health, repo, bw) | Node overview, stats, bandwidth | -| Upload | `/upload` | **590** | Kubo + Smart Contract | Quick + crypto file upload | -| Explorer | `/explorer` | 244 | Kubo (ls, cat) | IPFS content browser | +| Dashboard | `/dashboard` | 253 | Kubo (peers, repo/stats), inline health, lokale pins | Node overview, stats, bandwidth | +| Upload | `/upload` | **590** | Lokale `.data`-opslag + Smart Contract | Quick + crypto file upload | +| Explorer | `/explorer` | 244 | Kubo (ls, cat) + lokale pins | IPFS content browser | | IPNS | `/ipns` | 285 | Kubo (keys, publish, resolve) | IPNS name management | | Users | `/users` | 275 | Python API + Smart Contract | htpasswd users + wallet lookup | | Peers | `/peers` | 63 | Kubo (swarm/peers) | Connected peers list | -| Pins | `/pins` | 167 | Kubo (pin/ls, add, rm) | Pin management | +| Pins | `/pins` | 167 | Lokale `.data/pins.json` | Pin management | | Admin/Payment | `/admin/payment` | **536** | Smart Contract | Owner payment dashboard | | Settings | `/settings` | 319 | localStorage only | App configuration | | History | `/history` | 393 | localStorage only | Upload history viewer | @@ -105,7 +106,8 @@ Page Component → fetch('/api/endpoint', { credentials: 'include' }) → Next.js [...path]/route.ts → matchRoute(path) - → proxyIPFS() → Kubo RPC :5001 + → proxyIPFS() → Kubo RPC :5001 (explorer/*, ipns/*, …) + → proxyIPFS() → lokale .data-opslag (pins, files) → proxyUserAPI() → Python :8444 ``` @@ -116,7 +118,8 @@ User drags file → UploadPage state: FileEntry[] → startUpload() — sequential per file → uploadFile(file) → apiFetch POST /api/files/upload (FormData) - → proxyIPFS → Kubo /api/v0/add + → proxyIPFS → handleLocalUpload() → .data/files/- + → daarna auto-pin in .data/pins.json → addToHistory({ cid, name, size }) → localStorage → Show results (CID, gateway link, copy) ``` @@ -131,7 +134,7 @@ User selects file → PaymentService.getSupportedTokens() → [ETH, USDC, MAOS] → PaymentService.getMAOSDiscountTiers() → discount % → User pays - → Step 1: uploadFile(file) → Kubo → get CID + → Step 1: uploadFile(file) → lokale .data-opslag → get CID → Step 2a (free): onPaid({ method: 'free' }) → Step 2b (ETH): payWithETH(provider, cid, size, wei) → sendTransaction → contract.payWithETH(cid, bytesSize) @@ -156,21 +159,24 @@ Admin connects wallet ## Key Modules -### `src/lib/api.ts` (217 lines) +### `src/lib/api/` (8 modules, barrel via `index.ts`) -Central API layer. Exports: +Central API layer, opgesplitst per domein. `apiFetch()` (in `client.ts`) doet de +fetch met `credentials: 'include'`; alle modules daaromheen zijn één regel per +endpoint. -| Function | HTTP | Endpoint | Category | -|---|---|---|---| -| `getNodeInfo()` | GET | `/api/node/info` | Node | -| `getPeers()` | GET | `/api/peers` | Peers | -| `listPins()` / `addPin()` / `removePin()` | GET/POST/DELETE | `/api/pins` | Pins | -| `uploadFile(file)` | POST (FormData) | `/api/files/upload` | Files | -| `listUsers()` / `createUser()` / `deleteUser()` | GET/POST/DELETE | `/api/users` | Users | -| `explorerLs()` / `explorerCat()` / `explorerStat()` | POST | `/api/explorer/*` | Explorer | -| `listIPNSKeys()` / `ipnsPublish()` / `ipnsResolve()` / `ipnsGenKey()` | GET/POST | `/api/ipns/*` | IPNS | -| `getRepoStats()` / `getBWStats()` | GET | `/api/repo` / `/api/bw` | Stats | -| `checkHealth()` | GET | `/api/health` | Health | +| Module | Exporteert | Endpoint | +|---|---|---| +| `client.ts` | `apiFetch()` + gedeelde types | — | +| `gateway.ts` | `getNodeInfo()`, `getPeers()`, `getRepoStats()`, `getBWStats()`, `checkHealth()` | `/api/node/info`, `/api/peers`, `/api/repo/stats`, `/api/bw/stats`, `/api/health` | +| `pins.ts` | `listPins()` / `addPin()` / `removePin()` | `/api/pins` (lokaal) | +| `files.ts` | `uploadFile()`, `listFiles()` | `/api/files/upload`, `/api/files` (lokaal) | +| `users.ts` | `listUsers()` / `createUser()` / `deleteUser()` | `/api/users` (Python) | +| `explorer.ts` | `explorerLs()` / `explorerCat()` / `explorerStat()` | `/api/explorer/*` (Kubo) | +| `ipns.ts` | `listIPNSKeys()` / `ipnsPublish()` / `ipnsResolve()` / `ipnsGenKey()` | `/api/ipns/*` (Kubo) | + +`src/lib/api.ts.bak` is een achtergebleven backup van het oude monolitische +`api.ts` en wordt nergens geïmporteerd. ### `src/lib/wallet.ts` (196 lines) @@ -200,7 +206,7 @@ localStorage persistence. - `PortalSettings`: gateway URL, API endpoint, storage max, refresh, theme - `UploadRecord`: history with dedup by CID+name, capped at 500 entries -### `src/app/api/[...path]/route.ts` (~320 lines) +### `src/app/api/[...path]/route.ts` (121 lines) Catch-all API proxy. Routes: @@ -208,18 +214,79 @@ Catch-all API proxy. Routes: |---|---|---| | `/api/health` | `handleHealth()` | Inline JSON | | `/api/users*` | `proxyUserAPI()` | Python :8444 | +| `/api/pins*`, `/api/files*` | `proxyIPFS()` → lokale handlers | `.data` op schijf | | `/api/explorer/*` / `/api/ipns/*` / default | `proxyIPFS()` | Kubo :5001 | -**`mapToKuboAPI(path)`** translates Portal API paths to Kubo RPC endpoints: +De proxy zelf is 121 regels; de Kubo-vertaling staat in `proxy-ipfs.ts` en de +write-gate in `write-auth.ts`. + +**`mapToKuboAPI(path, method)`** in `proxy-ipfs.ts` translates Portal API paths to Kubo RPC endpoints: - `/node/info` → `/api/v0/id` - `/peers` → `/api/v0/swarm/peers` -- `/pins` → `/api/v0/pin/ls|add|rm` -- `/files/upload` → `/api/v0/add` - `/explorer/ls` → `/api/v0/ls` - `/ipns/publish` → `/api/v0/name/publish` - `/repo/stats` → `/api/v0/repo/stat` - `/bw/stats` → `/api/v0/bw` +`pins`, `pins/` en `files/upload` hebben **geen** mapping: `proxyIPFS()` +onderschept die paden vóór de Kubo-vertaling en serveert ze lokaal (zie onder). +Voeg hier dus geen `pin/*`- of `add`-tak toe zonder `proxyIPFS()` en +`isMutatingRequest()` mee te wijzigen. + +--- + +## Lokale opslag (`.data/`) en de gateway-route + +### Pins en files gaan niet naar Kubo + +`proxyIPFS()` (`src/app/api/[...path]/proxy-ipfs.ts`) behandelt `pins`, +`pins/` en `files/*` volledig zelf. Deze paden bereiken de Kubo RPC nooit, +ongeacht of `KUBO_API_URL` wel of niet gezet is — de lokale opslag werkt dus +ook zonder Kubo. + +| Portal-endpoint | Handler | Effect | +|---|---|---| +| `GET /api/pins` | `handleLocalPins` | Leest `.data/pins.json` → Kubo-vorm `{ Keys: { : { Type } } }` | +| `POST /api/pins` | `handleLocalPins` | `localAddPin(cid, name, size)` | +| `DELETE /api/pins/` | `handleLocalPins` | `localRemovePin(cid)` | +| `POST /api/files/upload` | `handleLocalUpload` | `localSaveFile()` + auto-pin | +| `GET /api/files` | `handleLocalListFiles` | `localListFiles()` | +| `GET /api/files/` | `handleLocalGetFile` | Serveert `.data/files/-` als attachment | +| `DELETE /api/files/` | `handleLocalDeleteFile` | `localDeleteFile(cid)` | + +### Opslaglayout + +| Pad | Inhoud | +|---|---| +| `.data/pins.json` | `Record` — `src/lib/local-pins.ts` | +| `.data/files/-` | De ruwe bestandsbytes — `src/lib/local-files.ts` | +| `.data/files.json` | `Record` | + +De "CID" van een lokaal opgeslagen bestand is geen echte IPFS-CID maar +`"Qm" + sha256(inhoud).slice(0, 44)` (`generateCid()` in `local-files.ts`). +Content is daarmee adresseerbaar binnen deze portal, maar niet op een +ander IPFS-netwerk. `.data/` is runtime-state: het ontstaat bij het eerst +uploaden en moet in een deploy een volume zijn, want een rebuild wist hem anders +leeg. ⚠️ `.data/` staat op dit moment **niet** in `.gitignore` — voeg die regel +toe voordat er per ongeluk runtime-state gecommit wordt. + +### `GET /ipfs/` — de gateway-route + +`src/app/ipfs/[cid]/route.ts` is de gateway die deze share-links bedient. +`gatewayLink()` in `src/lib/helpers.ts` bouwt alle deel-links als +`/ipfs/`, wat precies dit pad volgt. + +| Request | Gedrag | +|---|---| +| `GET /ipfs/` | Valideert het CID-prefix (`Qm`/`bafy`/`bafk`), daarna `POST /api/v0/cat?arg=`, met Basic auth als `KUBO_BASIC_AUTH` gezet is | +| `GET /ipfs/?filename=…` | Zet `Content-Disposition: inline; filename="…"` | + +De response draagt het `Content-Type` dat Kubo terugstuurt, met +`application/octet-stream` als fallback als upstream niets bruikbaars stuurt. +De body wordt volledig in geheugen geladen (`arrayBuffer()`) — voor grote +bestanden is dat nog geen streaming. Foutpaden: `400` (ongeldig CID), `501` +(zonder `KUBO_API_URL`), de Kubo-status zelf, of `502` bij een netwerkfout. + --- ## Write authentication @@ -351,7 +418,7 @@ Appears as 3 disconnected communities in the graph but is one continuous flow at - **Kubo RPC**: `http://127.0.0.1:5001` - **Python API**: `http://192.168.1.176:8444` (htpasswd + admin key) - **Smart Contract**: `0xCBc6...` on chain 270 (zkSync Local) -- **IPFS Gateway**: `https://maos.dedyn.io/ipfs/` +- **IPFS Gateway**: `https://ipfs.maos.dedyn.io/ipfs/` - **Refresh Interval**: 15s (Dashboard) - **Max Uploads**: 20 files, 100MB each - **Storage Max**: 30 GB (localStorage setting)