724 lines
20 KiB
Markdown
724 lines
20 KiB
Markdown
# Wegwichtel REST-API
|
||
|
||
Diese Datei dokumentiert die vollständige HTTP-Schnittstelle des Wegwichtel-Servers. Sämtliche JSON-Ressourcen und aktiven GPX-, Bild- und Audiodateien werden unter `/api` bereitgestellt. Ein separates öffentliches `/media`-URL-Schema wird nicht verwendet.
|
||
|
||
## 1. Grundlagen
|
||
|
||
### Basisadressen
|
||
|
||
Lokaler Standard:
|
||
|
||
```text
|
||
http://127.0.0.1:47145
|
||
```
|
||
|
||
API-Basis:
|
||
|
||
```text
|
||
http://127.0.0.1:47145/api
|
||
```
|
||
|
||
Bei einer Nginx-Installation bleibt der Pfad gleich, beispielsweise:
|
||
|
||
```text
|
||
https://wegwichtel.example.org/api
|
||
```
|
||
|
||
### Formate
|
||
|
||
- 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`.
|
||
- Pro Medien-Request wird genau eine Bild- beziehungsweise Audiodatei verarbeitet.
|
||
- Zeitstempel werden als SQLite- oder ISO-8601-Text ausgegeben.
|
||
- Die aktuelle API besitzt keine Authentifizierung. Schreibzugriffe dürfen nicht ungeschützt öffentlich erreichbar sein.
|
||
|
||
### Uploadgrenzen und Dateitypen
|
||
|
||
Das Dateilimit pro Datei wird durch `MAX_UPLOAD_MB` festgelegt und beträgt standardmäßig `50 MB`.
|
||
|
||
Unterstützte Dateitypen:
|
||
|
||
| Ressource | Dateiendungen beziehungsweise MIME-Typen |
|
||
|---|---|
|
||
| GPX | `.gpx`, `application/gpx+xml`, `application/xml`, `text/xml` |
|
||
| Bilder | JPEG, PNG, WebP |
|
||
| Audio | MP3, MP4/M4A, AAC, Ogg, WAV, WebM |
|
||
|
||
Die Uploadfelder heißen:
|
||
|
||
| Ressource | Feldname |
|
||
|---|---|
|
||
| GPX | `gpx` |
|
||
| einzelnes Bild | `picture` |
|
||
| einzelne Audiodatei | `audio` |
|
||
|
||
### Fehlerformat
|
||
|
||
```json
|
||
{
|
||
"error": "HttpError",
|
||
"message": "Strecke nicht gefunden."
|
||
}
|
||
```
|
||
|
||
Typische Statuscodes:
|
||
|
||
| Status | Bedeutung |
|
||
|---:|---|
|
||
| `200` | Anfrage erfolgreich |
|
||
| `201` | Ressource wurde angelegt |
|
||
| `400` | Parameter oder Upload fehlt beziehungsweise ist ungültig |
|
||
| `404` | Route, POI oder Medienressource wurde nicht gefunden |
|
||
| `409` | Ressource existiert bereits oder Dateisystemzustand verhindert die Operation |
|
||
| `413` | Datei überschreitet `MAX_UPLOAD_MB` |
|
||
| `415` | Dateityp wird nicht unterstützt |
|
||
| `500` | Interner Serverfehler |
|
||
|
||
### IDs
|
||
|
||
Alle IDs sind positive, von SQLite erzeugte Ganzzahlen.
|
||
|
||
- `:id` bezeichnet bei `/routes/:id/...` die Route.
|
||
- `:pictureId` bezeichnet einen Datensatz aus `poi_images`.
|
||
- `:poiId` bezeichnet den POI und zugleich seine höchstens eine Audioressource.
|
||
|
||
Beispiele verwenden überwiegend Route `1`, POI `7` und Bild `15`.
|
||
|
||
## 2. Datenmodelle
|
||
|
||
### Route
|
||
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"slug": "schulwald-runde",
|
||
"name": "Schulwald-Runde",
|
||
"description": "Naturkundlicher Rundweg",
|
||
"schoolName": "Beispielschule",
|
||
"status": "active",
|
||
"start": { "lat": 52.5208, "lon": 13.407 },
|
||
"center": { "lat": 52.521, "lon": 13.408 },
|
||
"bounds": {
|
||
"minLat": 52.5208,
|
||
"minLon": 13.407,
|
||
"maxLat": 52.5212,
|
||
"maxLon": 13.409
|
||
},
|
||
"distanceM": 842.6,
|
||
"elevationGainM": 14.2,
|
||
"pointCount": 87,
|
||
"gpxUrl": "/api/routes/1/gpx",
|
||
"createdAt": "2026-06-16 12:00:00",
|
||
"updatedAt": "2026-06-16 12:00:00",
|
||
"deletedAt": null,
|
||
"proximityM": 324.8
|
||
}
|
||
```
|
||
|
||
### POI
|
||
|
||
```json
|
||
{
|
||
"id": 7,
|
||
"routeId": 1,
|
||
"title": "Die alte Eiche",
|
||
"description": "Hier wird das Alter der Eiche erklärt.",
|
||
"lat": 52.5208,
|
||
"lon": 13.407,
|
||
"triggerRadiusM": 60,
|
||
"sequence": 2,
|
||
"audioUrl": "/api/routes/1/audio/7",
|
||
"images": [
|
||
{
|
||
"id": 15,
|
||
"caption": "Blick auf die Baumkrone",
|
||
"sequence": 0,
|
||
"url": "/api/routes/1/pictures/15"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### Bildressource
|
||
|
||
```json
|
||
{
|
||
"id": 15,
|
||
"routeId": 1,
|
||
"poiId": 7,
|
||
"poiTitle": "Die alte Eiche",
|
||
"caption": "Blick auf die Baumkrone",
|
||
"sequence": 0,
|
||
"url": "/api/routes/1/pictures/15",
|
||
"createdAt": "2026-06-16 12:30:00"
|
||
}
|
||
```
|
||
|
||
### Audioressource
|
||
|
||
Pro POI kann höchstens eine Audiodatei existieren. Deshalb wird die Audioressource über die `poiId` adressiert.
|
||
|
||
```json
|
||
{
|
||
"routeId": 1,
|
||
"poiId": 7,
|
||
"poiTitle": "Die alte Eiche",
|
||
"url": "/api/routes/1/audio/7",
|
||
"updatedAt": "2026-06-16 12:35:00"
|
||
}
|
||
```
|
||
|
||
## 3. Endpunktübersicht
|
||
|
||
| Methode | Pfad | Zweck |
|
||
|---|---|---|
|
||
| `GET` | `/api/health` | Server- und SQLite-Zustand |
|
||
| `GET` | `/api/routes` | Routen auflisten |
|
||
| `GET` | `/api/routes/:id` | Route mit Punkten und POIs lesen |
|
||
| `GET` | `/api/routes/:id/gpx` | GPX-Datei der Route ausliefern |
|
||
| `POST` | `/api/routes` | Route anlegen |
|
||
| `PUT` | `/api/routes/:id` | Route aktualisieren |
|
||
| `POST` | `/api/routes/:id/append` | GPX-Punkte anhängen |
|
||
| `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 |
|
||
| `POST` | `/api/routes/:id/pois` | POI-Metadaten anlegen |
|
||
| `PUT` | `/api/pois/:id` | POI-Metadaten aktualisieren |
|
||
| `GET` | `/api/routes/:id/pictures` | Bilder einer Route auflisten |
|
||
| `GET` | `/api/routes/:id/pictures/:pictureId` | Bilddatei ausliefern; optional Metadaten mit `?metadata=true` |
|
||
| `POST` | `/api/routes/:id/pictures` | einzelnes Bild hochladen |
|
||
| `PUT` | `/api/routes/:id/pictures/:pictureId` | Bilddatei oder Metadaten aktualisieren |
|
||
| `DELETE` | `/api/routes/:id/pictures/:pictureId` | einzelnes Bild löschen |
|
||
| `GET` | `/api/routes/:id/audio` | Audiodateien einer Route auflisten |
|
||
| `GET` | `/api/routes/:id/audio/:poiId` | Audiodatei ausliefern; optional Metadaten mit `?metadata=true` |
|
||
| `POST` | `/api/routes/:id/audio` | Audiodatei für einen POI anlegen |
|
||
| `PUT` | `/api/routes/:id/audio/:poiId` | Audiodatei eines POIs ersetzen |
|
||
| `DELETE` | `/api/routes/:id/audio/:poiId` | Audiodatei eines POIs löschen |
|
||
|
||
## 4. Systemzustand
|
||
|
||
### `GET /api/health`
|
||
|
||
Parameter: keine.
|
||
|
||
```bash
|
||
curl http://127.0.0.1:47145/api/health
|
||
```
|
||
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"service": "wegwichtel",
|
||
"socket": "127.0.0.1:47145",
|
||
"sqliteVersion": "3.46.1",
|
||
"timestamp": "2026-06-16T12:00:00.000Z"
|
||
}
|
||
```
|
||
|
||
## 5. Routen lesen
|
||
|
||
### `GET /api/routes`
|
||
|
||
Ohne Positionsparameter werden alle aktiven Routen alphabetisch geliefert. Mit `lat` und `lon` wird die Entfernung zum Routenstart berechnet, anhand `radiusKm` gefiltert und nach Entfernung sortiert.
|
||
|
||
#### Query-Parameter
|
||
|
||
| Parameter | Typ | Pflicht | Standard | Beschreibung |
|
||
|---|---|---:|---:|---|
|
||
| `lat` | Dezimalzahl | nein | – | Breitengrad; nur zusammen mit `lon` wirksam |
|
||
| `lon` | Dezimalzahl | nein | – | Längengrad; nur zusammen mit `lat` wirksam |
|
||
| `radiusKm` | Dezimalzahl | nein | `DEFAULT_ROUTE_RADIUS_KM`, standardmäßig `25` | maximaler Abstand zum Routenstart |
|
||
| `includeDeleted` | Boolean-Text | nein | `false` | der exakte Wert `true` schließt gelöschte Routen ein |
|
||
|
||
Alle aktiven Routen:
|
||
|
||
```bash
|
||
curl http://127.0.0.1:47145/api/routes
|
||
```
|
||
|
||
Mit allen Query-Parametern:
|
||
|
||
```bash
|
||
curl --get http://127.0.0.1:47145/api/routes \
|
||
--data-urlencode 'lat=52.5208' \
|
||
--data-urlencode 'lon=13.4070' \
|
||
--data-urlencode 'radiusKm=12.5' \
|
||
--data-urlencode 'includeDeleted=true'
|
||
```
|
||
|
||
### `GET /api/routes/:id`
|
||
|
||
Liefert Route, GPX-Punkte und POIs einschließlich Medien-URLs.
|
||
|
||
#### Pfadparameter
|
||
|
||
| Parameter | Typ | Pflicht | Beschreibung |
|
||
|---|---|---:|---|
|
||
| `id` | Ganzzahl | ja | Route |
|
||
|
||
#### Query-Parameter
|
||
|
||
| Parameter | Typ | Pflicht | Standard | Beschreibung |
|
||
|---|---|---:|---:|---|
|
||
| `includeDeleted` | Boolean-Text | nein | `false` | mit `true` kann eine gelöschte Route gelesen werden |
|
||
|
||
```bash
|
||
curl http://127.0.0.1:47145/api/routes/1
|
||
```
|
||
|
||
```bash
|
||
curl 'http://127.0.0.1:47145/api/routes/1?includeDeleted=true'
|
||
```
|
||
|
||
### `GET /api/routes/:id/gpx`
|
||
|
||
Liefert die aktive GPX-Datei der Route direkt mit `Content-Type: application/gpx+xml` aus. Der in einer Routenressource enthaltene Wert `gpxUrl` verweist auf diesen Endpunkt.
|
||
|
||
```bash
|
||
curl http://127.0.0.1:47145/api/routes/1/gpx \
|
||
--output schulwald-runde.gpx
|
||
```
|
||
|
||
Parameter außer der Routen-ID sind nicht vorgesehen. Gelöschte oder unbekannte Routen antworten mit `404`.
|
||
|
||
## 6. Route anlegen und bearbeiten
|
||
|
||
### `POST /api/routes`
|
||
|
||
Legt eine Route aus einer GPX-Datei an.
|
||
|
||
Content-Type: `multipart/form-data`
|
||
|
||
| Feld | Typ | Pflicht | Standard | Beschreibung |
|
||
|---|---|---:|---|---|
|
||
| `gpx` | Datei | ja | – | GPX-Datei mit mindestens einem Trackpunkt |
|
||
| `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 |
|
||
| `schoolName` | Text | nein | leer | Schule oder Einrichtung |
|
||
|
||
```bash
|
||
curl -X POST http://127.0.0.1:47145/api/routes \
|
||
-F 'name=Schulwald-Runde' \
|
||
-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'
|
||
```
|
||
|
||
Erfolg: `201 Created` und `Location: /api/routes/<id>`.
|
||
|
||
### `PUT /api/routes/:id`
|
||
|
||
Aktualisiert Metadaten. Eine optionale GPX-Datei ersetzt alle bisherigen Trackpunkte.
|
||
|
||
| Feld | Typ | Pflicht | Verhalten ohne Feld |
|
||
|---|---|---:|---|
|
||
| `gpx` | Datei | nein | bisherige GPX-Punkte bleiben erhalten |
|
||
| `name` | Text | nein | bisheriger Wert bleibt |
|
||
| `description` | Text | nein | bisheriger Wert bleibt |
|
||
| `schoolName` | Text | nein | bisheriger Wert bleibt |
|
||
|
||
```bash
|
||
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'
|
||
```
|
||
|
||
### `POST /api/routes/:id/append`
|
||
|
||
Hängt alle Trackpunkte einer GPX-Datei an die Route an und berechnet Streckenwerte neu.
|
||
|
||
| Feld | Typ | Pflicht | Beschreibung |
|
||
|---|---|---:|---|
|
||
| `gpx` | Datei | ja | anzuhängende GPX-Datei |
|
||
|
||
```bash
|
||
curl -X POST http://127.0.0.1:47145/api/routes/1/append \
|
||
-F 'gpx=@verlaengerung.gpx;type=application/gpx+xml'
|
||
```
|
||
|
||
## 7. POIs lesen und bearbeiten
|
||
|
||
### `GET /api/routes/:id/pois`
|
||
|
||
Liefert alle POIs einer aktiven Route nach `sequence` und `id`.
|
||
|
||
```bash
|
||
curl http://127.0.0.1:47145/api/routes/1/pois
|
||
```
|
||
|
||
### `GET /api/pois/:id`
|
||
|
||
Liefert einen einzelnen POI einschließlich seiner aktuellen Bild- und Audio-URLs.
|
||
|
||
```bash
|
||
curl http://127.0.0.1:47145/api/pois/7
|
||
```
|
||
|
||
### `POST /api/routes/:id/pois`
|
||
|
||
Legt ausschließlich die POI-Metadaten an. Bilder und Audio werden anschließend über die gesonderten Medienendpunkte hochgeladen.
|
||
|
||
| Feld | Typ | Pflicht | Standard | Beschreibung |
|
||
|---|---|---:|---:|---|
|
||
| `title` | Text | nein | `Unbenannter POI` | Stationsname |
|
||
| `description` | Text | nein | leer | Beschreibung |
|
||
| `lat` | Dezimalzahl | ja | – | Breitengrad |
|
||
| `lon` | Dezimalzahl | ja | – | Längengrad |
|
||
| `triggerRadiusM` | Dezimalzahl | nein | `DEFAULT_POI_TRIGGER_METERS`, standardmäßig `80` | Aktivierungsradius in Metern |
|
||
| `sequence` | Ganzzahl | nein | `0` | Reihenfolge in der Stationsliste |
|
||
|
||
JSON-Beispiel mit allen Parametern:
|
||
|
||
```bash
|
||
curl -X POST http://127.0.0.1:47145/api/routes/1/pois \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"title": "Die alte Eiche",
|
||
"description": "Hier wird das Alter der Eiche erklärt.",
|
||
"lat": 52.5208,
|
||
"lon": 13.4070,
|
||
"triggerRadiusM": 60,
|
||
"sequence": 2
|
||
}'
|
||
```
|
||
|
||
Erfolg: `201 Created` und `Location: /api/pois/<id>`.
|
||
|
||
### `PUT /api/pois/:id`
|
||
|
||
Aktualisiert ausschließlich POI-Metadaten. Nicht übergebene Werte bleiben erhalten.
|
||
|
||
| Feld | Typ | Pflicht | Beschreibung |
|
||
|---|---|---:|---|
|
||
| `title` | Text | nein | neuer Stationsname |
|
||
| `description` | Text | nein | neue Beschreibung |
|
||
| `lat` | Dezimalzahl | nein | neuer Breitengrad |
|
||
| `lon` | Dezimalzahl | nein | neuer Längengrad |
|
||
| `triggerRadiusM` | Dezimalzahl | nein | neuer Aktivierungsradius |
|
||
| `sequence` | nichtnegative Ganzzahl | nein | neue Reihenfolge |
|
||
|
||
```bash
|
||
curl -X PUT http://127.0.0.1:47145/api/pois/7 \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"title": "Die sehr alte Eiche",
|
||
"description": "Aktualisierte Beschreibung",
|
||
"lat": 52.5209,
|
||
"lon": 13.4071,
|
||
"triggerRadiusM": 45,
|
||
"sequence": 3
|
||
}'
|
||
```
|
||
|
||
## 8. Bilder einzeln verwalten
|
||
|
||
### `GET /api/routes/:id/pictures`
|
||
|
||
Liefert alle Bilder der Route. Optional kann auf einen POI eingeschränkt werden.
|
||
|
||
#### Query-Parameter
|
||
|
||
| Parameter | Typ | Pflicht | Beschreibung |
|
||
|---|---|---:|---|
|
||
| `poiId` | positive Ganzzahl | nein | liefert nur Bilder dieses POIs |
|
||
|
||
Alle Bilder der Route:
|
||
|
||
```bash
|
||
curl http://127.0.0.1:47145/api/routes/1/pictures
|
||
```
|
||
|
||
Nur Bilder des POIs `7`:
|
||
|
||
```bash
|
||
curl --get http://127.0.0.1:47145/api/routes/1/pictures \
|
||
--data-urlencode 'poiId=7'
|
||
```
|
||
|
||
```json
|
||
{
|
||
"pictures": [
|
||
{
|
||
"id": 15,
|
||
"routeId": 1,
|
||
"poiId": 7,
|
||
"poiTitle": "Die alte Eiche",
|
||
"caption": "Blick auf die Baumkrone",
|
||
"sequence": 0,
|
||
"url": "/api/routes/1/pictures/15",
|
||
"createdAt": "2026-06-16 12:30:00"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### `GET /api/routes/:id/pictures/:pictureId`
|
||
|
||
Liefert standardmäßig die Bilddatei direkt aus. Genau dieser Pfad wird im Feld `url` der Bildressource und unter `pois[].images[].url` ausgegeben.
|
||
|
||
Bild speichern:
|
||
|
||
```bash
|
||
curl http://127.0.0.1:47145/api/routes/1/pictures/15 \
|
||
--output bild-15.jpg
|
||
```
|
||
|
||
Metadaten statt Dateidaten abrufen:
|
||
|
||
```bash
|
||
curl --get http://127.0.0.1:47145/api/routes/1/pictures/15 \
|
||
--data-urlencode 'metadata=true'
|
||
```
|
||
|
||
| Query-Parameter | Typ | Pflicht | Standard | Beschreibung |
|
||
|---|---|---:|---:|---|
|
||
| `metadata` | Boolean | nein | `false` | bei `true` JSON-Metadaten statt der Bilddatei liefern |
|
||
|
||
### `POST /api/routes/:id/pictures`
|
||
|
||
Lädt genau ein Bild hoch und ordnet es einem POI derselben Route zu.
|
||
|
||
Content-Type: `multipart/form-data`
|
||
|
||
| Feld | Typ | Pflicht | Standard | Beschreibung |
|
||
|---|---|---:|---:|---|
|
||
| `picture` | Datei | ja | – | JPEG-, PNG- oder WebP-Datei |
|
||
| `poiId` | positive Ganzzahl | ja | – | Ziel-POI derselben Route |
|
||
| `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 |
|
||
|
||
```bash
|
||
curl -X POST http://127.0.0.1:47145/api/routes/1/pictures \
|
||
-F 'poiId=7' \
|
||
-F 'caption=Blick auf die Baumkrone' \
|
||
-F 'sequence=0' \
|
||
-F 'picture=@eiche.jpg;type=image/jpeg'
|
||
```
|
||
|
||
Erfolg: `201 Created` und `Location: /api/routes/1/pictures/<pictureId>`.
|
||
|
||
### `PUT /api/routes/:id/pictures/:pictureId`
|
||
|
||
Aktualisiert Metadaten und kann optional die Datei ersetzen. Nicht übergebene Metadaten bleiben erhalten.
|
||
|
||
| Feld | Typ | Pflicht | Beschreibung |
|
||
|---|---|---:|---|
|
||
| `picture` | Datei | nein | ersetzt die bisherige Bilddatei |
|
||
| `poiId` | positive Ganzzahl | nein | ordnet das Bild einem anderen POI derselben Route zu |
|
||
| `caption` | Text | nein | neue Bildbeschreibung; leerer Text entfernt die Beschreibung |
|
||
| `sequence` | nichtnegative Ganzzahl | nein | neue Position in der Diashow |
|
||
|
||
Nur Metadaten per JSON ändern:
|
||
|
||
```bash
|
||
curl -X PUT http://127.0.0.1:47145/api/routes/1/pictures/15 \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"poiId": 7,
|
||
"caption": "Nahaufnahme der Eichenblätter",
|
||
"sequence": 1
|
||
}'
|
||
```
|
||
|
||
Datei und alle Metadaten ersetzen:
|
||
|
||
```bash
|
||
curl -X PUT http://127.0.0.1:47145/api/routes/1/pictures/15 \
|
||
-F 'poiId=7' \
|
||
-F 'caption=Neue Aufnahme der Eiche' \
|
||
-F 'sequence=2' \
|
||
-F 'picture=@eiche-neu.webp;type=image/webp'
|
||
```
|
||
|
||
Wird eine Datei ersetzt, entfernt der Server die bisherige Datei nach erfolgreicher Datenbankaktualisierung.
|
||
|
||
### `DELETE /api/routes/:id/pictures/:pictureId`
|
||
|
||
Entfernt Bilddatensatz und Datei.
|
||
|
||
```bash
|
||
curl -X DELETE http://127.0.0.1:47145/api/routes/1/pictures/15
|
||
```
|
||
|
||
```json
|
||
{
|
||
"id": 15,
|
||
"routeId": 1,
|
||
"poiId": 7,
|
||
"deleted": true
|
||
}
|
||
```
|
||
|
||
## 9. Audiodateien einzeln verwalten
|
||
|
||
Pro POI ist höchstens eine Audiodatei vorgesehen. Deshalb bildet die `poiId` den Schlüssel der Audioressource.
|
||
|
||
### `GET /api/routes/:id/audio`
|
||
|
||
Liefert alle vorhandenen Audiodateien der Route. POIs ohne Audio werden nicht ausgegeben.
|
||
|
||
#### Query-Parameter
|
||
|
||
| Parameter | Typ | Pflicht | Beschreibung |
|
||
|---|---|---:|---|
|
||
| `poiId` | positive Ganzzahl | nein | schränkt die Liste auf einen POI ein |
|
||
|
||
```bash
|
||
curl http://127.0.0.1:47145/api/routes/1/audio
|
||
```
|
||
|
||
```bash
|
||
curl --get http://127.0.0.1:47145/api/routes/1/audio \
|
||
--data-urlencode 'poiId=7'
|
||
```
|
||
|
||
```json
|
||
{
|
||
"audio": [
|
||
{
|
||
"routeId": 1,
|
||
"poiId": 7,
|
||
"poiTitle": "Die alte Eiche",
|
||
"url": "/api/routes/1/audio/7",
|
||
"updatedAt": "2026-06-16 12:35:00"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### `GET /api/routes/:id/audio/:poiId`
|
||
|
||
Liefert standardmäßig die Audiodatei des POIs direkt aus. Genau dieser Pfad wird in `audioUrl` und im Feld `url` einer Audioressource ausgegeben. Der Endpunkt unterstützt HTTP-Range-Anfragen, damit Browser innerhalb einer Audiodatei springen können.
|
||
|
||
Audiodatei speichern:
|
||
|
||
```bash
|
||
curl http://127.0.0.1:47145/api/routes/1/audio/7 \
|
||
--output ansage-7.mp3
|
||
```
|
||
|
||
Metadaten statt Dateidaten abrufen:
|
||
|
||
```bash
|
||
curl --get http://127.0.0.1:47145/api/routes/1/audio/7 \
|
||
--data-urlencode 'metadata=true'
|
||
```
|
||
|
||
| Query-Parameter | Typ | Pflicht | Standard | Beschreibung |
|
||
|---|---|---:|---:|---|
|
||
| `metadata` | Boolean | nein | `false` | bei `true` JSON-Metadaten statt der Audiodatei liefern |
|
||
|
||
Antwortet mit `404`, wenn der POI keine Audiodatei besitzt.
|
||
|
||
### `POST /api/routes/:id/audio`
|
||
|
||
Legt die Audiodatei eines POIs an.
|
||
|
||
Content-Type: `multipart/form-data`
|
||
|
||
| Feld | Typ | Pflicht | Beschreibung |
|
||
|---|---|---:|---|
|
||
| `audio` | Datei | ja | MP3-, MP4/M4A-, AAC-, Ogg-, WAV- oder WebM-Datei |
|
||
| `poiId` | positive Ganzzahl | ja | POI derselben Route |
|
||
|
||
```bash
|
||
curl -X POST http://127.0.0.1:47145/api/routes/1/audio \
|
||
-F 'poiId=7' \
|
||
-F 'audio=@ansage.mp3;type=audio/mpeg'
|
||
```
|
||
|
||
Erfolg: `201 Created` und `Location: /api/routes/1/audio/7`.
|
||
|
||
Existiert bereits eine Audiodatei, antwortet der Server mit `409`. Zum Ersetzen ist `PUT` zu verwenden.
|
||
|
||
### `PUT /api/routes/:id/audio/:poiId`
|
||
|
||
Ersetzt die vorhandene Audiodatei des POIs.
|
||
|
||
| Feld | Typ | Pflicht | Beschreibung |
|
||
|---|---|---:|---|
|
||
| `audio` | Datei | ja | neue Audiodatei |
|
||
|
||
```bash
|
||
curl -X PUT http://127.0.0.1:47145/api/routes/1/audio/7 \
|
||
-F 'audio=@ansage-neu.ogg;type=audio/ogg'
|
||
```
|
||
|
||
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/:id/audio/:poiId`
|
||
|
||
Entfernt die Audiodatei und setzt `audioUrl` des POIs auf `null`.
|
||
|
||
```bash
|
||
curl -X DELETE http://127.0.0.1:47145/api/routes/1/audio/7
|
||
```
|
||
|
||
```json
|
||
{
|
||
"routeId": 1,
|
||
"poiId": 7,
|
||
"deleted": true
|
||
}
|
||
```
|
||
|
||
## 10. Route weich löschen und wiederherstellen
|
||
|
||
### `DELETE /api/routes/:id`
|
||
|
||
Markiert die Route als gelöscht und verschiebt das vollständige Streckenverzeichnis mit GPX, Bildern und Audio nach `storage/trash/routes/`. Die Datenbankeinträge bleiben erhalten und ihre Pfade werden auf den Papierkorb umgeschrieben.
|
||
|
||
```bash
|
||
curl -X DELETE http://127.0.0.1:47145/api/routes/1
|
||
```
|
||
|
||
```json
|
||
{
|
||
"route": {
|
||
"id": 1,
|
||
"status": "deleted",
|
||
"gpxUrl": null
|
||
},
|
||
"softDeleted": true
|
||
}
|
||
```
|
||
|
||
### `POST /api/routes/:id/restore`
|
||
|
||
Verschiebt eine gelöschte Route zurück in den aktiven Speicher und schreibt alle GPX-, Bild- und Audiopfade zurück.
|
||
|
||
Parameter: keine.
|
||
|
||
```bash
|
||
curl -X POST http://127.0.0.1:47145/api/routes/1/restore
|
||
```
|
||
|
||
## 11. Dateien über REST ausliefern
|
||
|
||
Die API veröffentlicht keine internen Speicherpfade und keine `/media/...`-Adressen. Alle in JSON ausgegebenen Datei-URLs verweisen auf stabile REST-Endpunkte:
|
||
|
||
```text
|
||
GET /api/routes/1/gpx
|
||
GET /api/routes/1/pictures/15
|
||
GET /api/routes/1/audio/7
|
||
```
|
||
|
||
Die Zuordnung lautet:
|
||
|
||
| JSON-Feld | Datei-Endpunkt |
|
||
|---|---|
|
||
| `route.gpxUrl` | `/api/routes/:id/gpx` |
|
||
| `poi.audioUrl` | `/api/routes/:id/audio/:poiId` |
|
||
| `poi.images[].url` | `/api/routes/:id/pictures/:pictureId` |
|
||
| `picture.url` | `/api/routes/:id/pictures/:pictureId` |
|
||
| `audio.url` | `/api/routes/:id/audio/:poiId` |
|
||
|
||
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.
|