Wegwichtel/docs/REST-API.md
2026-06-16 23:52:37 +02:00

17 KiB
Raw Blame History

Wegwichtel REST-API

Diese Datei dokumentiert die vollständige HTTP-Schnittstelle des Wegwichtel-Servers. Die API-Basis lautet /api. Bild- und Audiodateien werden ebenfalls ausschließlich über API-Endpunkte ausgeliefert; die Clientanwendung verwendet dafür keine direkten Speicherpfade.

1. Grundlagen

Basisadressen

Lokaler Standard:

http://127.0.0.1:47145

API-Basis:

http://127.0.0.1:47145/api

Bei vorgeschaltetem Nginx bleibt der Pfad erhalten, zum Beispiel:

https://wegwichtel.example.org/api

Formate

  • JSON-Lesezugriffe liefern application/json.
  • Bild- und Audio-GETs liefern den jeweiligen binären Medientyp.
  • Schreibzugriffe ohne Datei akzeptieren JSON, URL-encoded Formulare oder multipart/form-data.
  • Datei-Uploads verwenden multipart/form-data.
  • Pro Medienrequest wird genau eine Datei verarbeitet.
  • Zeitstempel werden als SQLite- beziehungsweise ISO-8601-Text ausgegeben.
  • Die derzeitige API besitzt noch keine Authentifizierung. Schreibzugriffe dürfen nicht ungeschützt öffentlich erreichbar sein.

Uploadgrenzen und unterstützte Dateitypen

Das Dateilimit pro Upload wird mit MAX_UPLOAD_MB festgelegt und beträgt standardmäßig 50 MB.

Ressource Uploadfeld Dateitypen
GPX gpx .gpx, application/gpx+xml, XML
Bild picture JPEG, PNG, WebP
Audio audio MP3, MP4/M4A, AAC, Ogg, WAV, WebM

Fehlerformat

{
  "error": "HttpError",
  "message": "Strecke nicht gefunden."
}

Typische Statuscodes:

Status Bedeutung
200 Anfrage erfolgreich
201 Ressource wurde angelegt
400 Parameter oder Datei fehlt beziehungsweise ist ungültig
404 Route, POI oder Medienressource wurde nicht gefunden
409 Ressource existiert bereits oder widerspricht dem aktuellen Zustand
413 Datei überschreitet MAX_UPLOAD_MB
415 Dateityp wird nicht unterstützt
500 Interner Serverfehler

IDs und Medienadressierung

  • :id bezeichnet bei /routes/:id/... die Route.
  • :pictureId bezeichnet ein einzelnes Bild aus poi_images.
  • poiId bezeichnet einen POI innerhalb einer Route.
  • Ein POI kann mehrere Bilder, aber höchstens eine Audiodatei besitzen.
  • Bilder erhalten deshalb eine eigene Pfad-ID.
  • Audio verwendet keinen zusätzlichen Pfadabschnitt; der POI wird über poiId angegeben.

Beispiele verwenden 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": "/media/routes/1/route.gpx",
  "createdAt": "2026-06-16 12:00:00",
  "updatedAt": "2026-06-16 12:00:00",
  "deletedAt": null
}

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?poiId=7",
  "images": [
    {
      "id": 15,
      "caption": "Blick auf die Baumkrone",
      "sequence": 0,
      "url": "/api/routes/1/pictures/15"
    }
  ]
}

Bildmetadaten

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

Audiometadaten

