From 135625ebe7fb319eefd570ca2e6f106f5a657897 Mon Sep 17 00:00:00 2001 From: marc Date: Sat, 29 Aug 2026 14:24:58 +0200 Subject: [PATCH] feat(beispiel): mehrere Apps ueber einen Einsetzer, je mit eigener Kennung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Am Host erkennt nginx, welcher Dienst gemeint ist und welche App das fuer HoneyOS ist; die Leiste hebt sie dann hervor wie in Aether. Dieselbe Bauart wie im BentoPDF-Beispiel: Leiste und ihre zwei Abfragen laufen ueber die Adresse der App, damit deren CSP unangetastet bleibt. Die beiden .conf-Dateien sind weg — sie standen ein zweites Mal in der Compose-Datei, und zwei Fassungen derselben Sache laufen auseinander. Heute schon zweimal passiert. Geprueft mit zwei Diensten: richtige Kennung je Host, richtige Hervorhebung in der Leiste, gemeinsame App-Liste, Wetter. Co-Authored-By: Claude Opus 5 --- README.md | 9 +- beispiele/mehrere-apps/docker-compose.yml | 147 ++++++++++------------ beispiele/mehrere-apps/leiste.conf | 73 ----------- beispiele/mehrere-apps/upgrade.conf | 6 - 4 files changed, 75 insertions(+), 160 deletions(-) delete mode 100644 beispiele/mehrere-apps/leiste.conf delete mode 100644 beispiele/mehrere-apps/upgrade.conf diff --git a/README.md b/README.md index 103b66b..bc2cf61 100644 --- a/README.md +++ b/README.md @@ -256,11 +256,14 @@ Adresse der App. strenge CSP und COEP mitbringt: Leiste, Apps und Wetter erscheinen, ohne dass an dessen Kopfzeilen etwas geändert wird. - [`beispiele/mehrere-apps/`](beispiele/mehrere-apps/) — **HoneyOS und ein - Einsetzer für alle Dienste in einem Stapel.** Am Host erkennt nginx, welcher + Einsetzer für beliebig viele Dienste in einem Stapel.** Zwei Tabellen oben in + der Datei sagen, welcher Name zu welchem Behälter gehört und welche App das + für HoneyOS ist; eine neue App sind zwei Zeilen. Die Leiste hebt die aktuelle + App hervor, sobald `$appid` der `id` in der App-Liste entspricht — genau wie + in Aether und InkNote. Am Host erkennt nginx, welcher Dienst gemeint ist; eine neue App sind zwei Zeilen in den Tabellen oben in `leiste.conf`. Das ist der Aufbau, den man haben will, sobald es mehr als ein - Werkzeug ist — nicht ein Behälter je App, und nicht HoneyOS getrennt davon - gepflegt. + Nicht ein Behälter je App, und nicht HoneyOS getrennt davon gepflegt. Das Netz `honeybar` legt dieser Stapel selbst an, unter festem Namen. Die fremden Dienste treten ihm bei (`networks: [default, honeybar]` und diff --git a/beispiele/mehrere-apps/docker-compose.yml b/beispiele/mehrere-apps/docker-compose.yml index 7505fef..eba4c31 100644 --- a/beispiele/mehrere-apps/docker-compose.yml +++ b/beispiele/mehrere-apps/docker-compose.yml @@ -1,29 +1,24 @@ # ───────────────────────────────────────────────────────────────────────────── -# HoneyOS und der Einsetzer in einem Stapel. +# HoneyOS und ein Einsetzer für beliebig viele fremde Dienste. # -# Pangolin → honeyos (der Schreibtisch selbst) -# Pangolin → leiste → bentopdf, kuma, … (fremde Dienste mit Leiste) +# außen ──► Pangolin ──► honeyos (der Schreibtisch) +# Pangolin ──► leiste ──► bentopdf:8080 (App mit Leiste) +# │ ──► uptime-kuma:3001 +# └──► honeyos:8080 (Leiste + zwei Abfragen) # -# Beides gehört zusammen: Die Leiste kommt von HoneyOS, der Einsetzer trägt sie -# in Werkzeuge, die man nicht ändern kann. Getrennt zu pflegen hiesse, bei jeder -# neuen App an zwei Stellen zu denken. +# Die fremden Dienste brauchen kein `ports:` mehr — erreichbar sind sie über +# ihren Namen im Netz `honeybar`, und nach außen führt nur der Einsetzer. # -# cp ../../.env.example .env && ausfüllen +# Eine neue App hinzufügen heißt: zwei Zeilen in den Tabellen `$ziel` und +# `$appid`, dann `docker compose up -d --force-recreate leiste`. +# +# cp ../../.env.example .env && ausfüllen (HONEYOS_APPS_KEY setzen!) # mkdir -p daten && sudo chown 1000:1000 daten # docker compose up -d # -# Das Netz `honeybar` legt dieser Stapel selbst an — die fremden Dienste treten -# ihm bei. In deren Compose-Datei: -# -# services: -# bentopdf: -# networks: [default, honeybar] # ports: kann dann ganz weg -# networks: -# honeybar: -# external: true -# -# Diesen Stapel deshalb zuerst starten; danach die anderen. -# In Pangolin zeigt honeyos.… auf honeyos:8080 und jede App auf leiste:80. +# Danach in Pangolin: honeyos.… → honeyos:8080, jede App → leiste:80. +# Und in HoneyOS jede App eintragen — die `id` dort muss dem `$appid` hier +# entsprechen, sonst weiß die Leiste nicht, in welcher App man gerade ist. # ───────────────────────────────────────────────────────────────────────────── services: @@ -33,8 +28,7 @@ services: restart: unless-stopped user: "1000:1000" ports: - # Nur an localhost: davor steht Pangolin, nicht das offene Netz. - - "127.0.0.1:${HONEYOS_PORT:-8088}:8080" + - "${HONEYOS_PORT:-8088}:8080" volumes: - ${HONEYOS_DATA_DIR:-./daten}:/data environment: @@ -55,73 +49,75 @@ services: image: nginx:alpine container_name: honeybar-leiste restart: unless-stopped - # Die beiden nginx-Dateien stehen als `configs` unten **in dieser Datei**. - # - # Mit Bind-Mounts (`./leiste.conf:/etc/nginx/…`) müssten sie auf dem Wirt - # liegen — und fehlt eine, legt Docker dort wortlos ein *Verzeichnis* an - # und scheitert beim nächsten Start mit „not a directory". Beim Einsetzen - # über eine Oberfläche, wo man nur die Compose-Datei bearbeitet, ist das - # eine Falle ohne Nutzen. + ports: + - "${LEISTE_PORT:-8185}:80" configs: - - source: upgrade - target: /etc/nginx/conf.d/00-upgrade.conf - source: leiste target: /etc/nginx/conf.d/default.conf - ports: - - "127.0.0.1:${LEISTE_PORT:-8185}:80" networks: [honeybar] - # Der Einsetzer braucht HoneyOS nicht zum Starten — er reicht nur durch und - # setzt eine Zeile ein. Ist HoneyOS aus, fehlt die Leiste, die App läuft. + # Kein depends_on: Der Einsetzer reicht nur durch. Ist HoneyOS aus, fehlt + # die Leiste — die Apps laufen weiter. configs: - # Achtung beim Ändern: `$$` ist hier ein einfaches `$` für nginx. Ein - # einzelnes `$` würde Compose als eigene Variable lesen und leeren. - upgrade: + leiste: + # `$$` ist hier ein einfaches `$` für nginx. Ein einzelnes `$` setzt + # dagegen Compose ein — genau das ist bei ${HONEYOS_APPS_KEY} gewollt: der + # Schlüssel steht damit nur in der nginx-Konfiguration, nie in der Seite. content: | - # Gehört nach /etc/nginx/conf.d/, aber ausserhalb eines server-Blocks: - # „Connection: upgrade" nur dann setzen, wenn der Browser das auch will. + # ── Welcher Dienst steckt hinter welchem Namen ──────────────────────── + # Links die Adresse, unter der du die App aufrufst; rechts Behältername + # und Port im Docker-Netz. + map $$host $$ziel { + default ""; + pdf.honeyos.duckdns.org "bentopdf:8080"; + status.honeyos.duckdns.org "uptime-kuma:3001"; + } + + # ── Und welche App das für HoneyOS ist ──────────────────────────────── + # Muss der `id` in der App-Liste entsprechen. Daran erkennt die Leiste, + # wo man gerade ist, und hebt den Eintrag hervor — wie in Aether. + map $$host $$appid { + default ""; + pdf.honeyos.duckdns.org "bentopdf"; + status.honeyos.duckdns.org "status"; + } + map $$http_upgrade $$connection_upgrade { default upgrade; '' close; } - leiste: - content: | - # ───────────────────────────────────────────────────────────────────────────── - # Ein Einsetzer für alle Dienste, die die Leiste tragen sollen. - # - # Pangolin gibt den ursprünglichen Host weiter; daran erkennt nginx, welcher - # Dienst gemeint ist. Eine neue App heisst: zwei Zeilen in den Tabellen unten, - # neu laden, fertig — kein weiterer Behälter. - # ───────────────────────────────────────────────────────────────────────────── - - # Wohin durchgereicht wird. Links der Name, unter dem du die App aufrufst, - # rechts Behältername und Port im Docker-Netz. - map $$host $$ziel { - default ""; - pdf.honeyos.duckdns.org "bentopdf:8080"; - status.honeyos.duckdns.org "uptime-kuma:3001"; - } - - # Welche App das für HoneyOS ist. Muss der id in der App-Liste entsprechen — - # daran erkennt die Leiste, wo man gerade ist, und hebt sie hervor. - map $$host $$appid { - default ""; - pdf.honeyos.duckdns.org "bentopdf"; - status.honeyos.duckdns.org "status"; - } server { listen 80; server_name _; - - # Für ein proxy_pass mit Variable braucht nginx einen Namensauflöser — - # im Docker-Netz ist das der eingebaute unter 127.0.0.11. resolver 127.0.0.11 valid=30s ipv6=off; - # Unbekannter Host: nicht raten, sondern ablehnen. Sonst wird aus einem - # Tippfehler in Pangolin eine stille Weiterleitung irgendwohin. + # Unbekannter Name: nicht raten. Sonst wird aus einem Tippfehler in + # Pangolin eine stille Weiterleitung irgendwohin. if ($$ziel = "") { return 404; } + # ── Die Leiste, durchgereicht über DIESE Adresse ────────────────── + # Dadurch ist sie für den Browser eigener Ursprung: Die CSP der App + # bleibt unangetastet, CORS und COEP entfallen. + location /honeybar/ { + proxy_pass http://honeyos:8080/honeybar/; + } + + # Ihre zwei Abfragen — einzeln benannt und nur lesend. Ein pauschales + # /api/ hinge die ganze HoneyOS-Schnittstelle unter die Adresse der + # App. Den Ausweis setzt der Server ein. + location = /honeyos-api/apps { + limit_except GET { deny all; } + proxy_pass http://honeyos:8080/api/apps; + proxy_set_header X-HoneyOS-Key "${HONEYOS_APPS_KEY}"; + } + location = /honeyos-api/weather { + limit_except GET { deny all; } + proxy_pass http://honeyos:8080/api/weather; + proxy_set_header X-HoneyOS-Key "${HONEYOS_APPS_KEY}"; + } + + # ── Die App selbst ─────────────────────────────────────────────── location / { proxy_pass http://$$ziel; proxy_set_header Host $$host; @@ -129,22 +125,18 @@ configs: proxy_set_header X-Forwarded-Proto $$scheme; proxy_set_header X-Real-IP $$remote_addr; - # WebSockets durchlassen — viele Werkzeuge (Uptime Kuma, Dashboards, - # alles mit Live-Anzeige) brechen sonst ab. + # WebSockets durchlassen — Live-Anzeigen brechen sonst ab. proxy_http_version 1.1; proxy_set_header Upgrade $$http_upgrade; proxy_set_header Connection $$connection_upgrade; - # Unkomprimiert anfordern, sonst ist im Strom nichts zu ersetzen. + # Unkomprimiert anfordern, sonst ist nichts zu ersetzen. proxy_set_header Accept-Encoding ""; - # Nur HTML anfassen; alles andere geht unberührt durch. - sub_filter_types text/html; sub_filter_once on; sub_filter '' - ''; + ''; - # Lange Antworten nicht künstlich aufhalten (Downloads, Fortschritt). proxy_buffering off; proxy_read_timeout 300s; } @@ -152,6 +144,5 @@ configs: networks: honeybar: - # Fester Name, damit andere Compose-Dateien ihn als `external` finden. - # Ohne den hiesse das Netz nach dem Ordner, in dem diese Datei liegt. + # Fester Name, damit die App-Stapel ihn als `external` finden. name: honeybar diff --git a/beispiele/mehrere-apps/leiste.conf b/beispiele/mehrere-apps/leiste.conf deleted file mode 100644 index 5fc96c6..0000000 --- a/beispiele/mehrere-apps/leiste.conf +++ /dev/null @@ -1,73 +0,0 @@ -# ───────────────────────────────────────────────────────────────────────────── -# HINWEIS: Diese Datei ist die lesbare Fassung zum Nachschlagen. Verwendet wird -# derselbe Inhalt aus `docker-compose.yml` (Abschnitt `configs:`) — dort steht -# er noch einmal, mit `$$` statt `$`. Wer hier etwas ändert, ändert es dort. -# -# Der Grund für den Umweg: Bind-Mounts einzelner Dateien scheitern, sobald die -# Datei auf dem Wirt fehlt — Docker legt dann ein Verzeichnis an und bricht mit -# „not a directory" ab. Beim Einsetzen über eine Oberfläche will man ausserdem -# nur eine Datei bearbeiten, nicht drei. -# ───────────────────────────────────────────────────────────────────────────── - -# ───────────────────────────────────────────────────────────────────────────── -# Ein Einsetzer für alle Dienste, die die Leiste tragen sollen. -# -# Pangolin gibt den ursprünglichen Host weiter; daran erkennt nginx, welcher -# Dienst gemeint ist. Eine neue App heisst: zwei Zeilen in den Tabellen unten, -# neu laden, fertig — kein weiterer Behälter. -# ───────────────────────────────────────────────────────────────────────────── - -# Wohin durchgereicht wird. Links der Name, unter dem du die App aufrufst, -# rechts Behältername und Port im Docker-Netz. -map $host $ziel { - default ""; - pdf.honeyos.duckdns.org "bentopdf:8080"; - status.honeyos.duckdns.org "uptime-kuma:3001"; -} - -# Welche App das für HoneyOS ist. Muss der id in der App-Liste entsprechen — -# daran erkennt die Leiste, wo man gerade ist, und hebt sie hervor. -map $host $appid { - default ""; - pdf.honeyos.duckdns.org "bentopdf"; - status.honeyos.duckdns.org "status"; -} - -server { - listen 80; - server_name _; - - # Für ein proxy_pass mit Variable braucht nginx einen Namensauflöser — - # im Docker-Netz ist das der eingebaute unter 127.0.0.11. - resolver 127.0.0.11 valid=30s ipv6=off; - - # Unbekannter Host: nicht raten, sondern ablehnen. Sonst wird aus einem - # Tippfehler in Pangolin eine stille Weiterleitung irgendwohin. - if ($ziel = "") { return 404; } - - location / { - proxy_pass http://$ziel; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Real-IP $remote_addr; - - # WebSockets durchlassen — viele Werkzeuge (Uptime Kuma, Dashboards, - # alles mit Live-Anzeige) brechen sonst ab. - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - - # Unkomprimiert anfordern, sonst ist im Strom nichts zu ersetzen. - proxy_set_header Accept-Encoding ""; - - # Nur HTML anfassen; alles andere geht unberührt durch. - sub_filter_once on; - sub_filter '' - ''; - - # Lange Antworten nicht künstlich aufhalten (Downloads, Fortschritt). - proxy_buffering off; - proxy_read_timeout 300s; - } -} diff --git a/beispiele/mehrere-apps/upgrade.conf b/beispiele/mehrere-apps/upgrade.conf deleted file mode 100644 index f48caf2..0000000 --- a/beispiele/mehrere-apps/upgrade.conf +++ /dev/null @@ -1,6 +0,0 @@ -# Gehört nach /etc/nginx/conf.d/, aber ausserhalb eines server-Blocks: -# „Connection: upgrade" nur dann setzen, wenn der Browser das auch will. -map $http_upgrade $connection_upgrade { - default upgrade; - '' close; -}