Frontend-API-URL ueber MONITOR_API_BASE konfigurieren statt aus dem Port raten
Die Basis-URL der API wurde bisher aus dem Port der Seite abgeleitet (office.html: `if (location.port === "8090")`). Laeuft das Frontend hinter anderen Ports - in Produktion 4443 (web) / 4444 (api) - griff die Bedingung nicht, der relative Default /api/v1 lief gegen den Webserver ins Leere, und die Szene fiel still auf Demo-Daten zurueck: fremde Agenten-Namen und erfundene Ticketzahlen bei gleichzeitig leeren Detail-Modals. Die URL kommt jetzt aus MONITOR_API_BASE (.env, Default passend zum Port-Mapping in docker-compose.yml). Der Webserver liefert den Wert zur Laufzeit als /config.js aus; das nginx-Image setzt ihn beim Start per envsubst in die Config ein (default.conf -> default.conf.template). Damit bleibt html/ ein rein statisches, read-only gemountetes Verzeichnis - kein Build-Schritt, kein eigenes Entrypoint-Skript. Ein leerer Wert bedeutet "gleicher Origin" und faellt auf /api/v1 zurueck. Zusaetzlich macht ein roter Banner "API nicht erreichbar!" den Demo-Fallback sichtbar, statt ihn wie bisher stillschweigend als echte Daten auszugeben. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
9f1fa520d7
commit
ba52522415
10 changed files with 113 additions and 31 deletions
|
|
@ -3,6 +3,14 @@
|
|||
# Administrator-Key zur Registrierung neuer Agenten (POST /api/v1/agents, Header X-Admin-Key)
|
||||
MONITOR_ADMIN_API_KEY=change-me
|
||||
|
||||
# Basis-URL, unter der das Frontend (Browser!) die REST-API erreicht. Der Default passt zum
|
||||
# Port-Mapping des api-Service in docker-compose.yml (8091). Wer die Ports dort aendert oder
|
||||
# das System hinter einem Reverse Proxy betreibt, muss diesen Wert entsprechend nachziehen -
|
||||
# z.B. https://monitor.example.org:4444/api/v1.
|
||||
# Leer lassen, wenn Frontend und API unter demselben Origin ausgeliefert werden (dann wird
|
||||
# die relative URL /api/v1 verwendet).
|
||||
MONITOR_API_BASE=http://localhost:8091/api/v1
|
||||
|
||||
# Codeberg-Repo, aus dem Backlog/Done per Sync-Skript befuellt werden
|
||||
CODEBERG_OWNER=fds2610
|
||||
CODEBERG_REPO=monitor
|
||||
|
|
|
|||
5
API.md
5
API.md
|
|
@ -33,6 +33,11 @@ Implementierung: Vanilla-PHP (kein Framework), Persistenz in SQLite
|
|||
- Alle Antworten tragen `Access-Control-Allow-Origin: *` (CORS), da Frontend und API im
|
||||
Docker-Compose-Setup auf unterschiedlichen Ports laufen (siehe
|
||||
[docker-compose.yml](docker-compose.yml): Frontend `:8090`, API `:8091`).
|
||||
- Welche Basis-URL das Frontend anspricht, wird nicht geraten, sondern konfiguriert: Der
|
||||
Webserver liefert sie zur Laufzeit als `/config.js`
|
||||
(`window.MONITOR_API_BASE`) aus, gespeist aus der Umgebungsvariable `MONITOR_API_BASE`
|
||||
(siehe `.env.example` und `docker/nginx/default.conf.template`). Leerer Wert = gleicher
|
||||
Origin, dann wird der relative Pfad `/api/v1` verwendet.
|
||||
|
||||
## Authentifizierung
|
||||
|
||||
|
|
|
|||
11
README.md
11
README.md
|
|
@ -19,7 +19,8 @@ die dazugehörigen Klärungen in [Detailfragen.md](Detailfragen.md) und
|
|||
Server-Sent Events, Polling-Fallback), Klick auf Board/Monitore öffnet Detail-Modals,
|
||||
Agenten laufen animiert zwischen Schreibtisch/Board/Monitor-Wand/Kaffee-Ecke (Phase 3-5,
|
||||
siehe Visualization.md Abschnitt 9). Ist die API nicht erreichbar, zeigt die Szene
|
||||
ersatzweise Demo-Daten.
|
||||
ersatzweise Demo-Daten — kenntlich gemacht durch einen roten Banner „API nicht erreichbar!".
|
||||
Die Basis-URL der API kommt aus `MONITOR_API_BASE` (siehe `.env.example`).
|
||||
- Weiterhin: klassische Ping-Überwachung (`check_fds.sh`, `hosts.conf`, `mail_fds.py`) als
|
||||
eine Datenquelle für den System-Monitor; die alte tabellarische Statusseite ist mit
|
||||
Phase 7 entfallen (F16), `status.txt` speist weiterhin den System-Monitor
|
||||
|
|
@ -53,6 +54,14 @@ docker compose run --rm api composer test # PHPUnit (API)
|
|||
docker compose run --rm frontend # Node-Tests (Frontend-Layoutlogik)
|
||||
```
|
||||
|
||||
Der Browser erreicht die API nicht über das Compose-Netzwerk, sondern über die in
|
||||
`MONITOR_API_BASE` (`.env`) hinterlegte URL — der Default `http://localhost:8091/api/v1` passt
|
||||
zum Port-Mapping oben. **Wer die Ports in `docker-compose.yml` ändert oder das System hinter
|
||||
einem Reverse Proxy betreibt, muss `MONITOR_API_BASE` entsprechend nachziehen**, sonst zeigt
|
||||
die Szene den Banner „API nicht erreichbar!" und fällt auf Demo-Daten zurück. Der Webserver
|
||||
liefert den Wert zur Laufzeit als `/config.js` aus (`docker/nginx/default.conf.template`), ein
|
||||
Build-Schritt ist dafür nicht nötig.
|
||||
|
||||
Details zur API: [api/README.md](api/README.md). Details zu den Endpunkten: [API.md](API.md).
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ services:
|
|||
# Optional: echte status.txt aus der Produktion zum Testen einbinden, z.B.
|
||||
# - ./status.txt:/status.txt:ro
|
||||
working_dir: /app
|
||||
# Achtung: Eine Aenderung des Host-Ports muss in .env (MONITOR_API_BASE) nachgezogen
|
||||
# werden - das Frontend laeuft im Browser und erreicht die API nicht ueber das
|
||||
# Compose-Netzwerk, sondern ueber genau diesen Port.
|
||||
ports:
|
||||
- "8091:8080"
|
||||
environment:
|
||||
|
|
@ -31,8 +34,14 @@ services:
|
|||
# Ueberlagert den Platzhalter html/API.md - der muss existieren, weil in
|
||||
# einem read-only gemounteten Verzeichnis kein Mountpoint angelegt werden kann.
|
||||
- ./API.md:/usr/share/nginx/html/API.md:ro
|
||||
# Setzt office.html als Einstiegsseite (sonst 403 auf der Basis-URL).
|
||||
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
# Setzt office.html als Einstiegsseite (sonst 403 auf der Basis-URL) und liefert
|
||||
# /config.js aus. Als Template gemountet, damit das nginx-Image beim Start
|
||||
# MONITOR_API_BASE per envsubst einsetzt (Ausgabe: /etc/nginx/conf.d/default.conf).
|
||||
- ./docker/nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro
|
||||
environment:
|
||||
# Wird in docker/nginx/default.conf.template eingesetzt und als /config.js an den
|
||||
# Browser ausgeliefert. Leer = gleicher Origin (relatives /api/v1).
|
||||
MONITOR_API_BASE: ${MONITOR_API_BASE:-}
|
||||
ports:
|
||||
- "8090:80"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
# Einstiegsseite ist die Buero-Visualisierung - ohne diese Direktive liefert
|
||||
# nginx beim Aufruf der Basis-URL ein 403 (Directory-Listing ist deaktiviert,
|
||||
# und eine index.html gibt es nicht).
|
||||
index office.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
}
|
||||
29
docker/nginx/default.conf.template
Normal file
29
docker/nginx/default.conf.template
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
# Einstiegsseite ist die Buero-Visualisierung - ohne diese Direktive liefert
|
||||
# nginx beim Aufruf der Basis-URL ein 403 (Directory-Listing ist deaktiviert,
|
||||
# und eine index.html gibt es nicht).
|
||||
index office.html;
|
||||
|
||||
# Laufzeit-Konfiguration fuers Frontend: Das html/-Verzeichnis ist read-only gemountet,
|
||||
# deshalb wird die API-Basis-URL nicht als Datei abgelegt, sondern hier aus der Umgebung
|
||||
# ausgeliefert (MONITOR_API_BASE, siehe .env). Das nginx-Image ersetzt ${MONITOR_API_BASE}
|
||||
# beim Containerstart per envsubst - nur Variablen, die tatsaechlich in der Umgebung
|
||||
# definiert sind, nginx-eigene wie $uri bleiben unangetastet. Ist MONITOR_API_BASE gar
|
||||
# nicht definiert, bleibt der Platzhalter stehen und nginx verweigert den Start
|
||||
# ("unknown \"monitor_api_base\" variable") - besser ein lauter Fehler als eine still
|
||||
# falsche URL. docker-compose.yml setzt die Variable daher immer (ggf. leer).
|
||||
location = /config.js {
|
||||
default_type application/javascript;
|
||||
add_header Cache-Control "no-store";
|
||||
return 200 'window.MONITOR_API_BASE = "${MONITOR_API_BASE}";';
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
}
|
||||
|
|
@ -66,6 +66,24 @@ header.office-header p {
|
|||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Warnbanner, wenn die API nicht erreichbar ist und die Szene auf Demo-Daten zurueckfaellt
|
||||
(siehe setApiBanner() in js/office-render.js). */
|
||||
.api-banner {
|
||||
flex: 0 0 auto;
|
||||
padding: 10px 24px;
|
||||
background: #dc2626;
|
||||
color: #ffffff;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
/* Muss nach der Regel oben stehen: ohne diese Zeile wuerde das hidden-Attribut vom
|
||||
flex-Wert ueberstimmt und der Banner waere dauerhaft sichtbar. */
|
||||
.api-banner[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#office-scene {
|
||||
flex: 1 1 auto;
|
||||
/* min-height: 0 hebt die Flex-Default-Mindesthoehe (min-content) auf - sonst kann
|
||||
|
|
|
|||
|
|
@ -1,13 +1,23 @@
|
|||
// Duenne fetch()-Wrapper fuer die Phase-1-REST-API (siehe API.md). Reine Daten-Ebene, kein
|
||||
// DOM-Zugriff - office-render.js ruft diese Funktionen auf und reicht die Ergebnisse an die
|
||||
// Layoutlogik (office-scene.js) weiter. Basis-URL ist per window.MONITOR_API_BASE
|
||||
// ueberschreibbar (z.B. wenn Frontend und API auf unterschiedlichen Ports laufen, siehe
|
||||
// docker-compose.yml: web auf :8090, api auf :8091).
|
||||
// ueberschreibbar - gesetzt wird sie zur Laufzeit von /config.js, das der Webserver aus der
|
||||
// Umgebungsvariable MONITOR_API_BASE erzeugt (siehe .env.example und
|
||||
// docker/nginx/default.conf.template). Noetig, weil Frontend und API auf unterschiedlichen
|
||||
// Origins laufen koennen (Dev: web :8090, api :8091); die API liefert dafuer CORS-Header,
|
||||
// siehe api/src/Http/Response.php.
|
||||
|
||||
const DEFAULT_API_BASE = "/api/v1";
|
||||
|
||||
function apiBase() {
|
||||
return (typeof window !== "undefined" && window.MONITOR_API_BASE) || DEFAULT_API_BASE;
|
||||
const configured = typeof window !== "undefined" ? window.MONITOR_API_BASE : null;
|
||||
// Leer = gleicher Origin. Der startsWith-Check faengt einen nicht ersetzten Platzhalter
|
||||
// ab (z.B. wenn config.js von einem anderen Webserver ohne Substitution ausgeliefert
|
||||
// wird) - der waere sonst eine truthy Muell-URL.
|
||||
if (!configured || configured.startsWith("${")) {
|
||||
return DEFAULT_API_BASE;
|
||||
}
|
||||
return configured;
|
||||
}
|
||||
|
||||
async function getJson(path) {
|
||||
|
|
|
|||
|
|
@ -1013,6 +1013,17 @@ function agentPlacements(agents, deskLayout) {
|
|||
return placements;
|
||||
}
|
||||
|
||||
// Der Demo-Fallback unten sieht auf den ersten Blick wie eine echte Szene aus (fremde
|
||||
// Agenten-Namen, erfundene Ticketzahlen, aber leere Detail-Modals). Damit das auf einem
|
||||
// Produktiv-Monitor nicht fuer echte Daten gehalten wird, blendet der Fallback zusaetzlich
|
||||
// einen roten Banner ein - und wieder aus, sobald die API antwortet.
|
||||
function setApiBanner(visible) {
|
||||
const banner = document.getElementById("api-status-banner");
|
||||
if (banner) {
|
||||
banner.hidden = !visible;
|
||||
}
|
||||
}
|
||||
|
||||
async function boot() {
|
||||
const container = document.getElementById("office-scene");
|
||||
if (!container) {
|
||||
|
|
@ -1039,9 +1050,11 @@ async function boot() {
|
|||
events: data.events,
|
||||
kpis: data.kpis,
|
||||
});
|
||||
setApiBanner(false);
|
||||
} catch (err) {
|
||||
console.warn("[office] Live-API nicht erreichbar, zeige Demo-Daten.", err);
|
||||
renderOfficeScene(container);
|
||||
setApiBanner(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,18 +18,14 @@
|
|||
</p>
|
||||
</header>
|
||||
|
||||
<div id="api-status-banner" class="api-banner" role="alert" hidden>API nicht erreichbar!</div>
|
||||
|
||||
<main id="office-scene" aria-label="Buero-Szene"></main>
|
||||
|
||||
<script>
|
||||
// Docker-Compose-Dev-Setup (siehe docker-compose.yml): Frontend auf Port 8090, API auf
|
||||
// Port 8091 - unterschiedliche Origins, daher die explizite Basis-URL (die API liefert
|
||||
// dafuer CORS-Header, siehe api/src/Http/Response.php). Bei einem Produktions-Setup mit
|
||||
// gemeinsamem Origin/Reverse-Proxy kann dieser Block entfernt werden, Default ist dann
|
||||
// das gleiche Origin unter /api/v1 (siehe html/js/office-api.js).
|
||||
if (location.port === "8090") {
|
||||
window.MONITOR_API_BASE = `${location.protocol}//${location.hostname}:8091/api/v1`;
|
||||
}
|
||||
</script>
|
||||
<!-- Laufzeit-Konfiguration (API-Basis-URL): wird vom Webserver aus der Umgebungsvariable
|
||||
MONITOR_API_BASE erzeugt, siehe docker/nginx/default.conf.template und .env.example.
|
||||
Muss vor dem Modul stehen - ein klassisches Script laeuft synchron vor jedem Modul. -->
|
||||
<script src="/config.js"></script>
|
||||
<script type="module" src="js/office-render.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue