|
|
1 week ago | |
|---|---|---|
| apps-script | 1 week ago | |
| server | 1 week ago | |
| web | 1 week ago | |
| .gitignore | 1 week ago | |
| README.md | 1 week ago | |
| install.sh | 1 week ago | |
README.md
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
- Google Tabelle anlegen und das Apps Script einfügen (Teil 1)
- Server mit
install.shaufsetzen (Teil 2) - Am Handy die Seite öffnen und im Setup-Tab URL und Token eintragen (Teil 3)
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
- Neue Tabelle auf sheets.new anlegen, z. B. „Monitore Veranstaltung 2026".
- Erweiterungen → Apps Script. Den Standardinhalt von
Code.gslöschen und den Inhalt ausapps-script/Code.gseinfügen. - Ganz oben
CONFIG.API_TOKENdurch ein langes Zufallstoken ersetzen. Erzeugen z. B. mit:
Dieses Token brauchst du gleich noch zweimal (Server und Oberfläche).openssl rand -hex 24 - Speichern. Im Editor die Funktion
testStatusLogikauswählen und Ausführen – beim ersten Mal Zugriff freigeben. Im Ausführungsprotokoll muss „Alle 9 Testfälle bestanden" stehen. - Zurück in der Tabelle: Seite neu laden, dann Menü Monitor-Check → Tabelle einrichten. Damit entstehen die Blätter
MonitoreundProtokollsamt Spaltenbreiten und Dropdowns. - 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.
- Beschreibung:
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.
# 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:
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:
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:
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:
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.
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
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
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:
# auf deinem Rechner, IP anpassen
scp -r server web root@192.168.1.50:/opt/monitor-check/
Dann im Container:
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
cd /opt/monitor-check/server
cp .env.example .env
nano .env
Anzupassen:
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.
chown -R monitor:monitor /opt/monitor-check /var/lib/monitor-check
chmod 600 /opt/monitor-check/server/.env
2.6 Als Dienst starten
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
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:
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
# 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.
# 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 |