30 KiB
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=trueliefern die Bild- und Audioendpunkte stattdessen JSON-Metadaten. - Schreibzugriffe ohne Dateien akzeptieren
application/json,application/x-www-form-urlencodedodermultipart/form-data. - GPX-Dateien werden ausschließlich als
multipart/form-dataübertragen. - Bild- und Audioendpunkte akzeptieren wahlweise
multipart/form-dataoder ein JSON-Objekt mit Base64-kodierten Dateidaten. - Der Server ermittelt den tatsächlichen MIME-Typ aus dem Dateiinhalt. Dateiname, Dateiendung, Multipart-
Content-Type, JSON-Felder und Data-URL-Präfixe werden nicht als Typnachweis verwendet. - Für JSON wird
Content-Type: application/jsonempfohlen.Content-Type: text/jsonwird aus Kompatibilitätsgründen ebenfalls akzeptiert. - Ein leeres JSON-Objekt (
{}) enthält keine Datei und kann deshalb keinen Bild- oder Audio-Upload ausführen. - Pro Medien-Request wird genau eine Bild- beziehungsweise Audiodatei verarbeitet.
- Base64 vergrößert die Requestgröße um ungefähr ein Drittel und benötigt beim Verarbeiten zusätzlichen Arbeitsspeicher. Für große Dateien ist
multipart/form-datavorzuziehen. - Zeitstempel werden als SQLite- oder ISO-8601-Text ausgegeben.
- Die Node.js-Anwendung besitzt keine eigene Authentifizierung. Ein vorgeschalteter Webserver kann Basic Auth erzwingen; die mitgelieferten Python-Werkzeuge erkennen
401 Unauthorized, fragen Zugangsdaten ab und wiederholen den Request.
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-XML mit <gpx>-Wurzelelement |
| Bilder | JPEG, PNG, WebP |
| Audio | MP3, MP4/M4A, AAC, Ogg, WAV, WebM |
Die Uploadfelder heißen:
| Ressource | Multipart-Feld beziehungsweise JSON-Eigenschaft |
|---|---|
| GPX | gpx |
| einzelnes Bild | picture |
| einzelne Audiodatei | audio |
Bei einem JSON-Medienupload ist picture beziehungsweise audio ein Objekt:
| Eigenschaft | Typ | Pflicht | Beschreibung |
|---|---|---|---|
filename |
Text | nein | ursprünglicher Dateiname für Protokollierung; die Endung wird nicht zur Typbestimmung verwendet |
base64 |
Text | ja, sofern dataUrl fehlt |
reiner Base64-Inhalt ohne Präfix |
dataUrl |
Text | alternativ zu base64 |
vollständige Base64-Data-URL; der dort genannte Medientyp wird ignoriert |
base64 und dataUrl sind Alternativen. Der Server erkennt Format und Speicherendung ausschließlich anhand der dekodierten Bytes. Ein vom Client mitgesendetes contentType- oder mimeType-Feld wird ignoriert und ist nicht erforderlich.
Inhaltsbasierte Dateityperkennung
Binäre Bild- und Audiodateien werden mit file-type anhand ihrer Magic Bytes analysiert. Für Audio-/Video-Container wird zusätzlich @file-type/av verwendet, damit beispielsweise M4A und WebM möglichst zuverlässig als Audio oder Video unterschieden werden. GPX ist ein textbasiertes XML-Format und wird deshalb separat als UTF-8 gelesen und anhand des <gpx>-Wurzelelements validiert; anschließend übernimmt der vorhandene GPX-Parser die fachliche Prüfung.
Daraus folgen zwei wichtige Regeln:
- Eine als
image/pngdeklarierte Textdatei wird mit415 Unsupported Media Typeabgewiesen. - Eine echte PNG-Datei wird auch dann akzeptiert, wenn sie
datei.binheißt oder im Multipart-Request kein Datei-Content-Typeangegeben ist.
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 |
401 |
vorgeschalteter Webserver verlangt Authentifizierung |
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.
:idbezeichnet bei/routes/:id/...die Route.:pictureIdbezeichnet einen Datensatz auspoi_images.:poiIdbezeichnet 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/pois/7/audio",
"images": [
{
"id": 15,
"caption": "Blick auf die Baumkrone",
"sequence": 0,
"url": "/api/routes/1/pois/7/pictures/15"
}
]
}
Bildressource
{
"id": 15,
"routeId": 1,
"poiId": 7,
"poiTitle": "Die alte Eiche",
"caption": "Blick auf die Baumkrone",
"sequence": 0,
"url": "/api/routes/1/pois/7/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/pois/7/audio",
"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/routes/:routeId/pois/:poiId |
einzelnen POI der Route lesen |
POST |
/api/routes/:id/pois |
POI-Metadaten anlegen |
PUT |
/api/routes/:routeId/pois/:poiId |
POI-Metadaten aktualisieren |
DELETE |
/api/routes/:routeId/pois/:poiId |
POI einschließlich Bildern und Audio löschen |
GET |
/api/routes/:routeId/pois/:poiId/pictures |
Bilder eines POIs auflisten |
GET |
/api/routes/:routeId/pois/:poiId/pictures/:pictureId |
Bilddatei ausliefern; optional Metadaten mit ?metadata=true |
POST |
/api/routes/:routeId/pois/:poiId/pictures |
einzelnes Bild für den POI hochladen |
PUT |
/api/routes/:routeId/pois/:poiId/pictures/:pictureId |
Bilddatei oder Metadaten aktualisieren |
DELETE |
/api/routes/:routeId/pois/:poiId/pictures/:pictureId |
einzelnes Bild löschen |
GET |
/api/routes/:routeId/pois/:poiId/audio |
Audiodatei ausliefern; optional Metadaten mit ?metadata=true |
POST |
/api/routes/:routeId/pois/:poiId/audio |
Audiodatei für den POI anlegen |
PUT |
/api/routes/:routeId/pois/:poiId/audio |
Audiodatei des POIs ersetzen |
DELETE |
/api/routes/:routeId/pois/:poiId/audio |
Audiodatei des 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; der Dateiname ist unerheblich |
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'
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'
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'
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/routes/:routeId/pois/:poiId
Liefert einen einzelnen POI einschließlich seiner aktuellen Bild- und Audio-URLs. Route und POI werden gemeinsam geprüft; der POI muss zur angegebenen aktiven Route gehören.
| Pfadparameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
routeId |
Ganzzahl | ja | ID der aktiven Route |
poiId |
Ganzzahl | ja | ID des POIs innerhalb dieser Route |
curl http://127.0.0.1:47145/api/routes/1/pois/7
Eine unbekannte Route, ein unbekannter POI oder eine falsche Route-POI-Kombination liefert 404 Not Found.
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/routes/<routeId>/pois/<poiId>.
PUT /api/routes/:routeId/pois/:poiId
Aktualisiert ausschließlich POI-Metadaten. Nicht übergebene Werte bleiben erhalten. Route und POI werden gemeinsam validiert; ein POI kann über diesen Endpunkt keiner anderen Route zugeordnet werden.
| Pfadparameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
routeId |
Ganzzahl | ja | ID der aktiven Route |
poiId |
Ganzzahl | ja | ID des POIs innerhalb dieser Route |
| 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/routes/1/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
}'
DELETE /api/routes/:routeId/pois/:poiId
Löscht den POI-Datensatz sowie alle zugehörigen Bilddatensätze, Bilddateien und die optionale Audiodatei. Dieser Vorgang ist im Gegensatz zum Soft Delete einer vollständigen Route nicht wiederherstellbar.
curl -X DELETE http://127.0.0.1:47145/api/routes/1/pois/7
{
"id": 7,
"routeId": 1,
"deleted": true
}
8. Bilder eines POIs einzeln verwalten
Die Route und der POI sind Bestandteil jedes Bildpfades. Dadurch ist die Zuordnung eindeutig und beim Upload muss keine zusätzliche poiId übergeben werden.
Gemeinsame Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
routeId |
positive Ganzzahl | ja | ID der aktiven Route |
poiId |
positive Ganzzahl | ja | ID eines POIs, der zu dieser Route gehört |
pictureId |
positive Ganzzahl | nur bei Einzelressourcen | ID des Bildes, das zu diesem POI gehört |
GET /api/routes/:routeId/pois/:poiId/pictures
Liefert alle Bilder des angegebenen POIs in Diashow-Reihenfolge.
curl http://127.0.0.1:47145/api/routes/1/pois/7/pictures
{
"pictures": [
{
"id": 15,
"routeId": 1,
"poiId": 7,
"poiTitle": "Die alte Eiche",
"caption": "Blick auf die Baumkrone",
"sequence": 0,
"url": "/api/routes/1/pois/7/pictures/15",
"createdAt": "2026-06-16 12:30:00"
}
]
}
GET /api/routes/:routeId/pois/:poiId/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/pois/7/pictures/15 \
--output bild-15.jpg
Metadaten statt Dateidaten abrufen:
curl --get http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15 \
--data-urlencode 'metadata=true'
| Query-Parameter | Typ | Pflicht | Standard | Beschreibung |
|---|---|---|---|---|
metadata |
Boolean | nein | false |
bei true JSON-Metadaten statt der Bilddatei liefern |
Die API antwortet mit 404, wenn Route, POI oder Bild nicht zusammengehören.
POST /api/routes/:routeId/pois/:poiId/pictures
Lädt genau ein Bild für den im Pfad angegebenen POI hoch. Zulässig sind zwei Übertragungsformen.
Variante A: multipart/form-data
| Feld | Typ | Pflicht | Standard | Beschreibung |
|---|---|---|---|---|
picture |
Datei | ja | – | JPEG-, PNG- oder WebP-Datei; der Typ wird aus dem Inhalt erkannt |
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/pois/7/pictures \
-F 'caption=Blick auf die Baumkrone' \
-F 'sequence=0' \
-F 'picture=@eiche.jpg'
Variante B: JSON mit Base64
Empfohlener Content-Type: application/json. Der Server akzeptiert zusätzlich text/json.
{
"caption": "Blick auf die Baumkrone",
"sequence": 0,
"picture": {
"filename": "eiche.jpg",
"base64": "/9j/4AAQSkZJRgABAQ..."
}
}
Beispiel mit text/json und einer separat erzeugten Payload-Datei:
base64 < eiche.jpg | tr -d '\n' > eiche.jpg.b64
jq -n \
--arg caption 'Blick auf die Baumkrone' \
--argjson sequence 0 \
--rawfile data eiche.jpg.b64 \
'{
caption: $caption,
sequence: $sequence,
picture: {
filename: "eiche.jpg",
base64: $data
}
}' > picture.json
curl -X POST http://127.0.0.1:47145/api/routes/1/pois/7/pictures \
-H 'Content-Type: text/json' \
--data-binary @picture.json
Alternativ kann eine Data-URL übertragen werden:
{
"caption": "Blick auf die Baumkrone",
"picture": {
"filename": "eiche.png",
"dataUrl": "data:image/png;base64,iVBORw0KGgoAAA..."
}
}
Ein Request mit -d '{}' schlägt mit 400 Bad Request fehl, weil weder eine Multipart-Datei noch ein JSON-Dateiobjekt enthalten ist.
Erfolg: 201 Created und Location: /api/routes/1/pois/7/pictures/<pictureId>.
PUT /api/routes/:routeId/pois/:poiId/pictures/:pictureId
Aktualisiert Metadaten und kann optional die Datei ersetzen. Nicht übergebene Metadaten bleiben erhalten. Ein Bild kann über diesen Endpunkt nicht einem anderen POI zugeordnet werden; dafür muss es beim bisherigen POI gelöscht und beim Ziel-POI neu angelegt werden.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
picture |
Multipart-Datei oder JSON-Dateiobjekt | nein | ersetzt die bisherige Bilddatei |
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/pois/7/pictures/15 \
-H 'Content-Type: application/json' \
-d '{
"caption": "Nahaufnahme der Eichenblätter",
"sequence": 1
}'
Datei und Metadaten per Multipart ersetzen:
curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15 \
-F 'caption=Neue Aufnahme der Eiche' \
-F 'sequence=2' \
-F 'picture=@eiche-neu.webp'
Datei und Metadaten per JSON ersetzen:
{
"caption": "Neue Aufnahme der Eiche",
"sequence": 2,
"picture": {
"filename": "eiche-neu.webp",
"base64": "UklGRiQAAABXRUJQVlA4..."
}
}
curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15 \
-H 'Content-Type: application/json' \
--data-binary @picture-update.json
Wird eine Datei ersetzt, entfernt der Server die bisherige Datei nach erfolgreicher Datenbankaktualisierung.
DELETE /api/routes/:routeId/pois/:poiId/pictures/:pictureId
Entfernt Bilddatensatz und Datei.
curl -X DELETE http://127.0.0.1:47145/api/routes/1/pois/7/pictures/15
{
"id": 15,
"routeId": 1,
"poiId": 7,
"deleted": true
}
9. Audiodatei eines POIs verwalten
Pro POI ist höchstens eine Audiodatei vorgesehen. Daher ist /audio selbst die Einzelressource; eine zusätzliche Audio-ID ist nicht erforderlich.
Gemeinsame Pfadparameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
routeId |
positive Ganzzahl | ja | ID der aktiven Route |
poiId |
positive Ganzzahl | ja | ID eines POIs, der zu dieser Route gehört |
GET /api/routes/:routeId/pois/:poiId/audio
Liefert standardmäßig die Audiodatei des POIs direkt aus. Genau dieser Pfad wird in audioUrl und im Feld url der Audioressource ausgegeben. Der Endpunkt unterstützt HTTP-Range-Anfragen, damit Browser innerhalb der Audiodatei springen können.
Audiodatei speichern:
curl http://127.0.0.1:47145/api/routes/1/pois/7/audio \
--output ansage-7.mp3
Metadaten statt Dateidaten abrufen:
curl --get http://127.0.0.1:47145/api/routes/1/pois/7/audio \
--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 Route und POI nicht zusammengehören oder der POI keine Audiodatei besitzt.
POST /api/routes/:routeId/pois/:poiId/audio
Legt die Audiodatei des im Pfad angegebenen POIs an. Zulässig sind multipart/form-data und JSON mit Base64.
Variante A: multipart/form-data
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
audio |
Datei | ja | MP3-, MP4/M4A-, AAC-, Ogg-, WAV- oder WebM-Datei; der Typ wird aus dem Inhalt erkannt |
curl -X POST http://127.0.0.1:47145/api/routes/1/pois/7/audio \
-F 'audio=@ansage.mp3'
Variante B: JSON mit Base64
{
"audio": {
"filename": "ansage.mp3",
"base64": "SUQzBAAAAAAAI1RTU0UAAA..."
}
}
base64 < ansage.mp3 | tr -d '\n' > ansage.mp3.b64
jq -n --rawfile data ansage.mp3.b64 \
'{
audio: {
filename: "ansage.mp3",
base64: $data
}
}' > audio.json
curl -X POST http://127.0.0.1:47145/api/routes/1/pois/7/audio \
-H 'Content-Type: application/json' \
--data-binary @audio.json
Auch hier wird Content-Type: text/json akzeptiert. Ein leeres {} enthält keine Audiodatei und liefert 400 Bad Request.
Erfolg: 201 Created und Location: /api/routes/1/pois/7/audio.
Existiert bereits eine Audiodatei, antwortet der Server mit 409. Zum Ersetzen ist PUT zu verwenden.
PUT /api/routes/:routeId/pois/:poiId/audio
Ersetzt die vorhandene Audiodatei des POIs. Die Datei kann als Multipart-Upload oder als JSON-Dateiobjekt übertragen werden.
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
audio |
Multipart-Datei oder JSON-Dateiobjekt | ja | neue Audiodatei |
Multipart-Beispiel:
curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/audio \
-F 'audio=@ansage-neu.ogg'
JSON-Beispiel:
{
"audio": {
"filename": "ansage-neu.ogg",
"base64": "T2dnUwACAAAAAAAAAAB..."
}
}
curl -X PUT http://127.0.0.1:47145/api/routes/1/pois/7/audio \
-H 'Content-Type: text/json' \
--data-binary @audio-update.json
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/:routeId/pois/:poiId/audio
Entfernt die Audiodatei und setzt audioUrl des POIs auf null.
curl -X DELETE http://127.0.0.1:47145/api/routes/1/pois/7/audio
{
"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/pois/7/pictures/15
GET /api/routes/1/pois/7/audio
Die Zuordnung lautet:
| JSON-Feld | Datei-Endpunkt |
|---|---|
route.gpxUrl |
/api/routes/:id/gpx |
poi.audioUrl |
/api/routes/:routeId/pois/:poiId/audio |
poi.images[].url |
/api/routes/:routeId/pois/:poiId/pictures/:pictureId |
picture.url |
/api/routes/:routeId/pois/:poiId/pictures/:pictureId |
audio.url |
/api/routes/:routeId/pois/:poiId/audio |
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.
12. Python-Werkzeuge
Unter tools/python/ liegen interaktive Skripte für Anlegen, Ändern, Erweitern, Wiederherstellen und Löschen von Routen, POIs, Bildern und Audio. Sie verwenden ausschließlich die Python-Standardbibliothek. Fehlende Parameter werden abgefragt. Antwortet ein vorgeschalteter Webserver mit HTTP 401, fragt die gemeinsame Request-Schicht Benutzername und Passwort ab und wiederholt den ursprünglichen Request.
Details und Aufrufbeispiele stehen in Python-Werkzeuge.md.
Gruppenbearbeitung
Siehe Gruppenbearbeitung.md. Alle Gruppenantworten sind
no-store. POST/DELETE prüfen einen übermittelten Origin gegen den Host.
| Methode und Pfad | Zweck |
|---|---|
| GET /api/groups | Gruppen der Sitzung auflisten; ohne Sitzung leere Liste |
| POST /api/groups | Mit routeId und creator_id eine unabhängige Kopie öffnen |
| POST /api/groups/join | Mit code (Array aus acht Unicode-Symbolen) und creator_id beitreten |
| POST /api/groups/:id/code | Als Besitzer neuen Einladungscode erzeugen |
| DELETE /api/groups/:id | Als Besitzer Einladung und Mitbearbeiterzugänge widerrufen |
Der Server setzt wegwichtel_group_session als geschütztes Cookie auf /api.
Nur Hashes werden gespeichert. Die creator_id allein erteilt keine Gruppenrechte.
Gruppenantworten enthalten id, route, role, active und revision; nur das Erzeugen
oder Erneuern liefert den Klartextcode und expiresAt (Unixsekunden).
Die bisherigen /api/routes/:id/...-Endpunkte prüfen auch den Gruppenzugang,
einschließlich GPX, Bilder und Audio. Unveröffentlichte Gruppenkopien sind nicht
öffentlich gelistet. GET liefert X-Group-Route und X-Group-Revision; Änderungen
müssen die geladene Revision als If-Match senden. Überlappende oder veraltete
Änderungen ergeben 409. Erfolgreiche Änderungen erhöhen die Revision. API-Clients
müssen Cookies beibehalten; Gruppenmedien können damit auch als normale Bild- und
Audio-Ressourcen abgerufen werden. Freigegebene Strecken bleiben schreibgeschützt.
Stationssperren für gemeinsame Entwürfe
POST /api/groups/routes/:routeId/pois/:poiId/lock verwendet die berechtigte
Gruppensitzung. {} beansprucht eine freie Station. Antwort: token, version,
mediaVersion, poi. Eine vorhandene Bearbeitung ergibt 423 mit
details.code=point-locked und details.replace. Nach einer bestätigten Rückfrage
mit { "replace": "<angezeigter Schlüssel>" } erneut senden. Eine inzwischen
veränderte Sperre braucht eine neue Bestätigung. { "token": "<eigener Schlüssel>" }
aktualisiert den eigenen Inhaltsstand; { "release": true, "token": "…" } gibt
nur die eigene Sperre frei. Fremde creator_id-Angaben werden nicht übernommen.
Laufende Schreibanfragen ergeben point-busy (423), ein verlorener Zugang zur
Bearbeitung point-lost (423), ein veränderter Stationsinhalt point-changed (409).
Für Schreibzugriffe auf /api/routes/:id/pois/:poiId und dessen Bild-/Tonpfade
sind X-Poi-Lock: <token> und If-Poi-Match: <version> erforderlich. Anstelle
von If-Match wird dort der Stationsinhalt geprüft. Erfolgreiche Antworten liefern
X-Poi-Id und X-Poi-Version, solange die Station existiert. Diese Version bei
mehreren Dateioperationen jeweils weiterverwenden. Eine neue Station wird über
den bisherigen POST angelegt und vor den weiteren Dateioperationen gesperrt.
Die Versionsabdrücke berücksichtigen Dateibytes, nicht bloß URLs oder Metadaten.