diff --git a/.gitignore b/.gitignore index a8a6f1b..a65dc50 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,8 @@ coverage/ __pycache__/ *.pyc -docs/ \ No newline at end of file +docs/* +!docs/*.md +!docs/agents/ +docs/agents/* +!docs/agents/*.md diff --git a/UMZUEGE.txt b/UMZUEGE.txt new file mode 100644 index 0000000..a201628 --- /dev/null +++ b/UMZUEGE.txt @@ -0,0 +1,25 @@ +Dokumentation umordnen + +1. Archiv im Projektverzeichnis entpacken. Es enthält vollständige Dateien. +2. Alte Dateien entfernen, damit keine doppelten Dokumente zurückbleiben: + +git rm -- AGENTS.md I18N.md GPX-Aufzeichnung.md TODO-MODERATION.md SONAR-REVIEW-17.md docs/editor-merge-notes.md README.md SECURITY.md CREATOR-ID.md MODERATION-MEDIEN.md tools/python/README.md public/vendor/README.md + +Zuordnung: +AGENTS.md -> docs/agents/AGENTS.md +I18N.md -> docs/agents/I18N.md +GPX-Aufzeichnung.md -> docs/agents/GPX-Aufzeichnung.md +TODO-MODERATION.md -> docs/agents/TODO-MODERATION.md +SONAR-REVIEW-17.md -> docs/agents/SONAR-REVIEW-17.md +docs/editor-merge-notes.md -> docs/agents/editor-merge-notes.md +README.md -> docs/README.md +SECURITY.md -> docs/SECURITY.md +CREATOR-ID.md -> docs/CREATOR-ID.md +MODERATION-MEDIEN.md -> docs/MODERATION-MEDIEN.md +tools/python/README.md -> docs/Python-Werkzeuge.md +public/vendor/README.md -> docs/Client-Abhaengigkeiten.md + +Agent-Aufträge und Übergaben sollen docs/agents/AGENTS.md ausdrücklich nennen. + +Basis: Repository-Commit 497b7b8. Keine Änderungen online eingecheckt. +Prüfung: npm run test:coverage; 147 JavaScript- und 23 Python-Tests bestanden. diff --git a/docs/CREATOR-ID.md b/docs/CREATOR-ID.md new file mode 100644 index 0000000..7bf0d76 --- /dev/null +++ b/docs/CREATOR-ID.md @@ -0,0 +1,80 @@ +# Erstellerkennung und erneutes Einsenden + +Beim App-Start entsteht ein kryptografischer Zufallswert mit 256 Bit über +`crypto.getRandomValues()`. Er liegt als 64-stellige Hexadezimalzeichenfolge +unter `wegwichtel.creator-id.v1` im Local Storage. Keine Zeitzone, Geräteangaben +oder Informationen über den Internetanbieter werden gesammelt. Ein zusätzlicher +Hash würde die Eindeutigkeit des Zufallswerts nicht erhöhen. + +Neue Entwürfe bekommen `route.creator_id`; alte lokale Entwürfe bekommen sie +beim nächsten Speichern. Beim Einsenden landet sie in `routes.creator_id`. +Die API liefert das Feld, die Moderations-CLI zeigt die Ersteller-ID. Bereits +zugeordnete Server-Strecken behalten ihren Ersteller bei Änderungen. Strecken +älterer Versionen können beim ersten erneuten Einsenden zugeordnet werden. + +## Speicherung und Löschen + +Das Ausschalten des Offline-Speichers, Löschen von Fortschritt, Entwürfen oder +Medien löscht die Gerätekennung nicht. Es gibt keine automatische Bereinigung +dieses getrennten Speicherschlüssels. Die gezielte Entwicklerfunktion +`Wegwichtel.LocalRoutes.forgetCreatorId()` entfernt die Kennung ausschließlich +nach ausdrücklich bestätigter Internetbrowser-Abfrage. Es wird kein zusätzlicher +Löschbutton in die Kinderoberfläche eingebaut. Nach bestätigtem Löschen entsteht +beim nächsten Zugriff eine neue Kennung; vorhandene Strecken behalten ihre alte +Erstellerzuordnung. Die App kann das Löschen sämtlicher Website-Daten in den +Einstellungen des Internetbrowsers nicht verhindern. Kopierte Browserdaten +kopieren auch die Identität; das ist keine Hardware-ID. + +## Aktualisieren einer eingesandten Strecke + +Der lokale Entwurf bleibt erhalten und merkt sich `serverRouteId`, +`serverPoiId`, `serverPictureId` und den Audio-Status. Erneutes Einsenden nutzt +PUT für bestehende Daten und POST für neue Stationen/Bilder. Entfernte lokale +Stationen und Medien werden bei einer erfolgreichen erneuten Übertragung auch +auf dem Server entfernt. Bestätigte Server-IDs werden nach jedem Schritt +lokal gespeichert, damit ein späterer Fehler nicht zum Neuanlegen der Strecke +führt. Der Entwurf bleibt nach Fehlern erneut einsendbar. + +Freigegebene Strecken bleiben unveränderlich. Die Prüfung erfolgt vor der +Aktualisierung und zusätzlich in den vorhandenen Server-Schreibprüfungen. +Bei älteren Entwürfen fehlen noch die gespeicherten Stations-/Bild-IDs: Beim +ersten erneuten Einsenden werden neue Stationen samt Medien angelegt und die +alten Stationen anschließend entfernt. Die Strecken-ID bleibt gleich. Deshalb +können sich bei dieser einmaligen Übernahme die Stations-IDs ändern. + +Die Übertragung mehrerer Dateien ist keine gemeinsame Transaktion. Bei einem +Abbruch können einzelne Änderungen bereits auf dem Server sein. Bei einem +verlorenen HTTP-Erfolg nach serverseitigem POST ist zusätzlich eine spätere +Idempotenzlösung erforderlich. Parallele Bearbeitung ist noch nicht freigegeben; +die aktuelle Übertragung behandelt den lokalen Entwurf als vollständigen Stand. + +## Datenbank und Betrieb + +Das Schema und die Startprüfung enthalten die nullable Spalte +`routes.creator_id`. Neue Datenbanken enthalten sie direkt. Es gibt keine +automatische Migration einer vorhandenen Datenbank; `npm run init-db` allein +fügt einer schon bestehenden Tabelle keine Spalte hinzu. Bestehende Strecken +dürfen für diese Änderung nicht gelöscht werden. Für die vorhandene Datenbank +ist vor dem Neustart folgende gezielte Schemaergänzung nötig: + +```sql +ALTER TABLE routes ADD COLUMN creator_id TEXT; +``` + +Die Anwendung vorher anhalten und die Datenbank sichern. Den tatsächlichen +Datenbankpfad aus `DATA_DIR` in `.env` verwenden. Die Anweisung nur einmal +anwenden und anschließend `npm run test:runtime-db` ausführen, dann die App +starten. Nicht die Datenbank oder Streckenverzeichnisse löschen. + +## Berechtigungen + +`creator_id` ist Metadatum, kein Passwort und kein Eigentumsnachweis. Eine frei +übermittelte Kennung gewährt keine Berechtigung. Bestehende Zugriffsregeln bleiben +wirksam. Abgesicherte Eigentumszuordnung, Einladungen und gemeinsame Bearbeitung +stehen in [TODO-MODERATION.md](agents/TODO-MODERATION.md) weiterhin aus. + +Die GPS-Aufzeichnung läuft nur innerhalb einer Bearbeitungssitzung. Schließen, +Abbrechen, Rückkehr zur Liste, Streckenwechsel und Verlassen der Seite beenden +sie; ein Neustart übernimmt keinen aktiven Aufzeichnungszustand. Beim erneuten +Öffnen eines lokalen Entwurfs beginnt eine neue Sitzung. Einzelheiten stehen in +[GPX-Aufzeichnung.md](agents/GPX-Aufzeichnung.md). diff --git a/docs/Client-Abhaengigkeiten.md b/docs/Client-Abhaengigkeiten.md new file mode 100644 index 0000000..cf65a62 --- /dev/null +++ b/docs/Client-Abhaengigkeiten.md @@ -0,0 +1,14 @@ +# Lokale Client-Abhängigkeiten + +Die Browseranwendung verwendet ausschließlich die unter `public/vendor/` abgelegten Dateien: + +- `jquery/jquery-4.0.0.min.js` +- `jquery/LICENSE.txt` +- `jquery-ui/jquery-ui-1.14.2.min.js` +- `jquery-ui/jquery-ui-1.14.2.min.css` +- `jquery-ui/images/*.png` +- `jquery-ui/LICENSE.txt` + +Die Dateien stammen aus den offiziellen npm-Paketen `jquery@4.0.0` und `jquery-ui@1.14.2`. `npm install` führt `scripts/vendor-sync.js` aus und kopiert die Distributionsdateien, das Base-Theme, dessen Bildressourcen und die Lizenztexte erneut aus `node_modules` nach `public/vendor/`. + +Die Dateien sind bereits im Projektarchiv enthalten. Der Browser benötigt deshalb weder ein CDN noch eine Netzwerkverbindung zum Laden der UI-Bibliotheken. diff --git a/docs/MODERATION-MEDIEN.md b/docs/MODERATION-MEDIEN.md new file mode 100644 index 0000000..fc414fc --- /dev/null +++ b/docs/MODERATION-MEDIEN.md @@ -0,0 +1,68 @@ +# Stationen und Medien prüfen + +`npm run moderate` zeigt unter jeder Strecke eine Tabelle mit Stations-ID, +Platz (ab 1, sortiert nach `sequence, id`), Beschreibung und Erstelldatum. +Bei leerer Beschreibung erscheint der Titel. Zeilenumbrüche werden nur für +die Tabellenanzeige zusammengefasst. Die Datenbank wird lesend geöffnet. + +## Dauerhafte Prüflinks + +In `.env` ergänzen: + +```dotenv +MODERATION_REVIEW_URL=https://wegwichtel.de/moderation/api +``` + +Die Tabelle zeigt für jedes Bild und jede Tonaufnahme einen eigenen Link. +Gelöschte Strecken erhalten keine Links. Ohne diese Einstellung bleibt die +Linkspalte leer. Zugangsdaten gehören nicht in URLs oder `.env`. +Es gibt keine Einmal-Tokens, keine zusätzliche Datenbanktabelle und kein PHP. +Eine Datenbank-Neuinitialisierung ist für diese Änderung nicht nötig. + +## Anmeldung mit Nginx und htpasswd + +`nginx/moderation-review.location.conf` in den bestehenden HTTPS-server-Block +aufnehmen. Der geschützte Pfad `/moderation/api/` nutzt die vorhandene Node-API. +GET und HEAD sind erlaubt; schreibende Methoden sind gesperrt. +Nginx prüft Benutzername und Passwort und leitet die Zugangsdaten nicht an Node weiter. +Die Vorlage verweigert HTTP; die Anmeldung muss über HTTPS erfolgen. + +Auf Debian/Ubuntu beispielsweise: + +```bash +sudo apt install apache2-utils +sudo htpasswd -c /etc/nginx/wegwichtel-moderation.htpasswd moderator +sudo chown root:www-data /etc/nginx/wegwichtel-moderation.htpasswd +sudo chmod 640 /etc/nginx/wegwichtel-moderation.htpasswd +sudo nginx -t +sudo systemctl reload nginx +``` + +`www-data` gegebenenfalls an den Nginx-Benutzer anpassen. `-c` nur beim ersten +Anlegen benutzen, damit bestehende Benutzer nicht überschrieben werden. +Beim Öffnen eines Prüflinks erscheint die Anmeldeabfrage des Internetbrowsers. +Bilder und Audio nutzen die bestehenden Datei-Endpunkte einschließlich Range-Requests. +Die Vorlage ersetzt nicht die vorhandene Server-Konfiguration. + +Wichtig für den Umfang: Die Authentifizierung schützt den Moderationspfad. +Die bisherigen `/api/`-Endpunkte bleiben entsprechend dem bisherigen Verhalten +zugänglich, auch für noch nicht freigegebene aktive Strecken. Das entspricht +bereits online verfügbaren Daten; es ist keine Zugriffssperre für unveröffentlichte +Inhalte. Eine solche Sperre müsste separat für die öffentliche API umgesetzt werden. + +## Falls das vorherige Archiv bereits eingebaut wurde + +Die Dateien `tools/php/moderation-review.php` und +`test/php/test_moderation_review.py` sowie den alten PHP-Location-Block entfernen. +Den npm-Eintrag `test:php` entfernen und `MODERATION_TOKEN_TTL_SECONDS` aus `.env` +löschen. Eine bereits angelegte Tabelle `moderation_review_tokens` wird nicht +mehr benutzt; sie kann bestehen bleiben. Die neuen Dateien ersetzen keine +Datenbank und löschen keine Strecken. + +## Tests + +Python-Tests prüfen Tabellenreihenfolge, stabile Bild-/Audiolinks, gelöschte +Strecken, URL-Konfiguration und dass die Anzeige die SQLite-Datei nicht verändert. +Nginx muss auf dem Zielserver mit `nginx -t` und anschließend über HTTP geprüft +werden: ohne oder mit falschem Passwort 401, mit gültigem Passwort Dateiabruf; +schreibende Methoden dürfen keine Änderung auslösen. diff --git a/docs/Python-Werkzeuge.md b/docs/Python-Werkzeuge.md new file mode 100644 index 0000000..5fbe790 --- /dev/null +++ b/docs/Python-Werkzeuge.md @@ -0,0 +1,124 @@ +# Python-Werkzeuge für die Wegwichtel-REST-API + +Die Skripte und die automatisierte Python-Coverage-Erzeugung benötigen nur Python 3 und die Standardbibliothek. Node.js, `curl`, `pip` und zusätzliche Python-Pakete sind auf dem aufrufenden Rechner nicht erforderlich. + +Alle Aufrufe erfolgen aus dem Projektverzeichnis. Die Skripte liegen weiterhin +unter `tools/python/`; nur diese Anleitung wurde nach `docs/` verschoben. + +## Authentifizierung + +Jeder Request wird zunächst ohne vorauseilenden Authorization-Header ausgeführt. Antwortet ein vorgeschalteter Webserver mit `401 Unauthorized`, verwendet die gemeinsame Request-Schicht zuerst die Umgebungsvariablen `API_USER` und `API_PASSWORD`. Sind beide gesetzt, wird derselbe Request genau mit diesen Basic-Auth-Credentials wiederholt. Fehlen sie, fragt das Werkzeug Benutzername und Passwort interaktiv ab. Das Passwort wird mit `getpass` ohne Bildschirmausgabe eingelesen. Führende und nachgestellte Whitespaces werden aus beiden Umgebungsvariablen entfernt; reine Whitespace-Werte gelten als nicht gesetzt. + +Für unbeaufsichtigte Aufrufe und CI-Pipelines: + +```bash +export API_USER='florian' +export API_PASSWORD='geheimes-passwort' +``` + +Die API-Basisadresse ist standardmäßig `https://wegwichtel.de`. Alle Skripte unterstützen außerdem `--base-url` für abweichende Zielsysteme und `--username` für eine interaktive Anmeldung. Fehlende fachliche Parameter werden interaktiv abgefragt. + +## Routen + +### Lokale Moderation auf dem Server + +`moderate_routes.py` greift direkt auf die bestehende SQLite-Datenbank zu und +benötigt keine API-Verbindung. Ohne Parameter zeigt es alle Strecken mit ID, +Name, Status, Freigabezeitpunkt und Ersteller-ID (`creator_id`) an. Darunter stehen die Stationen tabellarisch +mit ID, Platz, Beschreibung, Erstelldatum und dauerhaften Links zu Bildern und Tonaufnahmen. +Die Anzeige öffnet die Datenbank ausschließlich lesend. Einrichtung und +passwortgeschützter Webzugang sind in +[MODERATION-MEDIEN.md](MODERATION-MEDIEN.md) beschrieben. Gerätekennung und +erneutes Einsenden stehen in [CREATOR-ID.md](CREATOR-ID.md); noch offene +Berechtigungen in [TODO-MODERATION.md](agents/TODO-MODERATION.md): + +```bash +python3 tools/python/moderate_routes.py +python3 tools/python/moderate_routes.py --enable 12 +python3 tools/python/moderate_routes.py --disable 12 +``` + +Alternativ aus dem Projektverzeichnis über npm: + +```bash +npm run moderate +npm run moderate -- -e 12 +npm run moderate -- -d 12 +``` + +Das `--` trennt die npm-Optionen von den Parametern des Moderationsskripts. + +Kurzformen: `-e 12` und `-d 12`. `--enanble` wird als Alias für `--enable` +unterstützt. Freigeben setzt `activated_at` auf den aktuellen UTC-Zeitstempel; +Zurückziehen setzt es auf `NULL`. Gelöschte Strecken werden nicht freigegeben. + +Das Skript liest `.env` im Projektverzeichnis, unabhängig vom Arbeitsverzeichnis. +`DATA_DIR` bestimmt das Datenverzeichnis, standardmäßig `data`; darin liegt +`wegwichtel.sqlite`. Bereits gesetzte Umgebungsvariablen haben Vorrang wie beim +App-Start. Die `.env` unterstützt einzeilige Werte, Anführungszeichen, Kommentare +und `export`; sie wird nicht als Shell-Skript ausgeführt. Eine fehlende Datenbank +wird nicht neu angelegt. Der aufrufende Benutzer benötigt die entsprechenden +Dateirechte für Datenbank und Datenverzeichnis. + +### Bearbeitung über die API + +```bash +python3 tools/python/create_route.py +python3 tools/python/update_route.py +python3 tools/python/append_route.py +python3 tools/python/delete_route.py +python3 tools/python/restore_route.py +``` + +## POIs + +```bash +python3 tools/python/create_poi.py +python3 tools/python/update_poi.py +python3 tools/python/delete_poi.py +``` + +Beim Löschen eines POIs entfernt die API auch dessen Bilder und Audiodatei. + +## Bilder + +```bash +python3 tools/python/create_picture.py +python3 tools/python/update_picture.py +python3 tools/python/delete_picture.py +``` + +## Audio + +```bash +python3 tools/python/create_audio.py +python3 tools/python/update_audio.py +python3 tools/python/delete_audio.py +``` + +## Beispiel mit Parametern + +```bash +python3 tools/python/create_picture.py \ + --base-url https://wegwichtel.de \ + --route-id 1 \ + --poi-id 7 \ + --file ./eiche.jpg \ + --caption 'Blick auf die Baumkrone' \ + --sequence 0 +``` + +Die Multipart-Hilfsfunktion sendet für Dateien absichtlich keinen behaupteten MIME-Typ. Der Server bestimmt das Format aus dem Dateiinhalt. + +## Fehlerausgabe und Tests + +Alle Werkzeuge kapseln ihre Aktion in der gemeinsamen `run()`-Funktion. Bei API-, Datei-, Eingabe- oder unerwarteten Laufzeitfehlern werden der Fehlertyp, die Fehlermeldung und der vollständige Python-Stacktrace auf `stderr` ausgegeben. Der Prozess endet anschließend mit Exitcode `1`; ein manueller Abbruch per Tastatur verwendet Exitcode `130`. + +Die Python-Tests und der Cobertura-kompatible Bericht werden über die NPM-Skripte ausgeführt: + +```bash +npm run test:python +npm run test:coverage:python +``` + +Der Coverage-Runner verwendet `unittest` und das Standardbibliotheksmodul `trace`. Der Cobertura-kompatible Bericht liegt anschließend unter `coverage/python/coverage.xml` und wird über `sonar.python.coverage.reportPaths` von SonarQube eingelesen. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..37f8c1d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,226 @@ +# Wegwichtel Next + +Neuaufbau des früheren schiffsbezogenen Ansagesystems als mobile Lernweg-Anwendung für Schulen. GPX-Strecken werden serverseitig verwaltet; POIs können einzeln verwaltete Bilder mit Beschreibungen und eine separat verwaltete Audioansage enthalten. Der Client schlägt anhand der aktuellen Position nahe Routen vor und aktiviert POIs während einer Wanderung über einen GPS-Watcher. Beim Erreichen eines POIs wird die Audioansage nur vorgeladen und erst nach einer bewussten Bedienung abgespielt. + +Die Anleitungen für Anwender und Betrieb liegen direkt unter `docs/`. +Vorgaben und Entwicklungsaufgaben für Agents liegen unter `docs/agents/`. +Vor Änderungen die [Agent-Anweisungen](agents/AGENTS.md) lesen. + +Alle folgenden Befehle werden im Projektverzeichnis ausgeführt. + +## Architektur + +Die Trennung von Stationen und Weg, die Aufzeichnung alle 30 Sekunden und das +nachträgliche Ergänzen fehlender Koordinaten sind in +[`GPX-Aufzeichnung.md`](agents/GPX-Aufzeichnung.md) beschrieben. + +- **Server:** Node.js 22.13+, Express 5, `node:sqlite`, Multer, `fast-xml-parser`, `file-type` und `@file-type/av` +- **Client:** klassisches JavaScript, jQuery 4.0.0 und jQuery UI 1.14.2 mit lokalem Base-Theme +- **Medien:** getrennte Verzeichnisse für aktive und zum Löschen markierte Strecken +- **Löschmodell:** Soft Delete in SQLite plus atomisches Verschieben des gesamten Streckenordners in `storage/trash/routes` +- **Wiederherstellung:** `POST /api/routes/:id/restore` verschiebt die Daten zurück und korrigiert alle gespeicherten Pfade + +## Schnellstart + +```bash +cp .env.example .env +npm install +npm run init-db +npm start +``` + +Danach lauscht Wegwichtel ausschließlich auf dem lokalen Socket `127.0.0.1:47145` und ist unter `http://127.0.0.1:47145` erreichbar. Die offiziellen, exakt versionierten Distributionsdateien sind bereits unter `public/vendor` enthalten. `npm install` installiert zusätzlich die Pakete `jquery@4.0.0` und `jquery-ui@1.14.2` und synchronisiert daraus JavaScript, Base-Theme, Themebilder und Lizenzdateien erneut in das Vendor-Verzeichnis. Der Browser lädt keine Bibliotheken von einem CDN. + + +## Server-Socket + +Die Standardwerte stehen in `.env.example` und werden auch verwendet, wenn keine `.env` vorhanden ist: + +```dotenv +HOST=127.0.0.1 +PORT=47145 +``` + +`HOST` ist die tatsächliche Bind-Adresse des Node.js-Servers. Der Health-Endpunkt meldet beispielsweise: + +```json +{ + "ok": true, + "service": "wegwichtel", + "socket": "127.0.0.1:47145" +} +``` + +## Initialisierungsablauf des Clients + +1. statischer Initialisierungsbildschirm erscheint ohne Bibliotheksabhängigkeit, +2. lokale jQuery-Datei wird geladen und auf Version 4.0.0 geprüft, +3. lokales jQuery UI 1.14.2 samt Base-Theme wird geladen und über `jQuery.ui.version` geprüft, +4. die Clientmodule werden sequenziell geladen, +5. `/api/health` prüft Server und SQLite, +6. der Client lädt die Routenliste, +7. anschließend wird die Position ermittelt; daraus entstehen nahe Empfehlungen, während die Gesamtliste vollständig erhalten bleibt, +8. die responsive Routenansicht wird freigeschaltet. + +Scheitert ein Schritt, bleibt der Initialisierungsbildschirm mit einer konkreten Fehlermeldung und einem Wiederholungsbutton sichtbar. + +## Lokale UI-Abhängigkeiten + +Im Vendor-Verzeichnis liegen die zur Laufzeit verwendeten Dateien vollständig lokal: + +```text +public/vendor/jquery/jquery-4.0.0.min.js +public/vendor/jquery/LICENSE.txt +public/vendor/jquery-ui/jquery-ui-1.14.2.min.js +public/vendor/jquery-ui/jquery-ui-1.14.2.min.css +public/vendor/jquery-ui/images/*.png +``` + +Die Anwendung verwendet aus jQuery UI insbesondere das Widget **Button**. Das offizielle vollständige jQuery-UI-Bundle bleibt lokal verfügbar, damit weitere aktuelle Widgets ohne erneuten CDN-Bezug ergänzt werden können. Die frühere jQuery-Mobile-Seitensteuerung wurde durch eine eigene, History-API-basierte Navigation ersetzt. + +## API-Dokumentation + +Die vollständige REST-API ist einschließlich aller Pfad-, Query-, Formular- und Datei-Parameter sowie ausführlicher `curl`-Beispiele in [`REST-API.md`](REST-API.md) dokumentiert. Die README beschreibt bewusst nur Installation, Architektur und Bedienverhalten. + + +## Python-Werkzeuge + +Unter [`tools/python/`](Python-Werkzeuge.md) liegen eigenständige Python-3-Skripte zum Anlegen, Ändern, Erweitern, Wiederherstellen und Löschen von Routen, POIs, Bildern und Audiodateien. Die Werkzeuge selbst verwenden ausschließlich die Python-Standardbibliothek. Fehlende Parameter werden interaktiv abgefragt. Antwortet ein vorgeschalteter Passwortschutz mit HTTP 401, fragt die gemeinsame Request-Schicht Benutzername und Passwort ab und wiederholt den Request. Fehler werden mit einer verständlichen Zusammenfassung und dem vollständigen Python-Stacktrace auf `stderr` ausgegeben. + +## Dateistruktur + +```text +public/ mobiler Client + index.html reduzierte semantische Seitenstruktur + js/bootstrap-loader.js lädt und prüft lokale Bibliotheken + js/orientation.js Kompassausrichtung für den Navigationspfeil + js/vibration.js gekapselte Vibration mit benannten Mustern + vendor/ lokale jQuery-/jQuery-UI-Dateien samt Themebildern +src/routes/ REST-Routing +src/services/ GPX-, Geodaten-, Speicher- und Fachlogik +database/schema.sql SQLite-Schema +data/ lokale SQLite-Datei +storage/active/routes/ aktive GPX-, Bild- und Audiodateien +storage/trash/routes/ zum Löschen markierte Strecken +docs/ Dokumentation für Anwender und Betrieb + agents/ Anweisungen und Entwicklungsaufgaben für Agents +test/ automatisierte Tests +test-support/ isolierte Testhilfen und synthetische GPX-Testdaten +tools/python/ interaktive API-Werkzeuge ohne Node.js-Abhängigkeit +``` + +## Automatisierte Tests + +GPX-Testdaten liegen in `test-support/gpx-fixtures.js`. Die API-Tests schreiben sie +bei Bedarf in ein temporäres Verzeichnis. Mitgelieferte Beispielstrecken oder +Dateien unter `examples/` werden nicht vorausgesetzt. + +Das optionale Präsentations-Uploadskript verwendet ausdrücklich angegebene Dateien: + +```bash +bash scripts/upload-presentation-route.sh https://wegwichtel.de/api /pfad/strecke.gpx /pfad/stationen.json +``` + +Die JSON-Datei enthält `name`, `description` und `pois` mit `title`, `description`, +`lat`, `lon`, `triggerRadiusM` und `sequence`. Die Skript-Tests erzeugen ihre Daten +selbst und simulieren die HTTP-Aufrufe, einschließlich des Aufräumens bei Fehlern. + +Die REST-Integrationstests starten die Anwendung über `npm start` mit isolierten temporären Daten- und Speicherverzeichnissen. Der eigentliche Node.js-Prozess lauscht innerhalb des Testaufbaus auf `127.0.0.1:47146`. Davor läuft ein ausschließlich für die Tests bestimmter Reverse-Proxy auf `http://127.0.0.1:47145`, sodass dieser Loopback-Endpunkt das einzige Ziel der API-Aufrufe bleibt. Der Produktivserver wird von der Testsuite nicht angesprochen. + +Der lokale Proxy lässt `GET`, `HEAD` und `OPTIONS` ohne Anmeldung passieren. Für `POST`, `PUT`, `PATCH` und `DELETE` antwortet er zunächst mit `401 Unauthorized` und `WWW-Authenticate: Basic`, sofern kein gültiger Authorization-Header vorhanden ist. Nach erfolgreicher Basic-Auth-Prüfung entfernt der Proxy den Header wieder und leitet den Request an den lokal per NPM gestarteten Server weiter. Damit wird ein möglicher Nginx-Passwortschutz für schreibende Operationen realistisch simuliert, ohne Authentifizierung in die Anwendung selbst einzubauen. + +Der Integrationstest prüft dabei ausdrücklich, dass lesende Requests nicht präventiv authentifiziert werden und dass für `POST`, `PUT` und `DELETE` jeweils zuerst ein `401` und anschließend ein erfolgreicher authentifizierter Request erfolgt. Zusätzlich bleibt ein kleiner, isolierter HTTP-Mock auf einem zufälligen Loopback-Port bestehen, der die 401-Wiederholungslogik der Python-Request-Schicht unabhängig vom vollständigen REST-Test prüft. + +Die kombinierte Coverage wird mit folgenden Befehlen erzeugt: + +```bash +npm ci +npm run test:coverage +``` + +Der Python-Coverage-Runner verwendet ausschließlich die Python-Standardbibliothek. Er führt die Unit-Tests mit `unittest` unter dem eingebauten `trace`-Modul aus und erzeugt daraus ohne `pip` oder zusätzliche Pakete einen Cobertura-kompatiblen Bericht. Ausführungsfehler werden mit vollständigem Python-Stacktrace auf `stderr` ausgegeben. + +Dabei entstehen `coverage/js/lcov.info` für JavaScript und `coverage/python/coverage.xml` für die Python-Werkzeuge. Die zugehörigen SonarQube-Pfade stehen in `sonar-project.properties`. Die Coverage-Skripte erzwingen selbst keinen Mindestwert; die Bewertung und die Hinweise zu nicht abgedeckten Stellen bleiben Aufgabe des SonarQube Quality Gates. + +## Technische Hinweise + +- Medienpfade werden relativ zu `storage/` gespeichert. So bleibt das Projekt verschiebbar. +- Öffentliche GPX-, Bild- und Audiodateien werden ausschließlich über die zugehörigen `/api/routes/...`-Ressourcen ausgeliefert. Interne Speicherpfade und ein separates `/media`-URL-Schema werden nicht veröffentlicht. +- Dateiverschiebung und Datenbankänderung sind durch eine kompensierende Rückverschiebung gekoppelt: Schlägt die SQL-Transaktion fehl, wird das Verzeichnis an seinen vorherigen Ort zurückbewegt. +- GPX-Erweiterungen ergänzen die Trackpunkte in SQLite. Das Original-GPX bleibt im Skelett unverändert; ein späterer Exportdienst sollte aus den Datenbankpunkten eine konsolidierte GPX-Datei generieren. +- Die Node.js-Anwendung authentifiziert Schreibzugriffe nicht selbst. Ein vorgeschalteter Nginx kann Basic Auth erzwingen; Rollen, CSRF-Schutz, Rate-Limits und ein Moderationsworkflow bleiben sinnvolle Ausbaustufen. +- Uploadtypen werden serverseitig aus dem Inhalt erkannt. Binärformate verwenden `file-type` und `@file-type/av`; GPX wird als UTF-8-XML mit ``-Wurzelelement geprüft. Dateiname und vom Client behaupteter MIME-Typ sind dafür unerheblich. +- Der Server bindet standardmäßig nur an `127.0.0.1`; Zugriffe von anderen Geräten sind damit bewusst ausgeschlossen. +- Anwendungsaktionen verwenden Pointer Events für Touch, Stift und Maus. Enter und Leertaste bleiben als Tastaturbedienung erhalten. +- Bei gestarteter Route zeigt ein fixierter Footer vorrangig Entfernung und Richtung zum nächstgelegenen, noch nicht erreichten POI. Der GPX-Fortschritt wird parallel entlang der Trackpunkte nachgeführt; sind alle POIs erreicht, übernimmt wieder der nächste GPX-Trackpunkt beziehungsweise das Routenziel. Ohne Kompassdaten wird die Peilung mit Norden oben dargestellt. +- Auf iPhone und iPad wird die Freigabe der Geräteausrichtung beim Start der Route innerhalb der Benutzeraktion angefordert. +- Für die Geolokalisierung sollte der Client lokal über `http://127.0.0.1:47145` oder online ausschließlich über HTTPS geöffnet werden. Das betrifft ebenso den Gerätekompass. + +## Nächste Ausbaustufen + +- Administrationsoberfläche zum Zeichnen/Importieren von Routen und Platzieren der POIs +- Benutzer-, Schul- und Projektzuordnung mit Rollenmodell +- Offline-Cache/PWA für Wanderungen ohne Mobilfunkempfang +- Kartenansicht, GPX-Visualisierung und Abweichungswarnung +- Administrationsoberfläche für die bereits vorhandenen Einzelendpunkte zur Medienverwaltung +- Hintergrundbereinigung des Papierkorbs nach einer konfigurierbaren Aufbewahrungsfrist +- Integritätsjournal für Dateiverschiebungen und Wiederherstellungen + +## Routenauswahl + +Die Startseite zeigt standortbasierte Empfehlungen und darunter alle aktiven Routen. Die Gesamtliste bleibt auch bei verweigertem oder nicht verfügbarem GPS auswählbar und kann nach Name, Schule oder Beschreibung durchsucht werden. + +## Mobile Bedienung und Routennavigation + +Das HTML verwendet nur IDs, die von den Clientmodulen tatsächlich angesprochen werden. Die wenigen Klassen bilden wiederverwendete Layoutbausteine wie Seiten, Inhalte, Hinweise, Routenlisten und Routendaten ab. Automatisierte Tests gleichen diese Verwendungen ab. + +Alle selbst implementierten Schaltaktionen reagieren primär auf `pointerup`; dadurch funktionieren dieselben Handler mit Touchscreen, Eingabestift und Maus. Die Diashow kann zusätzlich horizontal gewischt werden. Für die Tastatur werden Enter und Leertaste separat behandelt. + +Beim Start der Route wird der aktuellen Position nächstgelegene GPX-Trackpunkt gesucht und der Fortschritt anschließend nur vorwärts entlang der Punktfolge nachgeführt. Für die sichtbare Navigation haben jedoch POIs Vorrang: Aus allen noch nicht automatisch aktivierten Stationen wird bei jeder Positionsänderung die räumlich nächstgelegene bestimmt. Der Footer zeigt: + +- Name und auf ganze Meter gerundete Luftlinienentfernung zum nächsten POI, +- einen frei rotierenden SVG-Pfeil relativ zur Geräte- oder Bewegungsrichtung, +- andernfalls die absolute Peilung bei Norden oben, +- nach dem Aktivieren unmittelbar den nächsten noch offenen POI, +- und erst nach Erreichen aller POIs wieder den nächsten GPX-Trackpunkt beziehungsweise das Routenziel. + +In der Stationsliste erhält jeder POI rechts einen kleinen Richtungspfeil und seine aktuelle Entfernung in Metern. Diese Werte werden mit jeder GPS- oder Kompassaktualisierung neu berechnet. Die Reihenfolge der Stationsliste bleibt dabei unverändert. + + +## Routensteuerung und GPS-Tracking + +Die Routenansicht besitzt drei nebeneinanderliegende, semantische Schaltflächen mit lokalen SVG-Symbolen: + +- **Route starten beziehungsweise fortsetzen:** aktiviert `navigator.geolocation.watchPosition()` und die Geräteausrichtung. +- **Route pausieren:** beendet den aktiven GPS-Watcher und den Kompass-Listener, pausiert eine laufende Audioansage und behält Routenfortschritt, Audioquelle, Abspielposition und bereits ausgelöste POIs bei. +- **Route beenden:** beendet alle Sensor-Listener, hält die Audioansage an, entlädt ihre Quelldatei, setzt den Fortschritt zurück und kehrt zur Routenauswahl zurück. + +Die aktuell geöffnete beziehungsweise automatisch aktivierte Station wird in der Stationsliste sichtbar hervorgehoben und mit `aria-current="step"` semantisch gekennzeichnet. Beim Eintritt in den Auslöseradius wird ihre Audiodatei mit `preload="auto"` vorbereitet, aber niemals automatisch gestartet. Die Wiedergabe beginnt ausschließlich über die nativen Audio-Bedienelemente. + +Die Vibration API ist in `public/js/vibration.js` gekapselt. Die aus dem historischen Projekt übernommenen Konstanten `MICRO`, `MINI`, `SHORT`, `LONG`, `ALARM` und `POI` stehen unter `Wegwichtel.Vibration.Patterns` zur Verfügung. Für eine neu aktivierte Station wird `ACTIVE_POI` mit dem Muster `[500, 300, 500]` verwendet. Nicht unterstützende Browser ignorieren den Aufruf ohne Fehler; Pause und Beenden brechen ein laufendes Muster ab. + +Beispiel für einen Aufruf mit Konstante: + +```javascript +Wegwichtel.Vibration.start(Wegwichtel.Vibration.Patterns.ACTIVE_POI); +``` + +Während die Route läuft, werden Positionsänderungen fortlaufend verarbeitet. Eine Browser-Webanwendung ist jedoch kein nativer Hintergrunddienst: Betriebssystem und Browser können die Aktualisierung bei gesperrtem Bildschirm, Energiesparmodus oder im Hintergrund drosseln beziehungsweise anhalten. + +Die Symbole liegen unter `public/images/icons/`. Sie befinden sich innerhalb nativer `