fixed missing endpoint GET /routes/:id/pois/:id and added json style fileupload

This commit is contained in:
Florian Zumpe
2026-06-17 01:39:50 +02:00
parent 5c85a0194b
commit 09ce7c0605
10 changed files with 380 additions and 50 deletions
+172 -20
View File
@@ -28,9 +28,13 @@ 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 JSON, `application/x-www-form-urlencoded` oder `multipart/form-data`.
- Schreibzugriffe mit Dateien verwenden `multipart/form-data`.
- 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.
- Bild- und Audioendpunkte akzeptieren wahlweise `multipart/form-data` oder ein JSON-Objekt mit Base64-kodierten Dateidaten.
- 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.
@@ -48,12 +52,23 @@ Unterstützte Dateitypen:
Die Uploadfelder heißen:
| Ressource | Feldname |
| Ressource | Multipart-Feld beziehungsweise JSON-Eigenschaft |
|---|---|
| GPX | `gpx` |
| einzelnes Bild | `picture` |
| einzelne Audiodatei | `audio` |
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` |
| `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,...` |
`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.
### Fehlerformat
```json
@@ -184,9 +199,9 @@ Pro POI kann höchstens eine Audiodatei existieren. Deshalb wird die Audioressou
| `DELETE` | `/api/routes/:id` | Route weich löschen |
| `POST` | `/api/routes/:id/restore` | Route wiederherstellen |
| `GET` | `/api/routes/:id/pois` | POIs einer Route auflisten |
| `GET` | `/api/pois/:id` | einzelnen POI lesen |
| `GET` | `/api/routes/:routeId/pois/:poiId` | einzelnen POI der Route lesen |
| `POST` | `/api/routes/:id/pois` | POI-Metadaten anlegen |
| `PUT` | `/api/pois/:id` | POI-Metadaten aktualisieren |
| `PUT` | `/api/routes/:routeId/pois/:poiId` | POI-Metadaten aktualisieren |
| `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 |
@@ -352,14 +367,21 @@ Liefert alle POIs einer aktiven Route nach `sequence` und `id`.
curl http://127.0.0.1:47145/api/routes/1/pois
```
### `GET /api/pois/:id`
### `GET /api/routes/:routeId/pois/:poiId`
Liefert einen einzelnen POI einschließlich seiner aktuellen Bild- und Audio-URLs.
Liefert einen einzelnen POI einschließlich seiner aktuellen Bild- und Audio-URLs. Route und POI werden gemeinsam geprüft; der POI muss zur angegebenen aktiven Route gehören.
| Pfadparameter | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `routeId` | Ganzzahl | ja | ID der aktiven Route |
| `poiId` | Ganzzahl | ja | ID des POIs innerhalb dieser Route |
```bash
curl http://127.0.0.1:47145/api/pois/7
curl http://127.0.0.1:47145/api/routes/1/pois/7
```
Eine unbekannte Route, ein unbekannter POI oder eine falsche Route-POI-Kombination liefert `404 Not Found`.
### `POST /api/routes/:id/pois`
Legt ausschließlich die POI-Metadaten an. Bilder und Audio werden anschließend über die gesonderten Medienendpunkte hochgeladen.
@@ -388,11 +410,16 @@ curl -X POST http://127.0.0.1:47145/api/routes/1/pois \
}'
```
Erfolg: `201 Created` und `Location: /api/pois/<id>`.
Erfolg: `201 Created` und `Location: /api/routes/<routeId>/pois/<poiId>`.
### `PUT /api/pois/:id`
### `PUT /api/routes/:routeId/pois/:poiId`
Aktualisiert ausschließlich POI-Metadaten. Nicht übergebene Werte bleiben erhalten.
Aktualisiert ausschließlich POI-Metadaten. Nicht übergebene Werte bleiben erhalten. Route und POI werden gemeinsam validiert; ein POI kann über diesen Endpunkt keiner anderen Route zugeordnet werden.
| Pfadparameter | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `routeId` | Ganzzahl | ja | ID der aktiven Route |
| `poiId` | Ganzzahl | ja | ID des POIs innerhalb dieser Route |
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
@@ -404,7 +431,7 @@ Aktualisiert ausschließlich POI-Metadaten. Nicht übergebene Werte bleiben erha
| `sequence` | nichtnegative Ganzzahl | nein | neue Reihenfolge |
```bash
curl -X PUT http://127.0.0.1:47145/api/pois/7 \
curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7 \
-H 'Content-Type: application/json' \
-d '{
"title": "Die sehr alte Eiche",
@@ -479,9 +506,9 @@ Die API antwortet mit `404`, wenn Route, POI oder Bild nicht zusammengehören.
### `POST /api/routes/:routeId/pois/:poiId/pictures`
Lädt genau ein Bild für den im Pfad angegebenen POI hoch.
Lädt genau ein Bild für den im Pfad angegebenen POI hoch. Zulässig sind zwei Übertragungsformen.
Content-Type: `multipart/form-data`
#### Variante A: `multipart/form-data`
| Feld | Typ | Pflicht | Standard | Beschreibung |
|---|---|---:|---:|---|
@@ -496,6 +523,60 @@ curl -X POST http://127.0.0.1:47145/api/routes/1/pois/7/pictures \
-F 'picture=@eiche.jpg;type=image/jpeg'
```
#### Variante B: JSON mit Base64
Empfohlener Content-Type: `application/json`. Der Server akzeptiert zusätzlich `text/json`.
```json
{
"caption": "Blick auf die Baumkrone",
"sequence": 0,
"picture": {
"filename": "eiche.jpg",
"contentType": "image/jpeg",
"base64": "/9j/4AAQSkZJRgABAQ..."
}
}
```
Beispiel mit `text/json` und einer separat erzeugten Payload-Datei:
```bash
base64 < eiche.jpg | tr -d '\n' > eiche.jpg.b64
jq -n \
--arg caption 'Blick auf die Baumkrone' \
--argjson sequence 0 \
--rawfile data eiche.jpg.b64 \
'{
caption: $caption,
sequence: $sequence,
picture: {
filename: "eiche.jpg",
contentType: "image/jpeg",
base64: $data
}
}' > picture.json
curl -X POST http://127.0.0.1:47145/api/routes/1/pois/7/pictures \
-H 'Content-Type: text/json' \
--data-binary @picture.json
```
Alternativ kann eine Data-URL übertragen werden:
```json
{
"caption": "Blick auf die Baumkrone",
"picture": {
"filename": "eiche.png",
"dataUrl": "data:image/png;base64,iVBORw0KGgoAAA..."
}
}
```
Ein Request mit `-d '{}'` schlägt mit `400 Bad Request` fehl, weil weder eine Multipart-Datei noch ein JSON-Dateiobjekt enthalten ist.
Erfolg: `201 Created` und `Location: /api/routes/1/pois/7/pictures/<pictureId>`.
### `PUT /api/routes/:routeId/pois/:poiId/pictures/:pictureId`
@@ -504,7 +585,7 @@ Aktualisiert Metadaten und kann optional die Datei ersetzen. Nicht übergebene M
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `picture` | Datei | nein | ersetzt die bisherige Bilddatei |
| `picture` | Multipart-Datei oder JSON-Dateiobjekt | nein | ersetzt die bisherige Bilddatei |
| `caption` | Text | nein | neue Bildbeschreibung; leerer Text entfernt die Beschreibung |
| `sequence` | nichtnegative Ganzzahl | nein | neue Position in der Diashow |
@@ -519,7 +600,7 @@ curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15 \
}'
```
Datei und Metadaten ersetzen:
Datei und Metadaten per Multipart ersetzen:
```bash
curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15 \
@@ -528,6 +609,26 @@ curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15 \
-F 'picture=@eiche-neu.webp;type=image/webp'
```
Datei und Metadaten per JSON ersetzen:
```json
{
"caption": "Neue Aufnahme der Eiche",
"sequence": 2,
"picture": {
"filename": "eiche-neu.webp",
"contentType": "image/webp",
"base64": "UklGRiQAAABXRUJQVlA4..."
}
}
```
```bash
curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15 \
-H 'Content-Type: application/json' \
--data-binary @picture-update.json
```
Wird eine Datei ersetzt, entfernt der Server die bisherige Datei nach erfolgreicher Datenbankaktualisierung.
### `DELETE /api/routes/:routeId/pois/:poiId/pictures/:pictureId`
@@ -584,9 +685,9 @@ Antwortet mit `404`, wenn Route und POI nicht zusammengehören oder der POI kein
### `POST /api/routes/:routeId/pois/:poiId/audio`
Legt die Audiodatei des im Pfad angegebenen POIs an.
Legt die Audiodatei des im Pfad angegebenen POIs an. Zulässig sind `multipart/form-data` und JSON mit Base64.
Content-Type: `multipart/form-data`
#### Variante A: `multipart/form-data`
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
@@ -597,23 +698,74 @@ curl -X POST http://127.0.0.1:47145/api/routes/1/pois/7/audio \
-F 'audio=@ansage.mp3;type=audio/mpeg'
```
#### Variante B: JSON mit Base64
```json
{
"audio": {
"filename": "ansage.mp3",
"contentType": "audio/mpeg",
"base64": "SUQzBAAAAAAAI1RTU0UAAA..."
}
}
```
```bash
base64 < ansage.mp3 | tr -d '\n' > ansage.mp3.b64
jq -n --rawfile data ansage.mp3.b64 \
'{
audio: {
filename: "ansage.mp3",
contentType: "audio/mpeg",
base64: $data
}
}' > audio.json
curl -X POST http://127.0.0.1:47145/api/routes/1/pois/7/audio \
-H 'Content-Type: application/json' \
--data-binary @audio.json
```
Auch hier wird `Content-Type: text/json` akzeptiert. Ein leeres `{}` enthält keine Audiodatei und liefert `400 Bad Request`.
Erfolg: `201 Created` und `Location: /api/routes/1/pois/7/audio`.
Existiert bereits eine Audiodatei, antwortet der Server mit `409`. Zum Ersetzen ist `PUT` zu verwenden.
### `PUT /api/routes/:routeId/pois/:poiId/audio`
Ersetzt die vorhandene Audiodatei des POIs.
Ersetzt die vorhandene Audiodatei des POIs. Die Datei kann als Multipart-Upload oder als JSON-Dateiobjekt übertragen werden.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `audio` | Datei | ja | neue Audiodatei |
| `audio` | Multipart-Datei oder JSON-Dateiobjekt | ja | neue Audiodatei |
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'
```
JSON-Beispiel:
```json
{
"audio": {
"filename": "ansage-neu.ogg",
"contentType": "audio/ogg",
"base64": "T2dnUwACAAAAAAAAAAB..."
}
}
```
```bash
curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/audio \
-H 'Content-Type: text/json' \
--data-binary @audio-update.json
```
Der Server entfernt die bisherige Datei nach erfolgreicher Aktualisierung. Besitzt der POI noch keine Audiodatei, antwortet der Server mit `404`; zum erstmaligen Anlegen ist `POST` zu verwenden.
### `DELETE /api/routes/:routeId/pois/:poiId/audio`