Wegwichtel/docs/REST-API.md
2026-06-17 00:10:46 +02:00

20 KiB
Raw Blame History

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:

http://127.0.0.1:47145

API-Basis:

http://127.0.0.1:47145/api

Bei einer Nginx-Installation bleibt der Pfad gleich, beispielsweise:

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

{
  "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

{
  "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

{
  "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

{
  "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.

{
  "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.

curl http://127.0.0.1:47145/api/health
{
  "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:

curl http://127.0.0.1:47145/api/routes

Mit allen Query-Parametern:

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
curl http://127.0.0.1:47145/api/routes/1
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.

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
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
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
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.

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.

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:

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
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:

curl http://127.0.0.1:47145/api/routes/1/pictures

Nur Bilder des POIs 7:

curl --get http://127.0.0.1:47145/api/routes/1/pictures \
  --data-urlencode 'poiId=7'
{
  "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:

curl http://127.0.0.1:47145/api/routes/1/pictures/15 \
  --output bild-15.jpg

Metadaten statt Dateidaten abrufen:

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
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:

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:

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.

curl -X DELETE http://127.0.0.1:47145/api/routes/1/pictures/15
{
  "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
curl http://127.0.0.1:47145/api/routes/1/audio
curl --get http://127.0.0.1:47145/api/routes/1/audio \
  --data-urlencode 'poiId=7'
{
  "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:

curl http://127.0.0.1:47145/api/routes/1/audio/7 \
  --output ansage-7.mp3

Metadaten statt Dateidaten abrufen:

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
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
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.

curl -X DELETE http://127.0.0.1:47145/api/routes/1/audio/7
{
  "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.

curl -X DELETE http://127.0.0.1:47145/api/routes/1
{
  "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.

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:

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.