# 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/` - 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/` - 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/` 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 |