You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Producer schreiben in ihr eigenes Runtime-Verzeichnis
77
+
- Das Webroot enthält nur veröffentlichte Dateinamen oder Symlinks
78
+
- Der HTTP-Pfad bleibt stabil, auch wenn sich das interne Runtime-Verzeichnis einer App ändert
79
+
80
+
Wichtig für die Auslieferung:
81
+
82
+
- Die Datei selbst sollte mit mindestens `0644` geschrieben werden
83
+
- Alle übergeordneten Verzeichnisse auf dem Pfad zum Symlink-Ziel müssen für den `nginx`-Prozess durchsuchbar sein, in der Praxis also typischerweise mindestens `0755`
84
+
- Fehlt dieses Execute-Bit auf einem Verzeichnis, endet der Request trotz vorhandenem Symlink mit `403 Forbidden`
85
+
86
+
Minimal nötig sind also drei Dinge: Runtime-Datei schreiben, Symlink im Webroot anlegen und den Pfad für `nginx` lesbar bzw. durchsuchbar machen.
87
+
88
+
### Wann reicht ein JSON-Endpoint?
89
+
90
+
Nicht jede Erweiterung braucht einen eigenen Menüpunkt in der UI.
91
+
92
+
- Ein zusätzlicher JSON-Endpoint reicht, wenn die bestehende UI nur weitere Daten anzeigen soll, zum Beispiel ein zusätzliches Panel oder weitere Kennzahlen auf einer vorhandenen Seite
93
+
- Ein eigener UI-View ist erst dann sinnvoll, wenn die Erweiterung einen eigenen Bedienkontext, eigene Interaktion oder eine eigene Seite innerhalb der Navigation braucht
94
+
95
+
Faustregel: Neue Daten allein sind noch keine UI-Erweiterung. Ein neuer View ist erst dann sinnvoll, wenn die bestehende Seite dafür fachlich zu eng wird.
96
+
58
97
### Optionaler Plattform-Dienst: Mesh-Status
59
98
60
99
Apps können den aktuellen Mesh-Zustand über `/run/freifunk/state/mesh-status.json` lesen, müssen davon aber nicht abhängen.
@@ -93,6 +132,83 @@ Bedeutung:
93
132
Für Apps ist `mesh.stable` das robustere Startsignal als `gateway.connected`, weil Mesh auch ohne selektiertes Gateway sinnvoll benutzbar sein kann.
94
133
Wenn eine App keinen Mesh-Bezug hat, kann dieser Plattform-Dienst ignoriert werden.
95
134
135
+
### UI-Erweiterungen
136
+
137
+
Wenn eine App einen eigenen Menüpunkt in der Standard-UI bekommen soll, klinkt sie sich als zusätzlicher View in die bestehende SPA ein.
138
+
Topbar, Sidebar, Routing und Grundlayout bleiben dabei in der Basis-UI.
139
+
Die Erweiterung liefert nur ihren eigenen Inhaltsbereich.
140
+
141
+
Ziel ist ein einheitliches Erscheinungsbild ohne harte Runtime-Kopplung an das Basis-Frontend.
142
+
Für UI-Erweiterungen gilt folgender Minimalvertrag:
143
+
144
+
- Die Erweiterung verwendet dieselbe visuelle Sprache: Farben, Abstände, Status-Pills, Tabellenstil und allgemeine Layout-Konventionen
145
+
- Die Erweiterung kann intern dasselbe Framework wie die Basis-UI verwenden, zum Beispiel Preact
146
+
- Die Erweiterung hängt aber nicht von der konkret im Basis-Image eingebauten Framework-Version ab
147
+
- Die Erweiterung liefert ein eigenes Bundle aus und erwartet nicht, dass Host-Komponenten oder die Host-Runtime direkt importierbar sind
148
+
- Geteilt wird ein kleiner UI-Vertrag, nicht die komplette interne Frontend-Struktur
149
+
150
+
Minimaler technischer Vertrag:
151
+
152
+
- Die Basis-UI besitzt Navigation, Hash-Routing, Kopfbereich und allgemeines Seitenlayout
153
+
- Eine Erweiterung wird nicht per Verzeichnis-Scan gefunden, sondern über eine Registry-Datei angemeldet
154
+
- Das Bundle der Erweiterung wird im abgeleiteten Image nach `/usr/local/share/freifunk/ui-extensions/APP/` kopiert
155
+
- Zur Laufzeit wird es nach `/run/freifunk/www/ui/extensions/APP/` veröffentlicht
156
+
- Die Registry-Datei `/ui/extensions/index.json` wird von der Plattform erzeugt; einzelne Erweiterungen schreiben diese Datei nicht direkt
157
+
- Wenn mehrere Erweiterungen vorhanden sind, führt die Plattform deren Einträge zu einer gemeinsamen Registry zusammen
158
+
- Die Basis-UI lädt `/ui/extensions/index.json`, baut daraus die Menüeinträge und lädt das aktive Bundle dynamisch
159
+
- Erweiterungen deklarieren die von ihnen benötigten JSON-Endpunkte im jeweiligen Registry-Eintrag, damit die Basis-UI diese in ihren gemeinsamen Refresh-Zyklus aufnehmen kann
160
+
- Eine Erweiterung liefert mindestens Menü-Key, Label, Reihenfolge und einen Renderer für den Content-Bereich
161
+
162
+
Beispiel für die Registry-Datei:
163
+
164
+
```json
165
+
{
166
+
"extensions": [
167
+
{
168
+
"id": "metadata",
169
+
"label": "Metadaten",
170
+
"order": 100,
171
+
"hash": "metadata",
172
+
"entry": "/ui/extensions/metadata/index.js",
173
+
"endpoints": [
174
+
"/metadata.json"
175
+
]
176
+
}
177
+
]
178
+
}
179
+
```
180
+
181
+
Minimaler Renderer-Vertrag:
182
+
183
+
```javascript
184
+
exportfunctionrender(container, context) {
185
+
container.textContent='Metadata view';
186
+
}
187
+
188
+
exportfunctiondispose(container) {
189
+
container.textContent='';
190
+
}
191
+
```
192
+
193
+
Dabei gilt:
194
+
195
+
-`hash` bestimmt den View-Key in der SPA, zum Beispiel `#metadata`
196
+
-`entry` verweist auf das gebaute Bundle der Erweiterung
197
+
-`render()` rendert nur den Inhaltsbereich, nicht die gesamte Seite
198
+
- Die Basis-UI lädt Core- und Extension-Daten in einem gemeinsamen Refresh-Zyklus, typischerweise alle 30 Sekunden
199
+
- Erweiterungen starten dafür standardmäßig keine eigenen Polling-Timer
200
+
-`context` enthält nur kleine stabile Host-Helfer und den aktuellen Datenstand der Erweiterung, zum Beispiel `data`, `error`, `refreshNow()`, `fetchJson(url)`, `fetchText(url)`, `safe(value)` und einfache Formatierungsfunktionen
201
+
- Alles außerhalb dieses kleinen `context`-Vertrags gilt als intern und wird von Erweiterungen nicht direkt verwendet
202
+
203
+
Bewusst nicht Teil des Vertrags sind:
204
+
205
+
- direkte Imports interner Komponenten aus der Basis-SPA
206
+
- Abhängigkeit von einer exakt gleichen Host-Framework-Version
207
+
- eigene Topbar oder Sidebar innerhalb der Erweiterung
208
+
- eine separate, vollständig unabhängige Web-App für kleine Zusatzfunktionen
209
+
210
+
Damit bleibt die UI konsistent, und Updates des Basis-Images können erfolgen, ohne dass jede Erweiterung an interne Frontend-Details gekoppelt ist.
211
+
96
212
### Installation
97
213
98
214
Die Anwendung wird im Dockerfile in das Image installiert.
0 commit comments