|
|
# 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 |
|