diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..83a9a2c --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "portal-static", + "runtimeExecutable": "python", + "runtimeArgs": ["-m", "http.server", "8099", "--directory", "public"], + "port": 8099 + } + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..fe446d0 --- /dev/null +++ b/README.md @@ -0,0 +1,352 @@ +# appServer Portal UI + +Übersichts-Webserver für **`server.schooltech.ch`**. Er fasst eine Reihe von +Web-Diensten (Roboter-Steuerungen, Guacamole-Remotedesktops, Portainer, +VS Code, Nextcloud …) unter **einer Domain** zusammen und macht sie über +sprechende Subdomains erreichbar. + +Alle Dienste laufen als Subdomain von `.server.schooltech.ch` – egal ob sie +auf dem Server selbst, auf einem Gerät im LAN oder hinter einem SSH-Tunnel +stehen. Ein nginx-Reverse-Proxy nimmt jede Subdomain auf 443 entgegen, +terminiert TLS (Let's Encrypt) und leitet an den passenden Upstream weiter. + +--- + +## Inhalt + +- [Architektur-Übersicht](#architektur-übersicht) +- [Die Portal-Seite (`server.schooltech.ch`)](#die-portal-seite-serverschooltechch) +- [Die Dienste / Subdomains](#die-dienste--subdomains) +- [`forwarding.conf` – das Herzstück](#forwardingconf--das-herzstück) +- [`connect-proxies.sh` – Konfig-Generator](#connect-proxiessh--konfig-generator) +- [Authentifizierung](#authentifizierung) +- [TLS / Let's Encrypt](#tls--lets-encrypt) +- [Container & Betrieb](#container--betrieb) +- [Neuen Dienst hinzufügen](#neuen-dienst-hinzufügen) +- [Dateiübersicht](#dateiübersicht) + +--- + +## Architektur-Übersicht + +Alles hängt unter `*.server.schooltech.ch`. Der Unterschied zwischen den +Diensten ist nicht die Domain, sondern **wohin der Upstream zeigt**: + +![Architektur-Übersicht](doc/Architektur.svg) + +> Diagramm-Quelle: [`doc/Architektur.svg`](doc/Architektur.svg) (daraus lässt +> sich bei Bedarf eine PDF/PNG erzeugen). + +**Bausteine (Docker-Container, siehe `docker-compose.yaml`):** + +| Container | Image | Rolle | +|---|---|---| +| `appServer_PortalUI` | `nginx:alpine` | Reverse-Proxy + Auslieferung der Portal-Seite (Ports 80/443) | +| `AppServerAuth` | `node:24-alpine` | Login-/Session-Service (`auth/auth.js`, Port 3000 intern) | +| `appServer_TunnelHead` | `linuxserver/openssh-server` | SSH-Hub: entfernte Geräte bauen Reverse-Tunnel hierher auf | +| `appServer_guacamole` | `abesnier/guacamole` | Lokaler Guacamole-Remotedesktop | +| `appServer_LetsEncryptFetcher` | `certbot/certbot` | Holt/erneuert die TLS-Zertifikate | + +Alle Container hängen im Docker-Netzwerk **`appRobotNet`** und sprechen sich +per Container-Namen an (z. B. `appserverauth:3000`). + +--- + +## Die Portal-Seite (`server.schooltech.ch`) + +Ruft man die nackte Domain `server.schooltech.ch` auf, erscheint die +**Portal-Oberfläche** – eine schlanke Single-Page-App aus `public/`: + +- **`public/index.html`** – Grundgerüst: oben eine **Navigationsleiste** + (`
` mit Logo *„schooltech“*, der Service-Navigation `#services` + und einem Login/Logout-Button), darunter ein **`