This commit is contained in:
+51
-23
@@ -29,14 +29,15 @@ https://wegwichtel.example.org/api
|
||||
- Listen- und Metadatenzugriffe liefern JSON.
|
||||
- Die Einzelendpunkte für GPX, Bilder und Audio liefern die jeweilige Datei direkt aus. Mit `?metadata=true` liefern die Bild- und Audioendpunkte stattdessen JSON-Metadaten.
|
||||
- Schreibzugriffe ohne Dateien akzeptieren `application/json`, `application/x-www-form-urlencoded` oder `multipart/form-data`.
|
||||
- GPX-Dateien werden weiterhin ausschließlich als `multipart/form-data` übertragen.
|
||||
- GPX-Dateien werden ausschließlich als `multipart/form-data` übertragen.
|
||||
- Bild- und Audioendpunkte akzeptieren wahlweise `multipart/form-data` oder ein JSON-Objekt mit Base64-kodierten Dateidaten.
|
||||
- Der Server ermittelt den tatsächlichen MIME-Typ aus dem Dateiinhalt. Dateiname, Dateiendung, Multipart-`Content-Type`, JSON-Felder und Data-URL-Präfixe werden nicht als Typnachweis verwendet.
|
||||
- Für JSON wird `Content-Type: application/json` empfohlen. `Content-Type: text/json` wird aus Kompatibilitätsgründen ebenfalls akzeptiert.
|
||||
- Ein leeres JSON-Objekt (`{}`) enthält keine Datei und kann deshalb keinen Bild- oder Audio-Upload ausführen.
|
||||
- Pro Medien-Request wird genau eine Bild- beziehungsweise Audiodatei verarbeitet.
|
||||
- Base64 vergrößert die Requestgröße um ungefähr ein Drittel und benötigt beim Verarbeiten zusätzlichen Arbeitsspeicher. Für große Dateien ist `multipart/form-data` vorzuziehen.
|
||||
- Zeitstempel werden als SQLite- oder ISO-8601-Text ausgegeben.
|
||||
- Die aktuelle API besitzt keine Authentifizierung. Schreibzugriffe dürfen nicht ungeschützt öffentlich erreichbar sein.
|
||||
- Die Node.js-Anwendung besitzt keine eigene Authentifizierung. Ein vorgeschalteter Webserver kann Basic Auth erzwingen; die mitgelieferten Python-Werkzeuge erkennen `401 Unauthorized`, fragen Zugangsdaten ab und wiederholen den Request.
|
||||
|
||||
### Uploadgrenzen und Dateitypen
|
||||
|
||||
@@ -46,7 +47,7 @@ Unterstützte Dateitypen:
|
||||
|
||||
| Ressource | Dateiendungen beziehungsweise MIME-Typen |
|
||||
|---|---|
|
||||
| GPX | `.gpx`, `application/gpx+xml`, `application/xml`, `text/xml` |
|
||||
| GPX | GPX-XML mit `<gpx>`-Wurzelelement |
|
||||
| Bilder | JPEG, PNG, WebP |
|
||||
| Audio | MP3, MP4/M4A, AAC, Ogg, WAV, WebM |
|
||||
|
||||
@@ -62,12 +63,20 @@ Bei einem JSON-Medienupload ist `picture` beziehungsweise `audio` ein Objekt:
|
||||
|
||||
| Eigenschaft | Typ | Pflicht | Beschreibung |
|
||||
|---|---|---:|---|
|
||||
| `filename` | Text | ja | ursprünglicher Dateiname einschließlich passender Endung |
|
||||
| `contentType` | Text | ja, sofern keine `dataUrl` den Typ enthält | MIME-Typ, beispielsweise `image/jpeg` oder `audio/mpeg` |
|
||||
| `filename` | Text | nein | ursprünglicher Dateiname für Protokollierung; die Endung wird nicht zur Typbestimmung verwendet |
|
||||
| `base64` | Text | ja, sofern `dataUrl` fehlt | reiner Base64-Inhalt ohne Präfix |
|
||||
| `dataUrl` | Text | alternativ zu `base64` | vollständige Base64-Data-URL, beispielsweise `data:image/png;base64,...` |
|
||||
| `dataUrl` | Text | alternativ zu `base64` | vollständige Base64-Data-URL; der dort genannte Medientyp wird ignoriert |
|
||||
|
||||
`base64` und `dataUrl` sind Alternativen. Wird `dataUrl` verwendet, kann `contentType` daraus übernommen werden. `filename` bleibt erforderlich, weil die Dateiendung für Ablage und Auslieferung benötigt wird.
|
||||
`base64` und `dataUrl` sind Alternativen. Der Server erkennt Format und Speicherendung ausschließlich anhand der dekodierten Bytes. Ein vom Client mitgesendetes `contentType`- oder `mimeType`-Feld wird ignoriert und ist nicht erforderlich.
|
||||
|
||||
### Inhaltsbasierte Dateityperkennung
|
||||
|
||||
Binäre Bild- und Audiodateien werden mit `file-type` anhand ihrer Magic Bytes analysiert. Für Audio-/Video-Container wird zusätzlich `@file-type/av` verwendet, damit beispielsweise M4A und WebM möglichst zuverlässig als Audio oder Video unterschieden werden. GPX ist ein textbasiertes XML-Format und wird deshalb separat als UTF-8 gelesen und anhand des `<gpx>`-Wurzelelements validiert; anschließend übernimmt der vorhandene GPX-Parser die fachliche Prüfung.
|
||||
|
||||
Daraus folgen zwei wichtige Regeln:
|
||||
|
||||
- Eine als `image/png` deklarierte Textdatei wird mit `415 Unsupported Media Type` abgewiesen.
|
||||
- Eine echte PNG-Datei wird auch dann akzeptiert, wenn sie `datei.bin` heißt oder im Multipart-Request kein Datei-`Content-Type` angegeben ist.
|
||||
|
||||
### Fehlerformat
|
||||
|
||||
@@ -85,6 +94,7 @@ Typische Statuscodes:
|
||||
| `200` | Anfrage erfolgreich |
|
||||
| `201` | Ressource wurde angelegt |
|
||||
| `400` | Parameter oder Upload fehlt beziehungsweise ist ungültig |
|
||||
| `401` | vorgeschalteter Webserver verlangt Authentifizierung |
|
||||
| `404` | Route, POI oder Medienressource wurde nicht gefunden |
|
||||
| `409` | Ressource existiert bereits oder Dateisystemzustand verhindert die Operation |
|
||||
| `413` | Datei überschreitet `MAX_UPLOAD_MB` |
|
||||
@@ -202,6 +212,7 @@ Pro POI kann höchstens eine Audiodatei existieren. Deshalb wird die Audioressou
|
||||
| `GET` | `/api/routes/:routeId/pois/:poiId` | einzelnen POI der Route lesen |
|
||||
| `POST` | `/api/routes/:id/pois` | POI-Metadaten anlegen |
|
||||
| `PUT` | `/api/routes/:routeId/pois/:poiId` | POI-Metadaten aktualisieren |
|
||||
| `DELETE` | `/api/routes/:routeId/pois/:poiId` | POI einschließlich Bildern und Audio löschen |
|
||||
| `GET` | `/api/routes/:routeId/pois/:poiId/pictures` | Bilder eines POIs auflisten |
|
||||
| `GET` | `/api/routes/:routeId/pois/:poiId/pictures/:pictureId` | Bilddatei ausliefern; optional Metadaten mit `?metadata=true` |
|
||||
| `POST` | `/api/routes/:routeId/pois/:poiId/pictures` | einzelnes Bild für den POI hochladen |
|
||||
@@ -308,7 +319,7 @@ Content-Type: `multipart/form-data`
|
||||
|
||||
| Feld | Typ | Pflicht | Standard | Beschreibung |
|
||||
|---|---|---:|---|---|
|
||||
| `gpx` | Datei | ja | – | GPX-Datei mit mindestens einem Trackpunkt |
|
||||
| `gpx` | Datei | ja | – | GPX-Datei mit mindestens einem Trackpunkt; der Dateiname ist unerheblich |
|
||||
| `name` | Text | bedingt | Name aus GPX | erforderlich, wenn GPX keinen Namen enthält |
|
||||
| `slug` | Text | nein | aus `name` | interne URL-freundliche Kennung |
|
||||
| `description` | Text | nein | leer | Routenbeschreibung |
|
||||
@@ -320,7 +331,7 @@ curl -X POST http://127.0.0.1:47145/api/routes \
|
||||
-F 'slug=schulwald-runde-klasse-7a' \
|
||||
-F 'description=Naturkundlicher Rundweg der Klasse 7a' \
|
||||
-F 'schoolName=Beispielschule' \
|
||||
-F 'gpx=@examples/sample-route.gpx;type=application/gpx+xml'
|
||||
-F 'gpx=@examples/sample-route.gpx'
|
||||
```
|
||||
|
||||
Erfolg: `201 Created` und `Location: /api/routes/<id>`.
|
||||
@@ -341,7 +352,7 @@ curl -X PUT http://127.0.0.1:47145/api/routes/1 \
|
||||
-F 'name=Schulwald-Runde 2026' \
|
||||
-F 'description=Überarbeitete Strecke' \
|
||||
-F 'schoolName=Beispielschule' \
|
||||
-F 'gpx=@route-neu.gpx;type=application/gpx+xml'
|
||||
-F 'gpx=@route-neu.gpx'
|
||||
```
|
||||
|
||||
### `POST /api/routes/:id/append`
|
||||
@@ -354,7 +365,7 @@ Hängt alle Trackpunkte einer GPX-Datei an die Route an und berechnet Streckenwe
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:47145/api/routes/1/append \
|
||||
-F 'gpx=@verlaengerung.gpx;type=application/gpx+xml'
|
||||
-F 'gpx=@verlaengerung.gpx'
|
||||
```
|
||||
|
||||
## 7. POIs lesen und bearbeiten
|
||||
@@ -443,6 +454,22 @@ curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7 \
|
||||
}'
|
||||
```
|
||||
|
||||
### `DELETE /api/routes/:routeId/pois/:poiId`
|
||||
|
||||
Löscht den POI-Datensatz sowie alle zugehörigen Bilddatensätze, Bilddateien und die optionale Audiodatei. Dieser Vorgang ist im Gegensatz zum Soft Delete einer vollständigen Route nicht wiederherstellbar.
|
||||
|
||||
```bash
|
||||
curl -X DELETE http://127.0.0.1:47145/api/routes/1/pois/7
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 7,
|
||||
"routeId": 1,
|
||||
"deleted": true
|
||||
}
|
||||
```
|
||||
|
||||
## 8. Bilder eines POIs einzeln verwalten
|
||||
|
||||
Die Route und der POI sind Bestandteil jedes Bildpfades. Dadurch ist die Zuordnung eindeutig und beim Upload muss keine zusätzliche `poiId` übergeben werden.
|
||||
@@ -512,7 +539,7 @@ Lädt genau ein Bild für den im Pfad angegebenen POI hoch. Zulässig sind zwei
|
||||
|
||||
| Feld | Typ | Pflicht | Standard | Beschreibung |
|
||||
|---|---|---:|---:|---|
|
||||
| `picture` | Datei | ja | – | JPEG-, PNG- oder WebP-Datei |
|
||||
| `picture` | Datei | ja | – | JPEG-, PNG- oder WebP-Datei; der Typ wird aus dem Inhalt erkannt |
|
||||
| `caption` | Text | nein | leer | sichtbare Bildbeschreibung und Grundlage für den Alternativtext |
|
||||
| `sequence` | nichtnegative Ganzzahl | nein | nächster freier Wert des POIs | Reihenfolge in der Diashow |
|
||||
|
||||
@@ -520,7 +547,7 @@ Lädt genau ein Bild für den im Pfad angegebenen POI hoch. Zulässig sind zwei
|
||||
curl -X POST http://127.0.0.1:47145/api/routes/1/pois/7/pictures \
|
||||
-F 'caption=Blick auf die Baumkrone' \
|
||||
-F 'sequence=0' \
|
||||
-F 'picture=@eiche.jpg;type=image/jpeg'
|
||||
-F 'picture=@eiche.jpg'
|
||||
```
|
||||
|
||||
#### Variante B: JSON mit Base64
|
||||
@@ -533,7 +560,6 @@ Empfohlener Content-Type: `application/json`. Der Server akzeptiert zusätzlich
|
||||
"sequence": 0,
|
||||
"picture": {
|
||||
"filename": "eiche.jpg",
|
||||
"contentType": "image/jpeg",
|
||||
"base64": "/9j/4AAQSkZJRgABAQ..."
|
||||
}
|
||||
}
|
||||
@@ -553,7 +579,6 @@ jq -n \
|
||||
sequence: $sequence,
|
||||
picture: {
|
||||
filename: "eiche.jpg",
|
||||
contentType: "image/jpeg",
|
||||
base64: $data
|
||||
}
|
||||
}' > picture.json
|
||||
@@ -606,7 +631,7 @@ Datei und Metadaten per Multipart ersetzen:
|
||||
curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15 \
|
||||
-F 'caption=Neue Aufnahme der Eiche' \
|
||||
-F 'sequence=2' \
|
||||
-F 'picture=@eiche-neu.webp;type=image/webp'
|
||||
-F 'picture=@eiche-neu.webp'
|
||||
```
|
||||
|
||||
Datei und Metadaten per JSON ersetzen:
|
||||
@@ -617,7 +642,6 @@ Datei und Metadaten per JSON ersetzen:
|
||||
"sequence": 2,
|
||||
"picture": {
|
||||
"filename": "eiche-neu.webp",
|
||||
"contentType": "image/webp",
|
||||
"base64": "UklGRiQAAABXRUJQVlA4..."
|
||||
}
|
||||
}
|
||||
@@ -691,11 +715,11 @@ Legt die Audiodatei des im Pfad angegebenen POIs an. Zulässig sind `multipart/f
|
||||
|
||||
| Feld | Typ | Pflicht | Beschreibung |
|
||||
|---|---|---:|---|
|
||||
| `audio` | Datei | ja | MP3-, MP4/M4A-, AAC-, Ogg-, WAV- oder WebM-Datei |
|
||||
| `audio` | Datei | ja | MP3-, MP4/M4A-, AAC-, Ogg-, WAV- oder WebM-Datei; der Typ wird aus dem Inhalt erkannt |
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:47145/api/routes/1/pois/7/audio \
|
||||
-F 'audio=@ansage.mp3;type=audio/mpeg'
|
||||
-F 'audio=@ansage.mp3'
|
||||
```
|
||||
|
||||
#### Variante B: JSON mit Base64
|
||||
@@ -704,7 +728,6 @@ curl -X POST http://127.0.0.1:47145/api/routes/1/pois/7/audio \
|
||||
{
|
||||
"audio": {
|
||||
"filename": "ansage.mp3",
|
||||
"contentType": "audio/mpeg",
|
||||
"base64": "SUQzBAAAAAAAI1RTU0UAAA..."
|
||||
}
|
||||
}
|
||||
@@ -717,7 +740,6 @@ jq -n --rawfile data ansage.mp3.b64 \
|
||||
'{
|
||||
audio: {
|
||||
filename: "ansage.mp3",
|
||||
contentType: "audio/mpeg",
|
||||
base64: $data
|
||||
}
|
||||
}' > audio.json
|
||||
@@ -745,7 +767,7 @@ Multipart-Beispiel:
|
||||
|
||||
```bash
|
||||
curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/audio \
|
||||
-F 'audio=@ansage-neu.ogg;type=audio/ogg'
|
||||
-F 'audio=@ansage-neu.ogg'
|
||||
```
|
||||
|
||||
JSON-Beispiel:
|
||||
@@ -754,7 +776,6 @@ JSON-Beispiel:
|
||||
{
|
||||
"audio": {
|
||||
"filename": "ansage-neu.ogg",
|
||||
"contentType": "audio/ogg",
|
||||
"base64": "T2dnUwACAAAAAAAAAAB..."
|
||||
}
|
||||
}
|
||||
@@ -836,3 +857,10 @@ Die Zuordnung lautet:
|
||||
| `audio.url` | `/api/routes/:routeId/pois/:poiId/audio` |
|
||||
|
||||
Die Dateinamen und relativen Pfade unter `storage/` bleiben ausschließlich interne Implementierungsdetails. Dateien gelöschter Routen liegen im Papierkorb und sind über keinen Datei-Endpunkt erreichbar.
|
||||
|
||||
|
||||
## 12. Python-Werkzeuge
|
||||
|
||||
Unter `tools/python/` liegen interaktive Skripte für Anlegen, Ändern, Erweitern, Wiederherstellen und Löschen von Routen, POIs, Bildern und Audio. Sie verwenden ausschließlich die Python-Standardbibliothek. Fehlende Parameter werden abgefragt. Antwortet ein vorgeschalteter Webserver mit HTTP 401, fragt die gemeinsame Request-Schicht Benutzername und Passwort ab und wiederholt den ursprünglichen Request.
|
||||
|
||||
Details und Aufrufbeispiele stehen in [`tools/python/README.md`](../tools/python/README.md).
|
||||
|
||||
Reference in New Issue
Block a user