Drucker-Pi aus der Oberflaeche heraus einrichten
Die Einrichtung war Handarbeit: Dateien per scp auf den Pi, dort install.sh mit sudo starten, Token abschreiben, in die .env eintragen, Dienst neu starten. Das ist genau die Sorte Arbeit, die beim naechsten Geraetetausch niemand mehr weiss. Jetzt macht das ein Knopf unter "Einstellungen": Adresse, Benutzer und Passwort eintragen, und die App meldet sich per SSH an, prueft Python/systemd/Druckergeraet, uebertraegt deploy/bondrucker/, startet install.sh mit einem selbst erzeugten Token, raeumt auf und prueft von aussen nach. Jeder Schritt steht mit Ergebnis im Protokoll. Damit das ueberhaupt Sinn ergibt, liegt der Druckerzugang jetzt in der Datenbank statt in der .env - sonst muesste hinterher doch wieder jemand auf den Server. Die PRINTER_*-Werte in der .env sind nur noch Startwerte beim allerersten Start, wie RATE_PER_10MIN auch. Zwei Fehler, die beim Testen der Einrichtung auffielen: install.sh startete den Dienst nicht neu, sondern nur "enable --now". Bei einer erneuten Einrichtung lief die Bruecke deshalb mit dem ALTEN Token weiter und lehnte jeden Bon mit HTTP 403 ab. Jetzt: restart. Die Gegenprobe fragte nur /status ab - und /status prueft kein Token. Ein Dienst mit altem Token galt damit faelschlich als bereit. Die Bruecke meldet in /status jetzt zusaetzlich, ob das mitgeschickte Token passt (Version 1.1), und die Einrichtung prueft das mit. Zugangsdaten werden einmal benutzt und danach vergessen; das erzeugte Token wird aus dem Protokoll entfernt, bevor es in den Browser oder ins Journal geht. Neu dabei: ssh2, reines JavaScript. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
70756f3762
commit
5e23ccdcbc
@@ -18,8 +18,10 @@ gedacht für den Einsatz im Ladengeschäft (Tresen-Tablet, mehrere Plätze paral
|
||||
* **Keine Datenbank-Installation nötig.** SQLite, eine einzelne Datei.
|
||||
* **Keine Build-Kette.** Kein Webpack, kein TypeScript, kein npm run build.
|
||||
Vanilla JS im Frontend, Express im Backend.
|
||||
* **Abhängigkeiten:** 6 npm-Pakete, alle etabliert (express, express-session,
|
||||
express-rate-limit, sqlite3, bcrypt, dotenv).
|
||||
* **Abhängigkeiten:** 8 npm-Pakete, alle etabliert (express, express-session,
|
||||
express-rate-limit, sqlite3, bcrypt, dotenv, openid-client, ssh2). `ssh2` ist
|
||||
reines JavaScript und wird nur für die Einrichtung eines Drucker-Pi gebraucht
|
||||
(Abschnitt 6).
|
||||
|
||||
Realistisch: In 15 Minuten deployt, wenn die drei Stolpersteine aus Abschnitt 3
|
||||
beachtet werden. Ohne sie kann es beliebig lange dauern, weil zwei davon **stumm**
|
||||
@@ -213,6 +215,12 @@ Passwort fehlen — lieber laut scheitern als unsicher laufen.
|
||||
| `PRINTER_WIDTH` | `42` | Zeichen je Zeile: `42` bei 80-mm-Papier, `32` bei 58 mm |
|
||||
| `PRINTER_TIMEOUT_MS` | `6000` | Abbruch, wenn der Pi nicht antwortet |
|
||||
|
||||
**Die vier `PRINTER_*`-Werte oberhalb von `PRINTER_TIMEOUT_MS` sind nur
|
||||
Startwerte beim allerersten Start** — genau wie `RATE_PER_10MIN`. Danach lebt
|
||||
der Druckerzugang in der Datenbank und wird über die Oberfläche gepflegt; eine
|
||||
spätere Änderung in der `.env` bleibt wirkungslos. Der Grund steht in
|
||||
Abschnitt 6: ein Druckerwechsel soll kein Server-Zugang sein.
|
||||
|
||||
`SESSION_SECRET` muss ein *echter* Zufallswert sein. In der Vorgängerversion
|
||||
stand dort versehentlich der literale Text `$(openssl rand -base64 32)` — die
|
||||
Befehlssubstitution war beim Anlegen der Datei nie ausgeführt worden. Mit einem
|
||||
@@ -290,6 +298,37 @@ Kopf- und Fußzeilen des Bons stehen in der Datenbank und werden unter
|
||||
drucken". Der Probedruck dort beantwortet die Frage, ob es am Drucker oder an
|
||||
der App liegt.
|
||||
|
||||
#### Einen Pi einrichten
|
||||
|
||||
⚙️ Einstellungen → **🔧 Drucker einrichten** richtet einen frisch aufgesetzten
|
||||
Raspberry Pi vollständig selbst ein: IP, Benutzer und Passwort eintragen,
|
||||
Knopf drücken, fertig — rund drei Sekunden.
|
||||
|
||||
Die App meldet sich per SSH an, prüft Python/systemd/Druckergerät, überträgt die
|
||||
vier Dateien aus `deploy/bondrucker/` per SFTP nach `/tmp`, startet `install.sh`
|
||||
mit einem selbst erzeugten Token, räumt auf und prüft von außen nach. Danach
|
||||
übernimmt sie den Drucker selbst. Jeder Schritt steht mit Ergebnis im Protokoll.
|
||||
|
||||
Warum der Druckerzugang in der Datenbank liegt und nicht in der `.env`: Sonst
|
||||
wäre genau dieser Knopf sinnlos — die Werte müssten hinterher doch wieder von
|
||||
Hand auf dem Server eingetragen werden. Ein Druckerwechsel im Laden soll kein
|
||||
SSH-Zugang zum Timer-Server sein.
|
||||
|
||||
Zum Umgang mit den Zugangsdaten: Benutzername und Passwort stehen nur während
|
||||
des Vorgangs im Arbeitsspeicher. Sie werden nicht gespeichert, nicht ins Journal
|
||||
geschrieben und nicht an den Browser zurückgegeben; das erzeugte Drucker-Token
|
||||
wird vor der Ausgabe aus dem Protokoll entfernt. Dauerhaft bleibt allein dieses
|
||||
Token in der Datenbank.
|
||||
|
||||
Der Hostschlüssel des Pi wird beim Erstkontakt angenommen und im Protokoll
|
||||
ausgewiesen (`SHA256:…`) — im eigenen Netz vertretbar, aber es ist eben *kein*
|
||||
Schutz gegen einen Angreifer, der schon im Netz sitzt.
|
||||
|
||||
`install.sh` ist wiederholbar und **startet den Dienst dabei neu**. Ohne den
|
||||
Neustart liefe die Brücke mit dem alten Token weiter und würde jeden Bon mit
|
||||
HTTP 403 ablehnen — die Gegenprobe nach der Einrichtung prüft deshalb nicht nur
|
||||
die Erreichbarkeit, sondern auch, ob das neue Token angenommen wird.
|
||||
|
||||
**Der Pi hängt per WLAN und DHCP im Netz.** Wechselt seine Adresse, druckt die
|
||||
App nicht mehr, bis `PRINTER_HOST` angepasst ist — eine DHCP-Reservierung im
|
||||
Router erspart das.
|
||||
@@ -323,6 +362,7 @@ timer-app/
|
||||
│ ├── escpos.js ← ESC/POS-Bausteine für den Bondrucker
|
||||
│ ├── receipt.js ← Layout des Bons
|
||||
│ ├── printer.js ← Aufruf der Brücke auf dem Pi
|
||||
│ ├── provision.js ← richtet einen Drucker-Pi per SSH ein
|
||||
│ ├── package.json
|
||||
│ ├── package-lock.json
|
||||
│ ├── .env.example
|
||||
@@ -416,6 +456,7 @@ Alle Endpunkte außer `/health`, `/api/login`, `/api/auth/status` und
|
||||
| `PUT` | `/api/settings` | `{rateAmount, rateInterval}` sowie optional `{receiptHeader, receiptFooter, printAutoOnStop}` |
|
||||
| `GET` | `/api/printer/status` | Erreichbarkeit und Zustand des Druckers |
|
||||
| `POST` | `/api/printer/test` | Probedruck |
|
||||
| `POST` | `/api/printer/provision` | `{host, port, username, password}` → richtet den Pi ein und übernimmt ihn. Antwort enthält das Schrittprotokoll, nie die Zugangsdaten |
|
||||
| `GET` | `/api/sessions` | Historie; Parameter `from`, `to`, `q`, `limit`, `offset` |
|
||||
| `DELETE` | `/api/sessions/:id` | Eintrag löschen |
|
||||
| `POST` | `/api/sessions/:id/receipt` | Bon aus der Historie nachdrucken |
|
||||
|
||||
Reference in New Issue
Block a user