cleanup and integrate nginx server changes for image preview

This commit is contained in:
Florian Zumpe
2026-10-04 09:53:02 +02:00
parent 74090723c4
commit 55c2cd2ac4
15 changed files with 20 additions and 881 deletions
-66
View File
@@ -1,66 +0,0 @@
# Wegwichtel: Sprache der App
GPX-Änderungen berücksichtigen `GPX-Aufzeichnung.md`: Stationen sind `wpt`,
Aufzeichnungen sind `trkpt`, der Server normalisiert zu `rtept`. Fehlende
Koordinaten bleiben gespeichert und bearbeitbar; keine Ersatzkoordinaten erfinden.
Die GPX-Dateien sind die einzige Quelle der Routenpunkte und berechneten
Routenwerte. SQLite speichert davon nur Min-/Max-Grenzen für die Näheresuche.
Schemaänderungen immer auch mit der Startprüfung an einer vorhandenen,
frisch erzeugten Testdatenbank prüfen.
Diese Vorgabe des Nutzers gilt für alle künftigen Änderungen an der Oberfläche.
Die App richtet sich an Grundschulkinder und Menschen ohne technische Vorkenntnisse.
- Schreibe kurze, verständliche Sätze. Sprich die Nutzer mit „du“ an.
- Erkläre, was eine Aktion bewirkt und was als Nächstes zu tun ist.
- Verwende durchgängig „Strecke“, „Station“, „Meine Entwürfe“ und „für unterwegs speichern“.
- Technische Begriffe wie GPX, JSON, Cache, API, OPFS, IndexedDB, Local Storage,
Server, Token oder interne Feldnamen gehören nicht in Bedienungstexte,
Hilfe, Statusmeldungen, Dateiauswahl-Beschreibungen oder vorgelesene Beschriftungen.
- Bezeichne einen Wegexport als „Weg als Datei herunterladen“. Erkläre bei Bedarf,
dass die Datei für eine andere Wander-App gedacht ist und keine Bilder oder Ton enthält.
- Verwende „Bilder und Tonaufnahmen“ statt „Medien“, „Zur Prüfung senden“ statt „Upload“
und „auf diesem Gerät“ statt „lokal im Browser“.
- Fehlermeldungen nennen das Problem und einen verständlichen nächsten Schritt.
Zeige keine rohen Fehler aus dem Betriebssystem, Netzwerkantworten oder Dateipfade.
- Vereinfache auch Hilfen zu Daten und Speicher. Behalte wichtige Angaben zu
Löschung, Erlaubnissen, Grenzen und Speicher-Schätzungen bei. Mache keine
Zusicherungen, die die App nicht einhalten kann.
- Rechtliche Texte dürfen notwendige Rechtsbegriffe behalten. Erkläre sie einfach;
ändere dabei nicht die Bedeutung, Verantwortlichen oder Kontaktdaten.
- Fachliche Details für Entwicklung und Betrieb gehören in Projekt-Dokumentation
und Code-Kommentare. Code-Bezeichner, Dateiformate und Schnittstellen bleiben fachlich präzise.
- Prüfe neue Texte und angepasste Text-Erwartungen in den vorhandenen Tests.
## Texte und Übersetzungen
- Alle eigenen Bedienungstexte liegen zentral in der Klasse `WegwichtelTexts`
in `public/js/texts-de.js`. Keine neuen sichtbaren Texte direkt in HTML oder
Funktionscode schreiben.
- Ausnahme: Impressum und Datenschutzerklärung bleiben ausschließlich statisch
auf Deutsch in `public/index.html` und tragen `lang="de"`. Ihre Texte und
Kontaktangaben gehören nicht in Sprachdateien und erhalten keine i18n-Attribute.
Auch ihre Dialogüberschriften bleiben beim Sprachwechsel auf Deutsch.
- Das HTML enthält daraus erzeugte deutsche Standardtexte, damit Beschriftungen
vor dem Laden der Skripte vorhanden sind. Nach Änderungen am Textkatalog
`node scripts/localize-html.js` ausführen und `public/index.html` mit ablegen.
Diese erzeugten Texte nicht von Hand ändern; die i18n-Attribute bleiben erhalten.
- JavaScript bezieht Texte über `Wegwichtel.I18n.t(textKey, variables)`.
HTML verwendet `data-i18n` bzw. `data-i18n-aria-label`, `data-i18n-title`,
`data-i18n-placeholder` und `data-i18n-alt`.
- Zahlen und Namen sind Platzhalter; ganze Sätze übersetzen, keine Sätze aus
deutschen Wortteilen zusammensetzen. Für Mengen Einzahl und Mehrzahl verwenden.
- Nutzerinhalte wie Streckennamen, Beschreibungen und Aufnahmen nicht automatisch
übersetzen oder verändern. Interne Fehlerdiagnosen dürfen technisch bleiben;
die sichtbare Meldung muss verständlich sein.
- Sprachdateien heißen `public/js/texts-<Sprache>.js` und verwenden dieselbe
generische Klasse `WegwichtelTexts`. Sie registrieren sich in `TextCatalogs`.
Die App lädt Deutsch und Englisch und wählt anhand von `navigator.languages`
bzw. `navigator.language`. Fehlende Übersetzungen fallen auf den ersten
geladenen Katalog zurück (in der App Deutsch).
- Verwende kurze, stabile englische Funktionsschlüssel wie `app.no-route` oder
`bootstrap-loader.error.start-error`. Schlüssel nicht aus dem deutschen Text
ableiten. Startmeldungen in `bootstrap-loader.steps` nach Schritt gruppieren.
- Deutsch und Englisch enthalten dieselben Schlüssel, Platzhalter und Pluralformen.
Texte in beiden Sprachen einfach und sinngemäß schreiben. Sprachwechsel aktualisieren feste Beschriftungen;
bereits erzeugte dynamische Ansichten bei Bedarf neu rendern.
-80
View File
@@ -1,80 +0,0 @@
# 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` 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`.
-99
View File
@@ -1,99 +0,0 @@
# GPX und Wegaufzeichnung
## Stationen und Weg getrennt speichern
- Stationen mit Koordinaten stehen als `wpt` unmittelbar unter `gpx`.
- Beim ersten Speichern eines neuen Entwurfs startet während der Bearbeitung eine Aufzeichnung:
sofort eine Position, danach alle 30 Sekunden eine neue GPS-Abfrage.
Schließen, Abbrechen, Rückkehr zur Streckenliste oder Wechsel der Strecke
beendet die Aufzeichnung. Nach einem Neustart ist sie inaktiv. Beim erneuten
Öffnen eines lokalen Entwurfs zur Bearbeitung beginnt eine neue Sitzung.
Speichern innerhalb derselben Sitzung hebt Pause oder bewusstes Stoppen nicht auf.
- Die sichtbaren Schaltflächen erlauben Pausieren, Fortsetzen und Beenden.
Beim Löschen des Entwurfs endet die Aufzeichnung ebenfalls.
- Tatsächliche Positionen stehen als `trk/trkseg/trkpt` mit Zeitstempel und,
falls vorhanden, Höhe in der Datei des Entwurfs.
- Ohne Standort entsteht eine Erweiterung `wegwichtel:gpsGap` mit Zeitstempel.
Eine Station ohne Standort steht als `wegwichtel:pendingWaypoint` in den
Erweiterungen. Es werden keine Koordinaten wie `0,0` erfunden. Leere GPX-
`wpt` oder `trkpt` wären ungültig, daher werden Erweiterungen verwendet.
Die Aufzeichnung speichert immer die neueste Fassung des Entwurfs, damit
gleichzeitige Änderungen an Stationen erhalten bleiben. Auch eine manuelle
Bearbeitung ergänzt die neueste Aufzeichnung und bewahrt inzwischen hinzugekommene
Positionen. Speicherfehler pausieren die Aufzeichnung und zeigen einen Hinweis.
## Später Koordinaten ergänzen
Entwürfe und Stationen dürfen ohne GPS erstellt und gespeichert werden.
In der Streckenbearbeitung können unter „Orte auf dem Weg ergänzen“ fehlende
Positionen ergänzt, vorhandene geändert und weitere hinzugefügt werden.
Breiten- und Längengrad müssen gemeinsam angegeben werden oder beide leer bleiben.
Stationen haben eigene Felder unter „Ort ergänzen“.
Die Serverdatenbank erlaubt `NULL` in `pois.lat` und `pois.lon`.
Die Tabelle `route_points` entfällt. Routenpunkte, Start und Mittelpunkt,
Weglänge, Höhengewinn und Punktzahl werden aus der gespeicherten GPX-Datei
gelesen beziehungsweise berechnet. Die Änderung ist in
`database/schema.sql` enthalten; eine bestehende Datenbank bekommt sie nicht
allein durch das Kopieren des Codes. Das Projekt verwendet beim Deployment
die vereinbarte Neuerstellung der Datenbank, keine automatische Migration.
SQLite enthält weiterhin Verwaltungseinträge, Freigaben sowie Stations- und
Medienangaben. Von den berechneten Routenwerten bleiben dort ausschließlich
`min_lat`, `min_lon`, `max_lat` und `max_lon` als Suchwerte erhalten. Die
Näheresuche verwendet diese Grenzen zur Vorauswahl und berechnet anschließend
den Abstand zum Start aus der GPX-Datei. Anlegen, Ersetzen, Anhängen und Änderungen
an Stationen aktualisieren die Grenzen. Ohne Koordinaten bleiben alle vier leer.
Die Startprüfung erwartet dasselbe Schema einschließlich der optionalen
Stationskoordinaten, lehnt die entfernte Tabelle und Routenspalten ab und prüft
die Suchgrenzen gegen die GPX-Dateien. Ein zusätzlicher Test führt die Prüfung
in einem getrennten Prozess mit einer echten temporären Datenbank und Dateien
aus. Damit wird der Fall einer vorhandenen Datenbank auch beim normalen
Coverage-Lauf geprüft.
## Normalisierung auf dem Server
Beim Einreichen oder Ersetzen einer GPX-Datei normalisiert der Node-Dienst den
aufgezeichneten Weg zu `rte/rtept`. Bereits vorhandene Routenpunkte haben Vorrang.
Aufeinanderfolgende Trackpunkte innerhalb des eingestellten Abstands werden
zusammengefasst. Der erste Punkt bleibt der Vertreter; der nächste wird erst
übernommen, wenn er weiter entfernt liegt. So werden kleine Schwankungen
während einer Pause nicht fortlaufend zur Weglänge addiert.
`GPX_CLUSTER_METERS` in `.env` steuert den Abstand, standardmäßig 20 Meter.
Das Verfahren ist eine geometrische Vereinfachung ohne externen Routingdienst.
Es berechnet keine Wege auf einer Straßenkarte. GPS-Lücken werden bei der
Routenberechnung übersprungen; zwischen gültigen Nachbarpunkten entsteht eine
direkte Verbindung. Die Lücken bleiben für spätere Bearbeitung als Erweiterungen
erhalten. Gibt es keine gültigen Wegpositionen, bleiben Weglänge und Punktzahl
bei 0 Metern und 0 Punkten; Stationen bleiben trotzdem gespeichert.
Die gespeicherte Serverdatei enthält den normalisierten Weg. Änderungen an
Stationen synchronisieren die `wpt` und die noch unvollständigen Stationen in
dieser Datei. GPX verwendet unverändert den Namespace
`http://www.topografix.com/GPX/1/1`; das ist eine Kennung, kein Netzwerkabruf.
## Anzeige beim Abspielen
Zwei Pfeile zeigen gleichzeitig die Richtung:
- zum nächsten Routenpunkt, ohne Entfernungsangabe;
- zur nächsten noch offenen Station mit Koordinaten, mit Metern.
Bei einer Strecke mit ausschließlich Stationen fehlt nur die Wegführung.
Stationen ohne Koordinaten lösen keine Standort-Erkennung aus. Während einer
Pause werden die Pfeile verborgen.
## Grenzen und Prüfung
Die Aufzeichnung läuft nur im geöffneten App-Fenster. Neuladen beendet sie;
bereits gespeicherte Positionen bleiben erhalten. Browser und Betriebssystem
können Zeitgeber und Standort-Abfragen im Hintergrund oder bei gesperrtem
Bildschirm verzögern. Ein verlässlicher Hintergrunddienst wird nicht zugesichert.
Bei verweigerter Standort-Erlaubnis bleiben die Einträge leer und bearbeitbar.
Die Tests prüfen Aufzeichnung, Pause/Fortsetzen/Ende, verspätete GPS-Antworten,
Speicherfehler, GPS-Lücken, manuelle Ergänzungen, getrennte Pfeile,
Servernormalisierung sowie Stationen ohne Koordinaten über die API.
-56
View File
@@ -1,56 +0,0 @@
# Texte und Sprachen
Die Sprachdateien `public/js/texts-de.js` und `public/js/texts-en.js` enthalten
jeweils die generische Klasse `WegwichtelTexts`. Beide Dateien verwenden dieselben
Schlüssel und registrieren ihren Katalog mit der eigenen Sprachkennung in
`Wegwichtel.TextCatalogs`. Es gibt keine sprachabhängigen Klassennamen.
Die App lädt beide Kataloge vor `i18n.js`. Beim Start wird die erste unterstützte
Sprache aus `navigator.languages` gewählt; ohne diese Liste wird
`navigator.language` verwendet. Regionale Varianten wie `en-GB` oder `de-DE`
verwenden den passenden Grundkatalog. Passt keine Sprache, wird der erste
geladene Katalog verwendet: in der App Deutsch. Einzelne fehlende Übersetzungen
fallen ebenfalls auf diesen Katalog zurück. Wird nur eine Sprachdatei eingebunden,
funktioniert sie allein und bildet den Fallback.
## Verwendung
Schlüssel beschreiben die Funktion, nicht den Wortlaut. Zusammengehörige Texte
liegen in verschachtelten Abschnitten; der Aufruf verwendet Punkte als Trennung.
```js
Wegwichtel.I18n.t('app.no-route');
Wegwichtel.I18n.t('bootstrap-loader.error.start-error');
Wegwichtel.I18n.t(`bootstrap-loader.steps.${step}`);
Wegwichtel.I18n.t('media.image.count', { count: 2 });
```
`bootstrap-loader.steps` ist nach den stabilen Schrittkennungen aufgebaut:
`storage`, `jquery`, `jquery-ui`, `modules`, `server`, `routes` und `location`.
Die Zuordnung benötigt keine zusätzliche Liste von Nachrichtenschlüsseln.
HTML verwendet `data-i18n`, `data-i18n-title`, `data-i18n-aria-label`,
`data-i18n-placeholder` und `data-i18n-alt`. Deutsche Standardtexte werden mit
`node scripts/localize-html.js` aus dem Katalog ins HTML übernommen. Dadurch
stehen Beschriftungen bereits vor dem Laden der Skripte bereit. Nach Änderungen
die erzeugte `public/index.html` mit ins Repository übernehmen.
Impressum und Datenschutzerklärung sind davon ausgenommen. Ihre rechtlichen
Texte und Kontaktangaben stehen ausschließlich statisch auf Deutsch in
`public/index.html`, ohne i18n-Attribute und mit `lang="de"` an beiden Artikeln.
Änderungen an diesen Angaben direkt im HTML vornehmen. Auch die zugehörigen
Dialogüberschriften bleiben auf Deutsch. Die Menüeinträge und die übrige
Bedienoberfläche werden weiterhin übersetzt.
Zum gezielten Prüfen kann `Wegwichtel.I18n.setLanguage('en')` aufgerufen werden.
Das aktualisiert feste Beschriftungen. Bereits aufgebaute dynamische Ansichten
müssen neu aufgebaut werden; die automatische Startauswahl erfolgt vor ihrem
ersten Aufbau. Ein sichtbarer Sprachschalter ist nicht enthalten.
Die Tests prüfen beide Kataloge, Platzhalter und Pluralformen, Browserpräferenzen,
den Betrieb mit nur einer Sprachdatei, deutsche HTML-Standardtexte und englische
App-Meldungen. Nutzerinhalte wie Streckennamen werden nicht übersetzt.
SonarQube prüft auch die Sprachdateien auf Probleme und Testabdeckung. Nur die
Duplikaterkennung nimmt `public/js/texts-*.js` aus: Übersetzungskataloge müssen
dieselbe Struktur haben und sind keine mehrfach implementierte Programmlogik.
-68
View File
@@ -1,68 +0,0 @@
# 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.
-218
View File
@@ -1,218 +0,0 @@
# 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.
## 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`](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 [`docs/REST-API.md`](docs/REST-API.md) dokumentiert. Die README beschreibt bewusst nur Installation, Architektur und Bedienverhalten.
## Python-Werkzeuge
Unter [`tools/python/`](tools/python/README.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
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 `<gpx>`-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 `<button>`-Elemente. Die Bilder selbst sind dekorativ (`alt=""`); der zugängliche Name wird über `aria-label` und `title` am jeweiligen Button bereitgestellt.
## Datenschutz, Impressum und lokale Browserdaten
Die Oberfläche enthält rechts oben einen global verfügbaren Informationsbutton. Er öffnet ein touch- und tastaturbedienbares Overlay mit Datenschutzerklärung, Impressum sowie einer technischen Erläuterung zu Cookies, Local Storage und den lokal eingebundenen JavaScript-Bibliotheken.
Die enthaltenen Rechtstexte sind technische Vorlagen. Vor einer öffentlichen Bereitstellung müssen insbesondere Betreiber, ladungsfähige Anschrift, Kontakt, Hostinganbieter, Protokollierungsdauer, Datenschutzkontakt und zuständige Aufsicht ergänzt und rechtlich geprüft werden. Eine Checkliste steht in [`docs/PUBLICATION-CHECKLIST.md`](docs/PUBLICATION-CHECKLIST.md).
Wegwichtel setzt selbst keine Cookies und bindet keine Analyse-, Werbe- oder Trackingdienste ein. Der Startbildschirm weist darauf hin. Die freiwillige Speicherung des Streckenfortschritts wird getrennt gesteuert:
- `wegwichtel.privacy.v1` speichert die gewählte Einstellung,
- `wegwichtel.route-progress.v1` enthält bei aktivierter Funktion ein JSON-Objekt mit aktueller Route, Routenstatus, besuchten und angesehenen Stationen, abgespielten Audioeinträgen und Zeitstempeln,
- Streckendaten mit einer letzten Aktualisierung von mindestens 24 Stunden werden beim nächsten Start schrittweise entfernt,
- beim Ausschalten der Funktion wird der gesamte Streckenfortschritt sofort aus dem Local Storage gelöscht,
- ohne aktivierte Speicherung bleiben alle Routen- und Stationsdaten ausschließlich im Arbeitsspeicher des geöffneten Browserfensters.
Der Initialisierungsbildschirm bleibt nach erfolgreichem Laden sichtbar. Erst eine bewusste Pointer- oder Tastaturaktion öffnet die eigentliche Anwendung. Das Informationsfenster und die Datenschutzeinstellungen können bereits auf dem Startbildschirm verwendet werden, ohne die Anwendung weiterzuschalten.
-17
View File
@@ -1,17 +0,0 @@
# Sicherheitshinweise
## Legacy-Projekt
Das historische PHP-Projekt enthielt fest eingetragene Datenbank-Zugangsdaten. Diese Werte wurden nicht in das neue Projekt übernommen. Bestehende Zugangsdaten des Altprojekts sollten rotiert und anschließend aus der Repository-Historie entfernt beziehungsweise als kompromittiert behandelt werden.
## Stand dieses Skeletts
Die Schreibendpunkte besitzen absichtlich noch keine Authentifizierung und dürfen so nicht öffentlich erreichbar gemacht werden. Vor einem Produktivbetrieb sind mindestens erforderlich:
- Login und Rollen für Lehrkräfte, Redakteure und Administration,
- CSRF-Schutz beziehungsweise tokenbasierte API-Authentifizierung,
- serverseitige Prüfung tatsächlicher Datei-Inhalte statt nur MIME-Typ und Endung,
- Begrenzung von Request-Rate, Speicherverbrauch und Medienabmessungen,
- Protokollierung sämtlicher Änderungen und Wiederherstellungen,
- HTTPS, sichere Header und ein restriktiver Reverse-Proxy,
- Datenschutzkonzept für Standortdaten und von Schülerinnen und Schülern erstellte Medien.
-53
View File
@@ -1,53 +0,0 @@
# SonarQube-Auswertung vom 3. Oktober 2026
Basis: Export `Wegwichtel-sonarqube-export(17).json`, Repository `cf4170f`.
Von 119 exportierten Issues sind fünf offen; die übrigen sind bereits geschlossen.
## Code-Korrekturen
- `javascript:S7773`, `public/js/local-routes.js`: beide globalen `NaN` durch
`Number.NaN` ersetzt. Fehlende Positionen bleiben weiterhin ungültige Zahlen
und werden als GPS-Lücken gespeichert.
- `javascript:S3358`, `src/services/gpx.js`: Auswahl der Punkte für die
Metrikberechnung in eine eigene Variable ausgelagert. Keine verschachtelte
bedingte Operation mehr; die bisherigen Fälle bleiben erhalten.
- `javascript:S1481`, `src/services/gpx.js`: die beiden unbenutzten Variablen
`_oldWaypoints` und `_oldPending` entfernt. Alte Wegpunkte und unvollständige
Stationen werden gezielt entfernt, bevor die aktuelle Stationsliste entsteht.
Andere Erweiterungen bleiben erhalten.
Das bereits korrigierte Moderationsskript bleibt unverändert.
## Security-Hotspot mit notwendiger Bewertung in SonarQube
- Schlüssel: `7854386c-ec53-4e14-b374-d7d356accf23`
- Datei: `src/services/gpx.js`
- Regel: `javascript:S5332`
- Meldung: Verwendung des HTTP-Protokolls
- Empfohlene Bewertung nach Prüfung: **Safe**
Begründung zum Einfügen in SonarQube:
> Die Zeichenfolge `http://www.topografix.com/GPX/1/1` ist der offizielle
> XML-Namespace von GPX 1.1. Die Funktion schreibt sie als xmlns-Attribut in die
> erzeugte GPX-Datei. Sie stellt keine HTTP-Verbindung her und überträgt keine
> Daten an diese Adresse. Ein Wechsel zu https würde einen anderen Namespace
> erzeugen und die GPX-Kompatibilität beeinträchtigen. Daher besteht an dieser
> Stelle kein unverschlüsselter Datentransport.
Offizielle Referenz: https://www.topografix.com/GPX/1/1/
Der gleiche Namespace in `public/js/local-routes.js` wurde laut Export bereits
als **Safe** bewertet. Der neue Hotspot ist weiterhin **TO_REVIEW**; ein
Code-Kommentar oder dieses Dokument ändert seinen Status in SonarQube nicht.
Es wurden keine Regeln deaktiviert und keine Findings per Suppression verborgen.
## Quality Gate laut Export
- Neue Coverage: 87,8 %, bestanden (mindestens 80 %).
- Neue Duplikate: rund 0,35 %, bestanden (höchstens 3 %).
- Neue offene Findings: fünf, durch die Code-Korrekturen adressiert.
- Geprüfte Security-Hotspots: 50 %, noch nicht bestanden (erforderlich 100 %).
Die abschließende Bewertung des Quality Gates benötigt einen neuen Scan und
die Bewertung des genannten Hotspots in SonarQube.
-20
View File
@@ -1,20 +0,0 @@
# Moderation: Ersteller und Gerätekennung
Umgesetzt: dauerhafte, zufällige 256-Bit-Gerätekennung im getrennten Local Storage,
`creator_id` in lokalen Entwürfen und Server-Strecken, Anzeige im Moderationsskript,
gezieltes Löschen nur nach ausdrücklicher Bestätigung und erneutes Einsenden noch
nicht freigegebener Strecken. Beschreibung: `CREATOR-ID.md`.
Das GPX-Attribut `creator="Wegwichtel"` bezeichnet weiterhin nur das Programm.
Noch offen für gemeinsame Bearbeitung:
- Authentifizierte Eigentumszuordnung und Einladungen für Mitbearbeiter entwickeln.
Eine frei übermittelte Gerätekennung allein beweist kein Eigentum.
- Wiederherstellung nach verlorenen Browserdaten und bewussten Gerätewechsel planen.
- Gleichzeitige Änderungen und Versionskonflikte erkennen; der derzeitige lokale
Entwurf wird beim Einsenden als vollständiger Stand behandelt.
- Idempotente POSTs auch bei verlorenem HTTP-Erfolg und Gesamtübertragungen mit
mehreren Dateien absichern.
Die Ersteller-ID ersetzt keine Anmeldung. Der Moderationszugang ist separat
über Nginx und eine Passwortdatei geschützt (siehe `MODERATION-MEDIEN.md`).
-47
View File
@@ -1,47 +0,0 @@
# Wegwichtel Editor v5
Dieses Paket bereinigt den einfachen Streckeneditor nach dem grünen SonarQube-Lauf weiter.
## Status
Der zuletzt geprüfte SonarQube-Export meldet ein bestandenes Quality Gate, keine Bugs, keine Vulnerabilities, keine Code Smells und keine Security Hotspots. Die New-Coverage lag bei 85,1 %.
## Fachliche Optimierungen gegenüber v4
- Der Editor sendet `activated_at` nicht mehr beim Speichern von Strecken.
- Neue Strecken bleiben durch den DB-Default unveröffentlicht.
- Bereits manuell aktivierte Strecken werden durch spätere Editor-Änderungen nicht versehentlich deaktiviert.
- Der Plus-Button besitzt `aria-haspopup="dialog"`, `aria-expanded` und `aria-controls`; beim Öffnen und Schließen wird `aria-expanded` synchronisiert.
- POI-Sortierpfeile sind am Listenanfang beziehungsweise Listenende deaktiviert.
- Das Initialschema enthält zusätzlich `idx_routes_activation` für spätere veröffentlichte Listenabfragen nach `status` und `activated_at`.
- POIs erhalten analog zu Strecken einen `pois_updated_at`-Trigger, damit `updated_at` bei Bearbeitungen automatisch aktualisiert wird.
- Die Dokumentation verwendet durchgehend `TEXT DEFAULT NULL` statt des für SQLite-STRICT falschen `DATETIME`.
## Datenbankschema
Für neu erzeugte Datenbanken steht die Spalte direkt in `database/schema.sql`:
```sql
activated_at TEXT DEFAULT NULL
```
Für bestehende SQLite-Datenbanken liegt weiterhin eine Migration vor:
```bash
sqlite3 <pfad-zur-wegwichtel-db> < database/migrations/20260901_add_route_activated_at.sql
```
Die Migration darf nur auf Bestandsdatenbanken ohne `activated_at` ausgeführt werden. Auf einer frischen Datenbank, die schon mit dem aktuellen `database/schema.sql` erstellt wurde, ist sie nicht nötig.
## Tests
```bash
node --check public/js/editor-ui.js
npm test -- test/editor-ui.test.js
npm test -- test/editor-database-schema.test.js
npm run test:coverage
```
## Optionaler Cleanup
`scripts/apply-editor-schema.mjs` war nur als Übergangshilfe gedacht. Nach diesem Paket ist das Initialschema direkt enthalten. Die Datei wird nicht mehr benötigt und kann entfernt werden, falls sie aus v3/v4 noch im Repository vorhanden ist.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

-22
View File
@@ -1,22 +0,0 @@
# In den bestehenden HTTPS-server-Block aufnehmen.
# Passwortdatei außerhalb des Projekt-/Webverzeichnisses ablegen.
location ^~ /moderation/api/ {
if ($scheme != https) { return 403; }
auth_basic "Wegwichtel Moderation";
auth_basic_user_file /etc/nginx/wegwichtel-moderation.htpasswd;
limit_except GET { deny all; }
proxy_pass http://127.0.0.1:47145/api/;
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_set_header Authorization "";
proxy_cache off;
proxy_hide_header Cache-Control;
add_header Cache-Control "no-store" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "no-referrer" always;
}
+20
View File
@@ -43,6 +43,26 @@ server {
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Passwortdatei außerhalb des Projekt-/Webverzeichnisses ablegen.
location ^~ /moderation/api/ {
if ($scheme != https) { return 403; }
auth_basic "Wegwichtel Moderation";
auth_basic_user_file /etc/nginx/wegwichtel-moderation.htpasswd;
limit_except GET { deny all; }
proxy_pass http://127.0.0.1:47145/api/;
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_set_header Authorization "";
proxy_cache off;
proxy_hide_header Cache-Control;
add_header Cache-Control "no-store" always;
add_header Referrer-Policy "no-referrer" always;
}
location / {
proxy_pass http://127.0.0.1:47145;
-14
View File
@@ -1,14 +0,0 @@
# Lokale Client-Abhängigkeiten
Die Browseranwendung verwendet ausschließlich die hier 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` in dieses Verzeichnis.
Die Dateien sind bereits im Projektarchiv enthalten. Der Browser benötigt deshalb weder ein CDN noch eine Netzwerkverbindung zum Laden der UI-Bibliotheken.
-121
View File
@@ -1,121 +0,0 @@
# 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.
## 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](../../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.