You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

378 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Monitor-Check
**Bildschirme, Lautsprecher und Übersetzungsanlagen vor Ort per Handy erfassen und prüfen.**
Bei Veranstaltungen mit vielen Monitoren, z. B. in Foyers, Nebenräumen oder
Übertragungssälen, muss vorher jedes Gerät einmal geprüft werden: Kommt Ton?
Kommt Bild? Läuft die richtige Sprache? Monitor-Check macht daraus eine kleine
Web-App fürs Handy. Ihr geht von Gerät zu Gerät, macht ein Foto, tippt drei
Mal auf JA / NEIN / n.A., und die Übersicht zeigt sofort, was funktioniert und
wo jemand nachsehen muss.
```
Handy (Browser)
├── Foto ──────────────► dein Server (Node) ──► /uploads/…jpg
└── Daten ──────────────► Apps-Script Web-App ──► Google Tabelle
```
Die **Daten liegen in einer Google Tabelle**, die ihr selbst besitzt. Dort
könnt ihr filtern, drucken oder teilen. Die **Oberfläche und die Fotos liegen
auf eurem eigenen Server**. Im Repo sind keine Zugangsdaten enthalten: Token,
Tabellen-URL und Domain richtet ihr bei der Installation selbst ein.
### Was das Tool kann
- **Erfassen in Sekunden:** Bezeichnung, Standort, Foto direkt aus der Kamera, Status
- **Automatischer Status** aus den drei Prüfpunkten *Audio*, *Bild* und *Sprache*
- **Übersicht** mit Kacheln, Suche und Statusfilter
- **Fortlaufende IDs** (`MON-001`, `MON-002` …), die nie doppelt vergeben werden
- **Protokoll** jeder Änderung mit Zeit und Person im Blatt `Protokoll`
- **Offline-Warteschlange:** Änderungen ohne Netz werden nachgereicht
- **Als App installierbar** über „Zum Home-Bildschirm“
### So kommt ihr zum Laufen
1. **Google Tabelle** anlegen und das Apps Script einfügen ([Teil 1](#teil-1--google-tabelle-einrichten))
2. **Server** mit `install.sh` aufsetzen ([Teil 2](#teil-2--server-aufsetzen))
3. **Am Handy** die Seite öffnen und im Setup-Tab URL und Token eintragen ([Teil 3](#teil-3--oberfläche-verbinden))
Dafür braucht ihr ein Google-Konto, einen Linux-Server oder LXC-Container
(Ubuntu 22.04/24.04) und eine Domain mit HTTPS. Ohne HTTPS gibt das Handy die
Kamera nicht frei.
### Anpassen
| Was | Wo |
|---|---|
| ID-Präfix (`MON`) und Stellenzahl | `apps-script/Code.gs` → `CONFIG.ID_PREFIX`, `CONFIG.ID_PAD` |
| Namen der Tabellenblätter | `apps-script/Code.gs` → `CONFIG.SHEET_NAME`, `CONFIG.LOG_SHEET_NAME` |
| Statusbezeichnungen | `apps-script/Code.gs` → `STATUS` und gleichlautend in `web/index.html` |
| Zugangsdaten am Gerät vorbelegen | `web/index.html` → `DEFAULTS` (nicht committen, wenn echte Werte drinstehen) |
| Bildgröße, Upload-Limit, Port | `server/.env` (Vorlage: `server/.env.example`) |
## Statusregeln
Jeder Eintrag hat drei Prüfpunkte: **Audio**, **Bild**, **Sprache** – jeweils `JA`, `NEIN` oder `n.A.`
| Eingabe | Ergebnis |
|---|---|
| alle bewerteten Punkte `JA` | **Funktionsfähig** |
| teilweise `NEIN` | **Teilweise funktionsfähig** |
| alle bewerteten Punkte `NEIN` | **Fehlerhaft** |
| alle drei `n.A.` | **Deaktiviert** |
| nichts erfasst | **Nicht geprüft** |
Ein einzelnes `n.A.` schließt nur diesen Punkt aus der Bewertung aus und zählt nie als Fehler. Beispiel: `n.A. / NEIN / NEIN` → **Fehlerhaft** (die zwei bewerteten Punkte sind beide nein), `n.A. / JA / NEIN` → **Teilweise funktionsfähig**.
Die Regeln stehen identisch in `Code.gs` (`berechneStatus`) und in `index.html` – das Sheet bleibt die maßgebliche Quelle, das Frontend zeigt die Vorschau sofort an.
---
## Teil 1 – Google Tabelle einrichten
1. Neue Tabelle auf [sheets.new](https://sheets.new) anlegen, z. B. „Monitore Veranstaltung 2026".
2. **Erweiterungen → Apps Script**. Den Standardinhalt von `Code.gs` löschen und den Inhalt aus `apps-script/Code.gs` einfügen.
3. Ganz oben `CONFIG.API_TOKEN` durch ein langes Zufallstoken ersetzen. Erzeugen z. B. mit:
```bash
openssl rand -hex 24
```
Dieses Token brauchst du gleich noch zweimal (Server und Oberfläche).
4. Speichern. Im Editor die Funktion `testStatusLogik` auswählen und **Ausführen** – beim ersten Mal Zugriff freigeben. Im Ausführungsprotokoll muss „Alle 9 Testfälle bestanden" stehen.
5. Zurück in der Tabelle: Seite neu laden, dann Menü **Monitor-Check → Tabelle einrichten**. Damit entstehen die Blätter `Monitore` und `Protokoll` samt Spaltenbreiten und Dropdowns.
6. Im Apps-Script-Editor: **Bereitstellen → Neue Bereitstellung → Typ: Web-App**
- Beschreibung: `Monitor-Check API`
- **Ausführen als: Ich**
- **Zugriff: Jeder** ← zwingend, sonst antwortet die API mit einer HTML-Anmeldeseite
- Bereitstellen, Berechtigungen bestätigen, die `…/exec`-URL kopieren.
> Nach jeder Änderung an `Code.gs`: **Bereitstellen → Bereitstellungen verwalten → Bearbeiten → Version: Neu → Bereitstellen**. Die URL bleibt dabei gleich.
---
## Teil 2 – Server aufsetzen
**Der schnelle Weg:** Projektordner auf den Server kopieren und `install.sh` laufen lassen – das erledigt alles ab Abschnitt 2.2.
```bash
# auf deinem Rechner (IP anpassen)
scp -r . root@192.168.1.50:/root/monitor-check-quelle
# auf dem Server
cd /root/monitor-check-quelle
sudo bash install.sh
```
Das Skript fragt nach Domain und E-Mail, erzeugt ein API-Token, installiert Node 20, nginx, den Dienst und holt das Let's-Encrypt-Zertifikat. Am Ende stehen alle Zugangsdaten auf dem Bildschirm und in `/opt/monitor-check/ZUGANGSDATEN.txt`.
Ohne Rückfragen:
```bash
sudo bash install.sh --domain monitore.deine-domain.de --email du@deine-domain.de --yes
```
Nützliche Schalter: `--dry-run` zeigt nur, was passieren würde. `--update` spielt nach Änderungen an `web/` oder `server/` die neuen Dateien ein und startet den Dienst neu. `--no-tls` und `--no-firewall` überspringen Zertifikat bzw. ufw. `--help` listet alles auf.
### Hinter einem vorgeschalteten Reverse-Proxy
Wenn TLS bereits auf einem vorgelagerten nginx liegt, übernimmt der Container nur HTTP:
```bash
sudo bash install.sh --behind-proxy --public-url https://monitore.deine-domain.de --yes
```
Das überspringt certbot, lässt den Container auf Port 80 lauschen und setzt `PUBLIC_URL` trotzdem auf **https** – wichtig, denn diese Adresse landet als Bild-Link in der Tabelle. Bei `http://` blockiert der Browser die Bilder später als Mixed Content.
Am Ende schreibt das Skript eine passende Vorlage nach `/opt/monitor-check/PROXY-SNIPPET.conf`, die du auf dem vorgelagerten Proxy einsetzen kannst. Ein Punkt daraus entscheidet über Erfolg oder Frust:
```nginx
client_max_body_size 20m;
```
Fehlt die Zeile im **äußeren** Proxy, brechen Foto-Uploads mit `413 Request Entity Too Large` ab – der Container selbst ist dann gar nicht schuld.
Der lokale nginx im Container darf auch ganz entfallen; dann hört Node direkt auf `0.0.0.0:8080` und der Proxy zielt dorthin:
```bash
sudo bash install.sh --behind-proxy --no-nginx --public-url https://monitore.deine-domain.de --yes
```
Der Unterschied ist klein: Mit lokalem nginx werden die Fotos direkt von der Platte ausgeliefert statt durch Node. Bei ein paar hundert Bildern spielt das keine Rolle – wähle, was besser in deine Landschaft passt.
Läuft das Skript durch, geht es direkt bei **Teil 3** weiter. Die Abschnitte 2.2 bis 2.7 beschreiben dieselben Schritte von Hand – nützlich zum Nachvollziehen oder Reparieren.
### 2.1 Container erstellen (auf dem Proxmox-Host)
Getestet mit Ubuntu 22.04/24.04 in einem unprivilegierten LXC-Container (Proxmox). Alle Befehle als `root` im Container.
```bash
pct create 120 local:vztmpl/ubuntu-24.04-standard_24.04-2_amd64.tar.zst \
--hostname monitor-check \
--cores 2 --memory 1024 --swap 512 \
--rootfs local-lvm:8 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--features nesting=1 \
--unprivileged 1 \
--onboot 1
pct start 120
pct enter 120
```
Ein bestehender Container tut es genauso – dann direkt bei 2.2 weiter.
### 2.2 Grundpakete
```bash
apt update && apt -y upgrade
apt -y install curl git nginx ca-certificates
# Node.js 20 LTS
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt -y install nodejs
node -v # sollte v20.x zeigen
```
### 2.3 Benutzer und Verzeichnisse
```bash
adduser --system --group --home /opt/monitor-check monitor
mkdir -p /opt/monitor-check /var/lib/monitor-check/uploads
```
### 2.4 Dateien einspielen
Ordner `server/` und `web/` aus diesem Projekt nach `/opt/monitor-check/` kopieren – z. B. vom Arbeitsrechner aus:
```bash
# auf deinem Rechner, IP anpassen
scp -r server web root@192.168.1.50:/opt/monitor-check/
```
Dann im Container:
```bash
cd /opt/monitor-check/server
npm install --omit=dev
```
`sharp` bringt fertige Binärpakete mit; sollte der Build doch starten, hilft `apt -y install build-essential python3`.
### 2.5 Konfiguration
```bash
cd /opt/monitor-check/server
cp .env.example .env
nano .env
```
Anzupassen:
```ini
PORT=8080
HOST=127.0.0.1
API_TOKEN=<dasselbe Token wie in Code.gs>
PUBLIC_URL=https://monitore.deine-domain.de
UPLOAD_DIR=/var/lib/monitor-check/uploads
WEB_DIR=/opt/monitor-check/web
ALLOWED_ORIGINS=https://monitore.deine-domain.de
```
`PUBLIC_URL` ist wichtig: genau diese Adresse landet als Bild-Link in der Tabelle.
```bash
chown -R monitor:monitor /opt/monitor-check /var/lib/monitor-check
chmod 600 /opt/monitor-check/server/.env
```
### 2.6 Als Dienst starten
```bash
cp /opt/monitor-check/server/monitor-check.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now monitor-check
systemctl status monitor-check --no-pager
curl -s localhost:8080/gesundheit # {"ok":true,…}
```
Logs mitlesen: `journalctl -u monitor-check -f`
### 2.7 nginx und HTTPS
```bash
cp /opt/monitor-check/server/nginx-monitor-check.conf /etc/nginx/sites-available/monitor-check
sed -i 's/monitore.example.org/monitore.deine-domain.de/' /etc/nginx/sites-available/monitor-check
ln -sf ../sites-available/monitor-check /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx
# Zertifikat (DNS muss vorher auf den Server zeigen)
apt -y install certbot python3-certbot-nginx
certbot --nginx -d monitore.deine-domain.de
```
**HTTPS ist Pflicht.** Ohne TLS gibt die Handy-Kamera im Browser kein Bild frei und `fetch` zu Google wird von der Seite aus blockiert.
Optional Firewall:
```bash
apt -y install ufw
ufw allow 22,80,443/tcp && ufw --force enable
```
---
## Teil 3 – Oberfläche verbinden
`https://monitore.deine-domain.de` am Handy öffnen. Beim ersten Start landest du im Tab **Setup**:
| Feld | Wert |
|---|---|
| Apps-Script Web-App URL | die `…/exec`-URL aus Schritt 1.6 |
| API-Token | dasselbe Token wie in `Code.gs` und `.env` |
| Upload-Endpoint | `https://monitore.deine-domain.de/api/upload` |
| Dein Name | wird je Prüfung als „Prüfer" gespeichert |
**Speichern**, dann **Verbindung testen** – die Meldung „Verbindung ok" bestätigt Schritt 1 und 2.
Die Werte liegen im `localStorage` des Browsers, also pro Gerät einmal eintragen. Willst du das vorbelegen, trage sie in `web/index.html` oben im Block `DEFAULTS` ein und lade die Datei neu auf den Server.
Auf dem iPhone: Teilen → **Zum Home-Bildschirm**. Danach startet das Tool wie eine App im Vollbild.
---
## Bedienung
**Erfassen** – Bezeichnung eingeben, optional Standort, aufs Fotofeld tippen (öffnet direkt die Kamera). Der Status lässt sich sofort mitsetzen oder später nachtragen. Die ID vergibt das Skript automatisch: `MON-001`, `MON-002`, …
**Übersicht** – Kacheln zeigen die Verteilung, darunter Suche und Statusfilter. Ein Tippen auf einen Eintrag öffnet die Detailansicht mit den drei Schaltern. Erneutes Tippen auf einen bereits gewählten Wert hebt die Auswahl wieder auf.
**Löschen** – In der Detailansicht ganz unten „Eintrag löschen", nach Rückfrage verschwindet die Zeile aus der Tabelle. Alternativ die Zeile direkt im Sheet löschen. Vergebene IDs werden dabei nie erneut vergeben, auch nicht die des zuletzt angelegten Eintrags – der Zähler steht in den Skript-Eigenschaften. Das zugehörige Foto bleibt auf dem Server liegen; siehe „Wartung" zum Aufräumen.
**Offline** – Bei Verbindungsabbruch wandern Neuanlagen und Statusmeldungen in eine lokale Warteschlange (Anzeige oben rechts) und gehen automatisch raus, sobald das Netz zurück ist. Manuell auslösen im Setup-Tab. Fotos brauchen dagegen eine Verbindung – ohne Netz den Eintrag ohne Bild anlegen und das Foto später über die Tabelle nachtragen.
---
## Tabellenstruktur
Blatt `Monitore`:
| Spalte | Inhalt |
|---|---|
| A ID | `MON-001` fortlaufend |
| B Bezeichnung | Freitext |
| C Standort | Freitext |
| D Bild-URL | Link auf dein `/uploads/…` |
| E Audio / F Bild / G Sprache | `JA` / `NEIN` / `n.A.` |
| H Status | automatisch berechnet, Zeile wird eingefärbt |
| I Notiz | Freitext |
| J Erfasst am, K Geprüft am, L Geprüft von | Zeitstempel und Person |
Blatt `Protokoll` schreibt jede Änderung mit – Zeitpunkt, ID, Aktion, Status, Person.
Direkt in der Tabelle bearbeitete Werte werden nicht automatisch neu bewertet. Dafür gibt es **Monitor-Check → Alle Status neu berechnen**.
Bilder als Vorschau in der Tabelle: eine leere Spalte ergänzen und dort
`=BILD(D2)` eintragen.
---
## Wartung
```bash
# Dienst neu starten (nach Änderungen an server.js oder .env)
systemctl restart monitor-check
# Oberfläche und Server aktualisieren – neue Dateien hochladen, dann
sudo bash install.sh --update
# Browser-Cache: Seite mit gedrücktem Reload neu laden
# Platzbedarf der Fotos
du -sh /var/lib/monitor-check/uploads
# Sicherung der Bilder
tar czf uploads-$(date +%F).tar.gz -C /var/lib/monitor-check uploads
```
Verwaiste Fotos gelöschter Einträge aufräumen: Spalte D des Sheets als CSV exportieren, auf den Server legen und abgleichen.
```bash
# behaelt nur Dateien, die noch in der Tabelle verlinkt sind
cd /var/lib/monitor-check/uploads
grep -o '[^/]*\.jpg' /root/bild-urls.csv | sort -u > /tmp/benutzt.txt
ls *.jpg | sort > /tmp/vorhanden.txt
comm -23 /tmp/vorhanden.txt /tmp/benutzt.txt # erst anschauen
comm -23 /tmp/vorhanden.txt /tmp/benutzt.txt | xargs -r rm -- # dann loeschen
```
Bilder werden auf max. 1600 px verkleinert und als JPEG (Qualität 78) gespeichert – rund 150–400 kB pro Foto. 1000 Einträge belegen also etwa 300 MB. Anpassbar über `MAX_IMAGE_PX` und `JPEG_QUALITY` in der `.env`.
## Fehlersuche
| Symptom | Ursache |
|---|---|
| „Unerwartete Antwort – Web-App-Zugriff auf ‚Jeder' gesetzt?" | Bereitstellung steht auf „Nur ich". In den Bereitstellungen auf **Jeder** ändern. |
| „Nicht autorisiert" | Token stimmt nicht überein. Muss in `Code.gs`, `.env` und im Setup-Tab identisch sein. |
| Änderungen an `Code.gs` wirken nicht | Neue **Version** bereitstellen, nicht nur speichern. |
| Kamera öffnet nicht | Seite läuft über `http` statt `https`. |
| Upload bricht bei großen Fotos ab | `client_max_body_size` in **jedem** nginx davor und `MAX_UPLOAD_MB` in der `.env` erhöhen. |
| Bilder erscheinen nicht, Konsole meldet Mixed Content | `PUBLIC_URL` steht auf `http://`, die Seite läuft über `https://`. In der `.env` korrigieren, Dienst neu starten. Bereits gespeicherte Links im Sheet per Suchen-und-Ersetzen anpassen. |
| Bild-Link im Sheet zeigt `127.0.0.1` | `PUBLIC_URL` in der `.env` setzen und Dienst neu starten. |
| Nichts geht mehr | `journalctl -u monitor-check -n 50 --no-pager` |
## Sicherheit
Das Token schützt die Schnittstelle gegen zufällige Zugriffe, ist aber kein Login – wer die URL und das Token hat, kann schreiben. Für eine interne Veranstaltung reicht das. Soll es enger sein, lege in nginx zusätzlich eine Basic-Auth über `location /` (nicht über `/api/upload`, sonst scheitert der Upload aus der Seite heraus) oder beschränke den Vhost auf das Veranstaltungs-Netz per `allow`/`deny`.
---
## Dateien
| Datei | Zweck |
|---|---|
| `install.sh` | richtet den Server komplett ein (Ubuntu 22.04/24.04) |
| `apps-script/Code.gs` | Backend an der Google Tabelle, JSON-API, Statusberechnung |
| `web/index.html` | die komplette Oberfläche in einer Datei |
| `server/server.js` | Foto-Upload und Auslieferung der Oberfläche |
| `server/.env.example` | Konfigurationsvorlage, wird zur `.env` (nicht im Repo) |
| `server/monitor-check.service` | systemd-Unit |
| `server/nginx-monitor-check.conf` | nginx-Vhost mit TLS-Vorbereitung |

Powered by TurnKey Linux.