{
  "routeId": 1,
  "poiId": 7,
  "poiTitle": "Die alte Eiche",
  "url": "/api/routes/1/audio?poiId=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
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 anlegen
PUT /api/pois/:id POI aktualisieren
GET /api/routes/:id/pictures Bildmetadaten auflisten
GET /api/routes/:id/pictures/:pictureId Bilddatei ausliefern
POST /api/routes/:id/pictures Bild anlegen
PUT /api/routes/:id/pictures/:pictureId Bilddatei oder Metadaten aktualisieren
DELETE /api/routes/:id/pictures/:pictureId Bild löschen
GET /api/routes/:id/audio Audio auflisten oder für einen POI ausliefern
POST /api/routes/:id/audio Audio für einen POI anlegen
PUT /api/routes/:id/audio Audio eines POIs ersetzen
DELETE /api/routes/:id/audio Audio eines POIs löschen

4. Systemzustand

GET /api/health

Parameter: keine.

curl http://127.0.0.1:47145/api/health

Beispielantwort:

{
  "ok": true,
  "service": "wegwichtel",
  "socket": "127.0.0.1:47145",
  "sqliteVersion": "3.46.1",
  "timestamp": "2026-06-16T12:00:00.000Z"
}

5. Routen

GET /api/routes

Listet aktive Routen auf. Werden lat und lon gemeinsam angegeben, berechnet der Server die Entfernung zum Routenstart, filtert mit radiusKm und sortiert nach Entfernung.

Query-Parameter:

Parameter Typ Pflicht Standard Beschreibung
lat Dezimalzahl nein Breitengrad des Ausgangspunkts
lon Dezimalzahl nein Längengrad des Ausgangspunkts
radiusKm Dezimalzahl nein DEFAULT_ROUTE_RADIUS_KM maximaler Abstand zum Routenstart
includeDeleted true/false nein false gelöschte Routen einschließen

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

Pfadparameter:

Parameter Typ Pflicht Beschreibung
id positive Ganzzahl ja Route

Query-Parameter:

Parameter Typ Pflicht Standard Beschreibung
includeDeleted true/false nein false auch eine weich gelöschte Route lesen
curl http://127.0.0.1:47145/api/routes/1

Die Antwort enthält points, pois, images[].url und audioUrl. Die Medien-URLs zeigen auf die API-Endpunkte.

POST /api/routes

Content-Type: multipart/form-data.

Feld Typ Pflicht Beschreibung
gpx Datei ja GPX-Datei
name Text nein, wenn GPX einen Namen enthält Anzeigename
slug Text nein gewünschter URL-tauglicher Bezeichner
description Text nein Beschreibung
schoolName Text nein Schule oder Projekt
curl -X POST http://127.0.0.1:47145/api/routes \
  -F 'name=Schulwald-Runde' \
  -F 'slug=schulwald-runde' \
  -F 'description=Naturkundlicher Rundweg' \
  -F 'schoolName=Beispielschule' \
  -F 'gpx=@route.gpx;type=application/gpx+xml'

PUT /api/routes/:id

Aktualisiert Routendaten. Eine neue GPX-Datei ersetzt die gespeicherten Trackpunkte.

Feld Typ Pflicht Beschreibung
name Text nein neuer Name
description Text nein neue Beschreibung
schoolName Text nein neue Schule
gpx Datei nein vollständige Ersatzstrecke
curl -X PUT http://127.0.0.1:47145/api/routes/1 \
  -F 'name=Schulwald-Runde 2026' \
  -F 'description=Überarbeitete Route' \
  -F 'schoolName=Beispielschule' \
  -F 'gpx=@route-neu.gpx;type=application/gpx+xml'

POST /api/routes/:id/append

Hängt GPX-Punkte an eine Route an.

Feld Typ Pflicht Beschreibung
gpx Datei ja GPX-Datei mit zusätzlichen Punkten
curl -X POST http://127.0.0.1:47145/api/routes/1/append \
  -F 'gpx=@erweiterung.gpx;type=application/gpx+xml'

DELETE /api/routes/:id

Markiert die Route als gelöscht und verschiebt GPX, Bilder und Audio in den Papierkorb.

curl -X DELETE http://127.0.0.1:47145/api/routes/1

POST /api/routes/:id/restore

Stellt eine weich gelöschte Route einschließlich ihrer Dateien wieder her.

curl -X POST http://127.0.0.1:47145/api/routes/1/restore

6. POIs

GET /api/routes/:id/pois

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

GET /api/pois/:id

curl http://127.0.0.1:47145/api/pois/7

POST /api/routes/:id/pois

Akzeptiert JSON, URL-encoded Formulare oder Multipart ohne Mediendateien.

Feld Typ Pflicht Standard Beschreibung
title Text nein Unbenannter POI Titel
description Text nein leer Beschreibung
lat Dezimalzahl ja Breitengrad
lon Dezimalzahl ja Längengrad
triggerRadiusM Dezimalzahl nein DEFAULT_POI_TRIGGER_METERS Aktivierungsradius in Metern
sequence Ganzzahl ≥ 0 nein 0 Reihenfolge
curl -X POST http://127.0.0.1:47145/api/routes/1/pois \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "Die alte Eiche",
    "description": "Informationen zur Baumart",
    "lat": 52.5208,
    "lon": 13.4070,
    "triggerRadiusM": 60,
    "sequence": 2
  }'

PUT /api/pois/:id

Alle Felder sind optional; nicht angegebene Werte bleiben erhalten.

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": 1
  }'

7. Bilder

Ein POI kann mehrere Bilder besitzen. Die Collection wird über die Route adressiert; jedes Bild hat zusätzlich eine pictureId.

GET /api/routes/:id/pictures

Liefert Bildmetadaten als JSON.

Query-Parameter:

Parameter Typ Pflicht Beschreibung
poiId positive Ganzzahl nein auf Bilder eines POIs einschränken

Alle Bilder der Route:

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

Nur Bilder von POI 7:

curl --get http://127.0.0.1:47145/api/routes/1/pictures \
  --data-urlencode 'poiId=7'

Beispielantwort:

