Reworked pause and stop buttons and documentation

This commit is contained in:
Florian Zumpe
2026-06-16 17:01:53 +02:00
parent 4529749baa
commit 40f230864b
6 changed files with 710 additions and 47 deletions
+636
View File
@@ -0,0 +1,636 @@
# Wegwichtel REST-API
Diese Datei dokumentiert die vollständige HTTP-Schnittstelle des aktuellen Wegwichtel-Servers. Die API wird unter `/api` bereitgestellt. Aktive GPX-, Bild- und Audiodateien werden zusätzlich unter `/media` ausgeliefert.
## 1. Grundlagen
### Basisadresse
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
- Lesezugriffe liefern JSON.
- Routen- und POI-Schreibzugriffe mit Dateien verwenden `multipart/form-data`.
- Fehler werden als JSON ausgegeben.
- Zeitstempel stammen aus SQLite beziehungsweise JavaScript und werden als Text oder ISO-8601-Zeitstempel ausgegeben.
- Die aktuelle API besitzt noch keine Authentifizierung. Schreibzugriffe dürfen deshalb nicht ungeschützt öffentlich erreichbar sein.
### Allgemeines Fehlerformat
```json
{
"error": "HttpError",
"message": "Strecke nicht gefunden."
}
```
Bei Detailinformationen kann zusätzlich `details` enthalten sein:
```json
{
"error": "HttpError",
"message": "Die GPX-Datei ist kein gültiges XML.",
"details": "Parsermeldung"
}
```
Typische Statuscodes:
| Status | Bedeutung |
|---:|---|
| `200` | Anfrage erfolgreich |
| `201` | Ressource wurde angelegt |
| `400` | Parameter oder Upload unvollständig beziehungsweise ungültig |
| `404` | Route, POI oder Datei wurde nicht gefunden |
| `409` | Dateisystemzustand verhindert Löschen oder Wiederherstellen |
| `413` | Eine hochgeladene Datei überschreitet das konfigurierte Limit |
| `415` | Dateityp wird nicht unterstützt |
| `500` | Interner Serverfehler |
### IDs
Die Pfadparameter `:id` sind positive, von SQLite erzeugte Ganzzahlen. Beispiele verwenden überwiegend Route `1` und POI `7`.
### Uploadgrenzen und Dateitypen
Das Dateilimit pro Datei wird durch `MAX_UPLOAD_MB` festgelegt und beträgt standardmäßig `50 MB`. Multer akzeptiert höchstens 25 Dateien pro Request.
Erlaubte Uploadtypen:
- GPX/XML: `.gpx`, `application/gpx+xml`, `application/xml`, `text/xml`
- Bilder: JPEG, PNG, WebP
- Audio: MP3, MP4/M4A, AAC, Ogg, WAV, WebM
Beim Anlegen oder Aktualisieren eines POIs gelten zusätzlich:
- höchstens eine Datei im Feld `audio`
- höchstens 20 Dateien im Feld `images`
## 2. Datenmodelle
### Kurzfassung einer Route
```json
{
"id": 1,
"slug": "schulwald-runde",
"name": "Schulwald-Runde",
"description": "Naturkundlicher Rundweg",
"schoolName": "Beispielschule",
"status": "active",
"start": { "lat": 52.5208, "lon": 13.4070 },
"center": { "lat": 52.5210, "lon": 13.4080 },
"bounds": {
"minLat": 52.5208,
"minLon": 13.4070,
"maxLat": 52.5212,
"maxLon": 13.4090
},
"distanceM": 842.6,
"elevationGainM": 14.2,
"pointCount": 87,
"gpxUrl": "/media/routes/1/route.gpx",
"createdAt": "2026-06-16 12:00:00",
"updatedAt": "2026-06-16 12:00:00",
"deletedAt": null,
"proximityM": 324.8
}
```
`proximityM` ist `null`, wenn beim Listenaufruf keine gültige Position übergeben wurde. Bei gelöschten Routen ist `gpxUrl` `null`, weil Dateien im Papierkorb nicht öffentlich ausgeliefert werden.
### Vollständige Route
`GET /api/routes/:id` ergänzt die Kurzfassung um `points` und `pois`:
```json
{
"id": 1,
"name": "Schulwald-Runde",
"points": [
{
"sequence": 0,
"lat": 52.5208,
"lon": 13.4070,
"elevation": 71.4,
"recordedAt": "2026-06-16T09:00:00Z"
}
],
"pois": []
}
```
### POI
```json
{
"id": 7,
"routeId": 1,
"title": "Die alte Eiche",
"description": "Hier wird das Alter der Eiche erklärt.",
"lat": 52.5208,
"lon": 13.4070,
"triggerRadiusM": 60,
"sequence": 2,
"audioUrl": "/media/routes/1/audio/uuid.mp3",
"images": [
{
"id": 15,
"caption": "",
"sequence": 0,
"url": "/media/routes/1/images/uuid.jpg"
}
]
}
```
## 3. Systemzustand
### `GET /api/health`
Prüft den Node.js-Prozess und eine SQLite-Abfrage.
Parameter: keine.
```bash
curl http://127.0.0.1:47145/api/health
```
Beispielantwort:
```json
{
"ok": true,
"service": "wegwichtel",
"socket": "127.0.0.1:47145",
"sqliteVersion": "3.46.1",
"timestamp": "2026-06-16T12:00:00.000Z"
}
```
## 4. Routen lesen
### `GET /api/routes`
Liefert standardmäßig alle aktiven Routen alphabetisch. Werden `lat` und `lon` gemeinsam übergeben, berechnet der Server die Entfernung zum Startpunkt, filtert anhand `radiusKm` und sortiert nach Entfernung.
#### Query-Parameter
| Parameter | Typ | Pflicht | Standard | Beschreibung |
|---|---|---:|---:|---|
| `lat` | Dezimalzahl | nein | – | Breitengrad der aktuellen Position; nur zusammen mit `lon` wirksam |
| `lon` | Dezimalzahl | nein | – | Längengrad der aktuellen Position; nur zusammen mit `lat` wirksam |
| `radiusKm` | Dezimalzahl | nein | `DEFAULT_ROUTE_RADIUS_KM`, standardmäßig `25` | maximaler Abstand zum Routenstart; nur bei gültigem `lat` und `lon` wirksam |
| `includeDeleted` | Boolean-Text | nein | `false` | nur der exakte Wert `true` schließt gelöschte Routen ein |
Alle aktiven Routen:
```bash
curl http://127.0.0.1:47145/api/routes
```
Alle Parameter in einem Beispiel:
```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'
```
Antwort:
```json
{
"routes": [
{
"id": 1,
"name": "Schulwald-Runde",
"status": "active",
"proximityM": 324.8
}
]
}
```
Hinweise:
- Eine ungültige Zahl wird intern wie ein nicht gesetzter Wert behandelt.
- Der Positionsfilter wird nur aktiv, wenn sowohl `lat` als auch `lon` gültige Zahlen sind.
- `radiusKm` wird derzeit nicht auf einen Mindest- oder Höchstwert begrenzt.
### `GET /api/routes/:id`
Liefert eine Route mit sämtlichen GPX-Punkten und POIs.
#### Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `id` | Ganzzahl | ja | ID der Route |
#### Query-Parameter
| Parameter | Typ | Pflicht | Standard | Beschreibung |
|---|---|---:|---:|---|
| `includeDeleted` | Boolean-Text | nein | `false` | mit `true` kann auch eine als gelöscht markierte Route gelesen werden |
Aktive Route:
```bash
curl http://127.0.0.1:47145/api/routes/1
```
Gelöschte Route ausdrücklich einschließen:
```bash
curl 'http://127.0.0.1:47145/api/routes/1?includeDeleted=true'
```
Bei einer gelöschten Route sind Medien-URLs `null`, weil der Papierkorb nicht unter `/media` veröffentlicht wird.
### `GET /api/routes/:id/pois`
Liefert alle POIs einer aktiven Route in der Reihenfolge `sequence`, anschließend `id`.
#### Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `id` | Ganzzahl | ja | ID der aktiven Route |
Weitere Parameter: keine.
```bash
curl http://127.0.0.1:47145/api/routes/1/pois
```
Antwort:
```json
{
"pois": [
{
"id": 7,
"routeId": 1,
"title": "Die alte Eiche",
"sequence": 2,
"audioUrl": "/media/routes/1/audio/uuid.mp3",
"images": []
}
]
}
```
## 5. POIs lesen
### `GET /api/pois/:id`
Liefert einen einzelnen POI einschließlich Bild- und Audio-URLs. Der POI muss zu einer aktiven Route gehören.
#### Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `id` | Ganzzahl | ja | ID des POIs |
Weitere Parameter: keine.
```bash
curl http://127.0.0.1:47145/api/pois/7
```
## 6. Route anlegen
### `POST /api/routes`
Legt eine neue Route aus einer GPX-Datei an. Trackpunkte, Streckenlänge, Höhengewinn, Startpunkt, Mittelpunkt und Grenzen werden aus der Datei berechnet.
Content-Type: `multipart/form-data`
#### Formular- und Dateiparameter
| Feld | Typ | Pflicht | Standard | Beschreibung |
|---|---|---:|---|---|
| `gpx` | Datei | ja | – | GPX-Datei mit mindestens einem verwertbaren `trkpt` |
| `name` | Text | bedingt | GPX-Track- oder Metadatenname | Routenname; erforderlich, falls die GPX-Datei keinen Namen enthält |
| `slug` | Text | nein | aus `name` abgeleitet | URL-freundliche interne Kennung; Kollisionen erhalten automatisch `-2`, `-3` usw. |
| `description` | Text | nein | leer | Beschreibung der Route |
| `schoolName` | Text | nein | leer | Name der Schule oder Einrichtung |
Beispiel mit allen Parametern:
```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:
- Status `201 Created`
- Header `Location: /api/routes/<id>`
- Body: vollständige neu angelegte Route
## 7. Route vollständig aktualisieren
### `PUT /api/routes/:id`
Ändert Metadaten einer aktiven Route. Eine optionale GPX-Datei ersetzt alle bisherigen Trackpunkte und die Datei `route.gpx`.
Content-Type: `multipart/form-data`
#### Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `id` | Ganzzahl | ja | ID der aktiven Route |
#### Formular- und Dateiparameter
| Feld | Typ | Pflicht | Verhalten bei Auslassung | Beschreibung |
|---|---|---:|---|---|
| `name` | Text | nein | alter Wert bleibt | neuer Routenname |
| `description` | Text | nein | alter Wert bleibt | neue Beschreibung; leere Zeichenfolge löscht den Inhalt |
| `schoolName` | Text | nein | alter Wert bleibt | neuer Schulname; leere Zeichenfolge löscht den Inhalt |
| `gpx` | Datei | nein | Track bleibt unverändert | ersetzt GPX-Datei, Trackpunkte und berechnete Kennzahlen vollständig |
`slug` kann über diesen Endpunkt derzeit nicht geändert werden.
Beispiel mit allen Parametern:
```bash
curl -X PUT http://127.0.0.1:47145/api/routes/1 \
-F 'name=Schulwald-Runde – überarbeitet' \
-F 'description=Neue Wegführung ab dem Schulhof' \
-F 'schoolName=Beispielschule Nord' \
-F 'gpx=@examples/sample-route.gpx;type=application/gpx+xml'
```
Nur die Beschreibung ändern:
```bash
curl -X PUT http://127.0.0.1:47145/api/routes/1 \
-F 'description=Nur dieser Wert wird geändert.'
```
Antwort: vollständige aktualisierte Route.
## 8. GPX-Punkte an eine Route anhängen
### `POST /api/routes/:id/append`
Hängt sämtliche verwertbaren Trackpunkte einer GPX-Datei an eine aktive Route an und berechnet die Streckenkennzahlen neu.
Content-Type: `multipart/form-data`
#### Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `id` | Ganzzahl | ja | ID der aktiven Route |
#### Dateiparameter
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `gpx` | Datei | ja | GPX-Datei mit den anzuhängenden Trackpunkten |
```bash
curl -X POST http://127.0.0.1:47145/api/routes/1/append \
-F 'gpx=@weiterer-abschnitt.gpx;type=application/gpx+xml'
```
Hinweis: Die Punkte werden in SQLite ergänzt. Die gespeicherte Originaldatei `route.gpx` wird durch diesen Endpunkt derzeit nicht zu einer konsolidierten GPX-Datei erweitert.
## 9. POI anlegen
### `POST /api/routes/:id/pois`
Legt einen POI für eine aktive Route an und speichert optional eine Audioansage und mehrere Bilder.
Content-Type: `multipart/form-data`
#### Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `id` | Ganzzahl | ja | ID der aktiven Route |
#### Formular- und Dateiparameter
| Feld | Typ | Pflicht | Standard | Beschreibung |
|---|---|---:|---|---|
| `title` | Text | nein | `Unbenannter POI` | Titel der Station |
| `description` | Text | nein | leer | Beschreibung der Station |
| `lat` | Dezimalzahl | ja | – | Breitengrad des POIs |
| `lon` | Dezimalzahl | ja | – | Längengrad des POIs |
| `triggerRadiusM` | Dezimalzahl | nein | `DEFAULT_POI_TRIGGER_METERS`, standardmäßig `80` | Entfernung in Metern, ab der die Station automatisch ausgelöst wird |
| `sequence` | Ganzzahl | nein | `0` | Sortierreihenfolge innerhalb der Route |
| `audio` | Datei | nein | keine | eine Audioansage |
| `images` | Datei, wiederholbar | nein | keine | bis zu 20 Bilder; jedes Bild wird als eigenes Feld `images` gesendet |
Beispiel mit allen Parametern:
```bash
curl -X POST http://127.0.0.1:47145/api/routes/1/pois \
-F 'title=Die alte Eiche' \
-F 'description=Hier wird das Alter der Eiche erklärt.' \
-F 'lat=52.5208' \
-F 'lon=13.4070' \
-F 'triggerRadiusM=60' \
-F 'sequence=2' \
-F 'audio=@ansage.mp3;type=audio/mpeg' \
-F 'images=@eiche-1.jpg;type=image/jpeg' \
-F 'images=@eiche-2.webp;type=image/webp'
```
Erfolg:
- Status `201 Created`
- Header `Location: /api/pois/<id>`
- Body: neu angelegter POI
Bildunterschriften können in der aktuellen API noch nicht per Parameter gesetzt werden und bleiben leer.
## 10. POI aktualisieren und Medien ergänzen
### `PUT /api/pois/:id`
Ändert einen POI einer aktiven Route. Eine neue Audiodatei ersetzt den in der Datenbank referenzierten Audiopfad. Neue Bilder werden an die vorhandene Bilderliste angehängt.
Content-Type: `multipart/form-data`
#### Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `id` | Ganzzahl | ja | ID des POIs |
#### Formular- und Dateiparameter
| Feld | Typ | Pflicht | Verhalten bei Auslassung | Beschreibung |
|---|---|---:|---|---|
| `title` | Text | nein | alter Wert bleibt | neuer Titel |
| `description` | Text | nein | alter Wert bleibt | neue Beschreibung |
| `lat` | Dezimalzahl | nein | alter Wert bleibt | neuer Breitengrad |
| `lon` | Dezimalzahl | nein | alter Wert bleibt | neuer Längengrad |
| `triggerRadiusM` | Dezimalzahl | nein | alter Wert bleibt | neuer automatischer Auslöseradius in Metern |
| `sequence` | Ganzzahl | nein | alter Wert bleibt | neue Sortierreihenfolge |
| `audio` | Datei | nein | alte Referenz bleibt | neue Audioansage; maximal eine Datei |
| `images` | Datei, wiederholbar | nein | Bilder bleiben unverändert | bis zu 20 zusätzliche Bilder |
Beispiel mit allen Parametern:
```bash
curl -X PUT http://127.0.0.1:47145/api/pois/7 \
-F 'title=Die sehr alte Eiche' \
-F 'description=Überarbeitete Ansage und neue Fotos.' \
-F 'lat=52.5209' \
-F 'lon=13.4072' \
-F 'triggerRadiusM=45' \
-F 'sequence=3' \
-F 'audio=@ansage-neu.ogg;type=audio/ogg' \
-F 'images=@eiche-3.png;type=image/png' \
-F 'images=@eiche-4.jpg;type=image/jpeg'
```
Wichtige aktuelle Einschränkungen:
- Einzelne Bilder können noch nicht per API gelöscht, umsortiert oder beschriftet werden.
- Neu hochgeladene Bilder werden hinter vorhandenen Bildern einsortiert.
- Beim Ersetzen der Audio-Referenz wird die vorherige Audiodatei derzeit nicht automatisch aus dem aktiven Verzeichnis entfernt.
## 11. Route zum Löschen markieren
### `DELETE /api/routes/:id`
Markiert eine aktive Route als gelöscht und verschiebt ihr gesamtes Verzeichnis einschließlich GPX, Bildern und Audio nach `storage/trash/routes`. Die Datenbankzeilen bleiben erhalten; alle gespeicherten Pfade werden auf den Papierkorb umgeschrieben.
#### Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `id` | Ganzzahl | ja | ID der aktiven Route |
Weitere Parameter: keine.
```bash
curl -X DELETE http://127.0.0.1:47145/api/routes/1
```
Antwort:
```json
{
"route": {
"id": 1,
"status": "deleted",
"gpxUrl": null,
"deletedAt": "2026-06-16 12:30:00"
},
"softDeleted": true
}
```
Es werden keine Dateien endgültig entfernt.
## 12. Route wiederherstellen
### `POST /api/routes/:id/restore`
Stellt eine als gelöscht markierte Route wieder her, verschiebt ihr Verzeichnis nach `storage/active/routes/<id>` zurück und korrigiert alle GPX-, Bild- und Audiopfade.
#### Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---:|---|
| `id` | Ganzzahl | ja | ID der gelöschten Route |
Weitere Parameter: keine. Der Request besitzt keinen Body.
```bash
curl -X POST http://127.0.0.1:47145/api/routes/1/restore
```
Antwort:
```json
{
"route": {
"id": 1,
"status": "active",
"gpxUrl": "/media/routes/1/route.gpx",
"deletedAt": null
},
"restored": true
}
```
## 13. Medien abrufen
Aktive Dateien werden nicht unter `/api`, sondern statisch unter `/media` ausgeliefert. Die benötigten URLs stehen in `gpxUrl`, `audioUrl` und `images[].url`.
Parameter: keine zusätzlichen Query- oder Formularparameter. Der komplette Pfad stammt aus der jeweiligen API-Antwort.
GPX-Datei:
```bash
curl --output route.gpx \
http://127.0.0.1:47145/media/routes/1/route.gpx
```
Audio:
```bash
curl --output ansage.mp3 \
http://127.0.0.1:47145/media/routes/1/audio/DATEINAME.mp3
```
Bild:
```bash
curl --output station.jpg \
http://127.0.0.1:47145/media/routes/1/images/DATEINAME.jpg
```
Nur `storage/active` wird veröffentlicht. Dateien gelöschter Routen unter `storage/trash` sind nicht über HTTP erreichbar.
## 14. Endpunktübersicht
| Methode | Pfad | Zweck |
|---|---|---|
| `GET` | `/api/health` | Server und SQLite prüfen |
| `GET` | `/api/routes` | Routen auflisten und optional räumlich filtern |
| `GET` | `/api/routes/:id` | vollständige Route lesen |
| `GET` | `/api/routes/:id/pois` | POIs einer Route lesen |
| `GET` | `/api/pois/:id` | einzelnen POI lesen |
| `POST` | `/api/routes` | Route aus GPX anlegen |
| `PUT` | `/api/routes/:id` | Route aktualisieren oder GPX ersetzen |
| `POST` | `/api/routes/:id/append` | GPX-Punkte anhängen |
| `POST` | `/api/routes/:id/pois` | POI und Medien anlegen |
| `PUT` | `/api/pois/:id` | POI aktualisieren und Medien ergänzen |
| `DELETE` | `/api/routes/:id` | Route in den Papierkorb verschieben |
| `POST` | `/api/routes/:id/restore` | Route wiederherstellen |
| `GET` | `/media/routes/...` | aktive GPX-, Bild- und Audiodateien abrufen |