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/<cid> 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
This commit is contained in:
maikrolf
2026-10-01 21:53:08 +02:00
parent 8e1231a7af
commit bac719723d
+93 -26
View File
@@ -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/<CID>` |
| IPFS Gateway | `https://ipfs.maos.dedyn.io/ipfs/<CID>` |
---
@@ -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/<CID>-<naam>
→ 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/<cid>` 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/<cid>` 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: { <cid>: { Type } } }` |
| `POST /api/pins` | `handleLocalPins` | `localAddPin(cid, name, size)` |
| `DELETE /api/pins/<cid>` | `handleLocalPins` | `localRemovePin(cid)` |
| `POST /api/files/upload` | `handleLocalUpload` | `localSaveFile()` + auto-pin |
| `GET /api/files` | `handleLocalListFiles` | `localListFiles()` |
| `GET /api/files/<cid>` | `handleLocalGetFile` | Serveert `.data/files/<cid>-<naam>` als attachment |
| `DELETE /api/files/<cid>` | `handleLocalDeleteFile` | `localDeleteFile(cid)` |
### Opslaglayout
| Pad | Inhoud |
|---|---|
| `.data/pins.json` | `Record<cid, { cid, name, size, created }>` — `src/lib/local-pins.ts` |
| `.data/files/<cid>-<naam>` | De ruwe bestandsbytes — `src/lib/local-files.ts` |
| `.data/files.json` | `Record<cid, { cid, name, size, modified, path }>` |
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/<cid>` — 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
`<NEXT_PUBLIC_GATEWAY_URL>/ipfs/<cid>`, wat precies dit pad volgt.
| Request | Gedrag |
|---|---|
| `GET /ipfs/<cid>` | Valideert het CID-prefix (`Qm`/`bafy`/`bafk`), daarna `POST <KUBO_API_URL>/api/v0/cat?arg=<cid>`, met Basic auth als `KUBO_BASIC_AUTH` gezet is |
| `GET /ipfs/<cid>?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)