{
  "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 die Bilddatei binär mit dem passenden Content-Type, beispielsweise image/jpeg, image/png oder image/webp.

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

Bildmetadaten werden über GET /api/routes/:id/pictures gelesen.

POST /api/routes/:id/pictures

Content-Type: multipart/form-data.

Feld Typ Pflicht Standard Beschreibung
picture Datei ja Bilddatei
poiId positive Ganzzahl ja zugehöriger POI
caption Text nein leer sichtbare Bildbeschreibung und Alternativtext
sequence Ganzzahl ≥ 0 nein nächste freie Position 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'

Die Antwort enthält Metadaten. Der Location-Header zeigt auf /api/routes/1/pictures/:pictureId.

PUT /api/routes/:id/pictures/:pictureId

Aktualisiert Metadaten und optional die Datei. Der Request kann JSON ohne Datei oder Multipart mit Datei verwenden.

Feld Typ Pflicht Beschreibung
picture Datei nein Ersatzdatei
poiId positive Ganzzahl nein Bild einem anderen POI derselben Route zuordnen
caption Text nein Bildbeschreibung
sequence Ganzzahl ≥ 0 nein Reihenfolge

Nur Metadaten ä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 Metadaten ersetzen:

curl -X PUT http://127.0.0.1:47145/api/routes/1/pictures/15 \
  -F 'poiId=7' \
  -F 'caption=Neue Bildbeschreibung' \
  -F 'sequence=1' \
  -F 'picture=@eiche-neu.webp;type=image/webp'

DELETE /api/routes/:id/pictures/:pictureId

Löscht den Datenbankeintrag und die einzelne Bilddatei.

curl -X DELETE http://127.0.0.1:47145/api/routes/1/pictures/15

8. Audio

Pro POI existiert höchstens eine Audiodatei. Deshalb gibt es keine zusätzliche Audio-ID und keinen Pfad wie /audio/:poiId. Alle Operationen verwenden /api/routes/:id/audio; der konkrete POI wird mit poiId angegeben.

GET /api/routes/:id/audio

Ohne poiId liefert der Endpunkt alle Audiometadaten der Route als JSON:

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

Beispielantwort:

{
  "audio": [
    {
      "routeId": 1,
      "poiId": 7,
      "poiTitle": "Die alte Eiche",
      "url": "/api/routes/1/audio?poiId=7",
      "updatedAt": "2026-06-16 12:35:00"
    }
  ]
}

Mit poiId liefert derselbe Endpunkt die Audiodatei binär:

Query-Parameter Typ Pflicht für Dateiausgabe Beschreibung
poiId positive Ganzzahl ja POI, dessen Audio geliefert wird
curl --get http://127.0.0.1:47145/api/routes/1/audio \
  --data-urlencode 'poiId=7' \
  --output ansage.mp3

Der Response-Content-Type entspricht dem gespeicherten Format, beispielsweise audio/mpeg oder audio/ogg.

POST /api/routes/:id/audio

Legt die Audiodatei für einen POI an. Besteht bereits eine Datei, antwortet der Server mit 409; zum Ersetzen wird PUT verwendet.

Feld Typ Pflicht Beschreibung
audio Datei ja Audiodatei
poiId positive Ganzzahl ja zugehöriger POI
curl -X POST http://127.0.0.1:47145/api/routes/1/audio \
  -F 'poiId=7' \
  -F 'audio=@ansage.mp3;type=audio/mpeg'

Der Location-Header lautet beispielsweise:

/api/routes/1/audio?poiId=7

PUT /api/routes/:id/audio

Ersetzt eine vorhandene Audiodatei. poiId darf als Query-Parameter oder als Multipart-Feld angegeben werden.

Query-Variante:

curl -X PUT 'http://127.0.0.1:47145/api/routes/1/audio?poiId=7' \
  -F 'audio=@ansage-neu.ogg;type=audio/ogg'

Multipart-Variante:

curl -X PUT http://127.0.0.1:47145/api/routes/1/audio \
  -F 'poiId=7' \
  -F 'audio=@ansage-neu.ogg;type=audio/ogg'
Parameter Ort Typ Pflicht Beschreibung
poiId Query oder Formular positive Ganzzahl ja POI
audio Formular Datei ja Ersatzdatei

DELETE /api/routes/:id/audio

Löscht die Audiodatei des angegebenen POIs und setzt audioUrl anschließend auf null.

Empfohlen als Query-Parameter:

curl -X DELETE 'http://127.0.0.1:47145/api/routes/1/audio?poiId=7'

Alternativ als JSON-Body:

curl -X DELETE http://127.0.0.1:47145/api/routes/1/audio \
  -H 'Content-Type: application/json' \
  -d '{ "poiId": 7 }'

9. Verwendung durch die Clientanwendung

Die Clientanwendung konstruiert Medienadressen ausschließlich über das API-Modul:

Wegwichtel.Api.pictureUrl(routeId, pictureId);
// /api/routes/1/pictures/15

Wegwichtel.Api.audioUrl(routeId, poiId);
// /api/routes/1/audio?poiId=7

Die Diashow setzt die API-Bildadresse als src des Bildes. Der HTML5-Audioplayer setzt die API-Audioadresse als src und lädt sie mit preload="auto". Das Erreichen eines POIs startet keine automatische Wiedergabe.

10. Nginx-Hinweis

Nginx sollte /api/ vollständig an den Node.js-Prozess weiterleiten. Da Bilder und Audio über /api ausgeliefert werden, muss der Proxy binäre Responses unverändert durchreichen:

location ^~ /api/ {
    proxy_pass http://127.0.0.1:47145;
    proxy_http_version 1.1;
    proxy_set_header Host              $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 120s;
}

Für Uploads muss außerdem client_max_body_size mindestens so groß wie MAX_UPLOAD_MB gewählt werden.