feat: Phase 8 – Modul-SDK (createModuleServer, Config, Logger, Platform-Client)

This commit is contained in:
MPM Dev
2026-10-07 16:29:40 +02:00
parent 0abe7b7abc
commit e6219e8cf9
9 changed files with 1081 additions and 107 deletions

View File

@@ -0,0 +1,111 @@
# platform-module-sdk
Offizielles SDK für MPM-Module (Phase 8). Es verdrahtet den verbindlichen
Modul-API-Vertrag und stellt Authentifizierung, Konfiguration, Logging und
einen Plattform-API-Client bereit.
## Installation (Vendor-Pattern)
Module sind selbstständige ZIP-Pakete. Das SDK wird als **eine Datei**
(`index.js`) in das Modul-Paket kopiert (z. B. `backend/platform-module-sdk.js`)
und per `require` geladen. Dadurch bleiben Module ohne npm-Registry lauffähig.
```text
mein-modul/
├── module.json
├── backend/
│ ├── server.js # Einstiegspunkt (Manifest: entrypoint)
│ └── platform-module-sdk.js # vendored SDK (Kopie von index.js)
└── README.md
```
## Schnellstart
```js
'use strict';
const path = require('node:path');
const {
createModuleServer,
loadManifest,
createLogger,
} = require('./platform-module-sdk');
const manifest = loadManifest(path.join(__dirname, '..', 'module.json'));
const logger = createLogger({ moduleId: manifest.id });
const port = Number(process.env.PORT ?? manifest.port);
const server = createModuleServer({
manifest,
logger,
permissions: ['meinmodul.read'],
routes: (request, response, context) => {
// context.identity → { userId, username, displayName, role }
// context.path → URL-Pfad
if (request.method === 'GET' && context.path === '/aufgaben') {
response.writeHead(200, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ aufgaben: [] }));
return;
}
response.writeHead(404, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ statusCode: 404, message: 'Nicht gefunden' }));
},
});
server.listen(port, '127.0.0.1', () => {
logger.info(`Modul gestartet`, { port });
});
```
Der Server verdrahtet automatisch den Vertrag:
| Endpunkt | Verhalten |
|---|---|
| `GET /health` | 200 `{ moduleId, version, status: "healthy" }` |
| `GET /api/manifest` | 200 Manifest + Status |
| `GET /api/me` | 200 Identität vom Gateway (401 ohne Identität) |
| fachliche Routen | `options.routes(...)`; ohne Identität 401 (`identityRequired`) |
## API
### `extractIdentity(headers)`
Liest die Benutzer-Identität aus den Gateway-Headern (`x-user-id`,
`x-user-username`, `x-user-display-name`, `x-user-role`). Case-insensitive;
`null`, wenn der Request nicht über den Gateway kam.
### `loadManifest(manifestPath)`
Lädt und validiert `module.json` (Pflichtfelder, Runtime `node`, apiVersion `v1`).
### `loadModuleConfig(env?)`
Laufzeit-Konfiguration: `port` (ENV `PORT`), `nodeEnv`, `platformBaseUrl`
(ENV `PLATFORM_INTERNAL_URL`, Default `http://127.0.0.1:3000`), `serviceToken`.
### `createLogger({ moduleId, level })`
Strukturierte JSON-Logs (`debug/info/warn/error`). Sensible Schlüssel
(Passwort, Token, Secret, Cookie, …) werden automatisch geschwärzt.
### `createPlatformClient({ baseUrl, serviceToken, timeoutMs }))`
HTTP-Client für interne Aufrufe zur Plattform (`get/post/patch/delete`).
Wirft bei Nicht-2xx einen Fehler mit Statuscode.
### `createModuleServer(options)`
Siehe Schnellstart. Optionen: `manifest`, `routes`, `permissions`,
`logger`, `identityRequired` (Default `true` – sicherer Default).
## Tests
Die SDK-Tests laufen im Backend-Test-Suite
(`apps/platform-backend/test/platform-module-sdk.spec.ts`):
```bash
cd apps/platform-backend && npm test
```
## Sicherheitsregeln für Modul-Autoren
- Nur auf `127.0.0.1` lauschen (der Gateway ist der einzige Zugang).
- Identität ausschließlich über `extractIdentity` beziehen – niemals aus
URL, Body oder eigenen Headern vertrauen.
- Keine Plattform-Secrets erwarten: Der Prozess-Manager übergibt nur
`PATH`, `NODE_ENV` und `PORT`.
- Logs niemals Passwörter/Tokens/personenbezogene Daten schreiben lassen.