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:
+93
-26
@@ -15,7 +15,7 @@ IPFS Portal is a **Next.js SPA** (Single Page Application) that provides a brows
|
|||||||
| Blockchain | zkSync Local (chain 270) |
|
| Blockchain | zkSync Local (chain 270) |
|
||||||
| IPFS | Kubo RPC API (port 5001) |
|
| IPFS | Kubo RPC API (port 5001) |
|
||||||
| User Mgmt | Python backend (port 8444) |
|
| 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 │
|
│ 'health' → inline JSON │
|
||||||
│ 'users' → proxyUserAPI() → :8444 (Python) │
|
│ 'users' → proxyUserAPI() → :8444 (Python) │
|
||||||
│ default → proxyIPFS() → :5001 (Kubo) │
|
│ default → proxyIPFS() → :5001 (Kubo) │
|
||||||
|
│ pins/files → lokale .data-opslag (nooit Kubo) │
|
||||||
│ │
|
│ │
|
||||||
│ mapToKuboAPI(path) → /api/v0/* translation │
|
│ 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 |
|
| Page | Route | Lines | Backend Dependencies | Description |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| Landing | `/` | ~130 | Kubo (health) | Hero + connect wallet + quick stats |
|
| Landing | `/` | ~130 | Kubo (health) | Hero + connect wallet + quick stats |
|
||||||
| Dashboard | `/dashboard` | 253 | Kubo (peers, pins, health, repo, bw) | Node overview, stats, bandwidth |
|
| Dashboard | `/dashboard` | 253 | Kubo (peers, repo/stats), inline health, lokale pins | Node overview, stats, bandwidth |
|
||||||
| Upload | `/upload` | **590** | Kubo + Smart Contract | Quick + crypto file upload |
|
| Upload | `/upload` | **590** | Lokale `.data`-opslag + Smart Contract | Quick + crypto file upload |
|
||||||
| Explorer | `/explorer` | 244 | Kubo (ls, cat) | IPFS content browser |
|
| Explorer | `/explorer` | 244 | Kubo (ls, cat) + lokale pins | IPFS content browser |
|
||||||
| IPNS | `/ipns` | 285 | Kubo (keys, publish, resolve) | IPNS name management |
|
| IPNS | `/ipns` | 285 | Kubo (keys, publish, resolve) | IPNS name management |
|
||||||
| Users | `/users` | 275 | Python API + Smart Contract | htpasswd users + wallet lookup |
|
| Users | `/users` | 275 | Python API + Smart Contract | htpasswd users + wallet lookup |
|
||||||
| Peers | `/peers` | 63 | Kubo (swarm/peers) | Connected peers list |
|
| 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 |
|
| Admin/Payment | `/admin/payment` | **536** | Smart Contract | Owner payment dashboard |
|
||||||
| Settings | `/settings` | 319 | localStorage only | App configuration |
|
| Settings | `/settings` | 319 | localStorage only | App configuration |
|
||||||
| History | `/history` | 393 | localStorage only | Upload history viewer |
|
| History | `/history` | 393 | localStorage only | Upload history viewer |
|
||||||
@@ -105,7 +106,8 @@ Page Component
|
|||||||
→ fetch('/api/endpoint', { credentials: 'include' })
|
→ fetch('/api/endpoint', { credentials: 'include' })
|
||||||
→ Next.js [...path]/route.ts
|
→ Next.js [...path]/route.ts
|
||||||
→ matchRoute(path)
|
→ matchRoute(path)
|
||||||
→ proxyIPFS() → Kubo RPC :5001
|
→ proxyIPFS() → Kubo RPC :5001 (explorer/*, ipns/*, …)
|
||||||
|
→ proxyIPFS() → lokale .data-opslag (pins, files)
|
||||||
→ proxyUserAPI() → Python :8444
|
→ proxyUserAPI() → Python :8444
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -116,7 +118,8 @@ User drags file
|
|||||||
→ UploadPage state: FileEntry[]
|
→ UploadPage state: FileEntry[]
|
||||||
→ startUpload() — sequential per file
|
→ startUpload() — sequential per file
|
||||||
→ uploadFile(file) → apiFetch POST /api/files/upload (FormData)
|
→ 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
|
→ addToHistory({ cid, name, size }) → localStorage
|
||||||
→ Show results (CID, gateway link, copy)
|
→ Show results (CID, gateway link, copy)
|
||||||
```
|
```
|
||||||
@@ -131,7 +134,7 @@ User selects file
|
|||||||
→ PaymentService.getSupportedTokens() → [ETH, USDC, MAOS]
|
→ PaymentService.getSupportedTokens() → [ETH, USDC, MAOS]
|
||||||
→ PaymentService.getMAOSDiscountTiers() → discount %
|
→ PaymentService.getMAOSDiscountTiers() → discount %
|
||||||
→ User pays
|
→ 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 2a (free): onPaid({ method: 'free' })
|
||||||
→ Step 2b (ETH): payWithETH(provider, cid, size, wei)
|
→ Step 2b (ETH): payWithETH(provider, cid, size, wei)
|
||||||
→ sendTransaction → contract.payWithETH(cid, bytesSize)
|
→ sendTransaction → contract.payWithETH(cid, bytesSize)
|
||||||
@@ -156,21 +159,24 @@ Admin connects wallet
|
|||||||
|
|
||||||
## Key Modules
|
## 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 |
|
| Module | Exporteert | Endpoint |
|
||||||
|---|---|---|---|
|
|---|---|---|
|
||||||
| `getNodeInfo()` | GET | `/api/node/info` | Node |
|
| `client.ts` | `apiFetch()` + gedeelde types | — |
|
||||||
| `getPeers()` | GET | `/api/peers` | Peers |
|
| `gateway.ts` | `getNodeInfo()`, `getPeers()`, `getRepoStats()`, `getBWStats()`, `checkHealth()` | `/api/node/info`, `/api/peers`, `/api/repo/stats`, `/api/bw/stats`, `/api/health` |
|
||||||
| `listPins()` / `addPin()` / `removePin()` | GET/POST/DELETE | `/api/pins` | Pins |
|
| `pins.ts` | `listPins()` / `addPin()` / `removePin()` | `/api/pins` (lokaal) |
|
||||||
| `uploadFile(file)` | POST (FormData) | `/api/files/upload` | Files |
|
| `files.ts` | `uploadFile()`, `listFiles()` | `/api/files/upload`, `/api/files` (lokaal) |
|
||||||
| `listUsers()` / `createUser()` / `deleteUser()` | GET/POST/DELETE | `/api/users` | Users |
|
| `users.ts` | `listUsers()` / `createUser()` / `deleteUser()` | `/api/users` (Python) |
|
||||||
| `explorerLs()` / `explorerCat()` / `explorerStat()` | POST | `/api/explorer/*` | Explorer |
|
| `explorer.ts` | `explorerLs()` / `explorerCat()` / `explorerStat()` | `/api/explorer/*` (Kubo) |
|
||||||
| `listIPNSKeys()` / `ipnsPublish()` / `ipnsResolve()` / `ipnsGenKey()` | GET/POST | `/api/ipns/*` | IPNS |
|
| `ipns.ts` | `listIPNSKeys()` / `ipnsPublish()` / `ipnsResolve()` / `ipnsGenKey()` | `/api/ipns/*` (Kubo) |
|
||||||
| `getRepoStats()` / `getBWStats()` | GET | `/api/repo` / `/api/bw` | Stats |
|
|
||||||
| `checkHealth()` | GET | `/api/health` | Health |
|
`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)
|
### `src/lib/wallet.ts` (196 lines)
|
||||||
|
|
||||||
@@ -200,7 +206,7 @@ localStorage persistence.
|
|||||||
- `PortalSettings`: gateway URL, API endpoint, storage max, refresh, theme
|
- `PortalSettings`: gateway URL, API endpoint, storage max, refresh, theme
|
||||||
- `UploadRecord`: history with dedup by CID+name, capped at 500 entries
|
- `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:
|
Catch-all API proxy. Routes:
|
||||||
|
|
||||||
@@ -208,18 +214,79 @@ Catch-all API proxy. Routes:
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `/api/health` | `handleHealth()` | Inline JSON |
|
| `/api/health` | `handleHealth()` | Inline JSON |
|
||||||
| `/api/users*` | `proxyUserAPI()` | Python :8444 |
|
| `/api/users*` | `proxyUserAPI()` | Python :8444 |
|
||||||
|
| `/api/pins*`, `/api/files*` | `proxyIPFS()` → lokale handlers | `.data` op schijf |
|
||||||
| `/api/explorer/*` / `/api/ipns/*` / default | `proxyIPFS()` | Kubo :5001 |
|
| `/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`
|
- `/node/info` → `/api/v0/id`
|
||||||
- `/peers` → `/api/v0/swarm/peers`
|
- `/peers` → `/api/v0/swarm/peers`
|
||||||
- `/pins` → `/api/v0/pin/ls|add|rm`
|
|
||||||
- `/files/upload` → `/api/v0/add`
|
|
||||||
- `/explorer/ls` → `/api/v0/ls`
|
- `/explorer/ls` → `/api/v0/ls`
|
||||||
- `/ipns/publish` → `/api/v0/name/publish`
|
- `/ipns/publish` → `/api/v0/name/publish`
|
||||||
- `/repo/stats` → `/api/v0/repo/stat`
|
- `/repo/stats` → `/api/v0/repo/stat`
|
||||||
- `/bw/stats` → `/api/v0/bw`
|
- `/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
|
## 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`
|
- **Kubo RPC**: `http://127.0.0.1:5001`
|
||||||
- **Python API**: `http://192.168.1.176:8444` (htpasswd + admin key)
|
- **Python API**: `http://192.168.1.176:8444` (htpasswd + admin key)
|
||||||
- **Smart Contract**: `0xCBc6...` on chain 270 (zkSync Local)
|
- **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)
|
- **Refresh Interval**: 15s (Dashboard)
|
||||||
- **Max Uploads**: 20 files, 100MB each
|
- **Max Uploads**: 20 files, 100MB each
|
||||||
- **Storage Max**: 30 GB (localStorage setting)
|
- **Storage Max**: 30 GB (localStorage setting)
|
||||||
|
|||||||
Reference in New Issue
Block a user