fix(honeybar): Ursprung zur Laufzeit statt beim Bauen

Die Adresse im <script>-Tag stand fest in der index.html, waehrend die CSP
aus HONEYBAR_ORIGIN kam. Wer die Leiste woanders betreibt, bekam genau den
Widerspruch: die CSP erlaubte den eigenen Ursprung, geladen wurde der alte.

Der erste Versuch, das ueber ein Bau-Argument zu loesen, half beim fertigen
Abbild nicht — dort ist der Bau vorbei, und ein Umzug haette einen Neubau
verlangt. Ursprung und Schluessel stehen jetzt in /api/v1/public/config, die
Oberflaeche haengt das Skript selbst ein. Beides speist sich aus derselben
Variablen wie die CSP, sie koennen also nicht auseinanderlaufen, und eine
Aenderung wirkt beim Neustart.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-08-24 23:07:35 +02:00
1 parent 6c7eb348b0
commit 57156a6394
11 files changed
+74 -67

No files matched your search

-22
View File
@@ -19,27 +19,5 @@
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<!-- HoneyOS-Leiste: der Umschalter zwischen den eigenen Apps. Sie rendert
in einem Shadow DOM, Aethers Stile berührt sie nicht.
Sie steht hier statt serverseitig eingespritzt, weil der Service Worker
die index.html in seinen Vorrat legt (navigateFallback) — was zur
Bauzeit nicht drinsteht, käme beim Browser nie an.
`defer`: ohne wartet der Seitenaufbau auf das Skript. Ist HoneyOS
langsam oder aus, darf das Aether nicht ausbremsen.
Ursprung und Schlüssel kommen beide aus der .env (HONEYBAR_ORIGIN,
HONEYBAR_KEY) und werden beim Bauen eingesetzt. Derselbe Ursprung
landet zur Laufzeit in der CSP — so können die beiden nicht
auseinanderlaufen. Ist er leer, entfernt vite.config.ts diesen Tag
ganz; Aether läuft dann ohne Leiste weiter. HoneyOS liegt auf einer anderen Site als Aether
(duckdns.org ist ein public suffix), deshalb schickt der Browser die
HoneyOS-Sitzung nicht mit — ohne diesen zweiten Ausweis bekäme die
Leiste nie eine App-Liste. Der Schlüssel steht bewusst nicht im
Quelltext: er käme sonst ins Repo und liesse sich nur mit einer
Code-Änderung wechseln. -->
<script defer src="%VITE_HONEYBAR_ORIGIN%/honeybar/honeybar.js"
data-app="aether" data-key="%VITE_HONEYBAR_KEY%"></script>
</body>
</html>
+5
View File
@@ -231,6 +231,11 @@ export interface PublicConfig {
oidc_button_label?: string
primary_color?: string
accent_color?: string
/** Ursprung der HoneyOS-Leiste (HONEYBAR_ORIGIN). Leer = keine Leiste. */
honeybar_origin?: string
/** Ausweis fuer deren App-Liste (HONEYBAR_KEY). Nur gesetzt, wenn es einen
* Ursprung gibt. */
honeybar_key?: string
external_apps: ExternalApp[]
}
+28
View File
@@ -0,0 +1,28 @@
/**
* Die HoneyOS-Leiste nachladen.
*
* Zur Laufzeit statt beim Bauen: Der Ursprung steht in der .env des Servers,
* ein fertiges Abbild laesst sich damit umziehen, ohne es neu zu bauen. Frueher
* stand die Adresse fest in der index.html — wer die Leiste woanders betrieb,
* bekam einen CSP-Verstoss, weil der erlaubte und der geladene Ursprung
* auseinanderliefen. Beides kommt jetzt aus HONEYBAR_ORIGIN.
*
* Faellt der Server aus oder ist kein Ursprung gesetzt, passiert schlicht
* nichts: Aether ist nicht auf die Leiste angewiesen.
*/
export function mountHoneybar(origin?: string, key?: string) {
if (!origin) return
// Der Aufruf haengt an der Konfiguration, und die wird auch neu geladen,
// wenn der Admin etwas aendert. Zweimal einbinden hiesse zwei Leisten.
if (document.querySelector('script[data-honeybar]')) return
const el = document.createElement('script')
el.dataset.honeybar = ''
el.defer = true
el.src = origin.replace(/\/+$/, '') + '/honeybar/honeybar.js'
// honeybar.js liest seine Einstellungen ueber document.currentScript –
// das gilt auch fuer ein nachtraeglich eingehaengtes Skript.
el.dataset.app = 'aether'
if (key) el.dataset.key = key
document.head.appendChild(el)
}
+2
View File
@@ -1,3 +1,4 @@
import { mountHoneybar } from '../honeybar'
import { createContext, useCallback, useContext, useEffect, useState, type ReactNode } from 'react'
import { publicApi } from '../api/client'
import { applyTheme } from '../theme'
@@ -103,6 +104,7 @@ export function AppSettingsProvider({ children }: { children: ReactNode }) {
if (typeof cfg.max_versions_kept === 'number' && cfg.max_versions_kept > 0) {
setMaxVersionsKept(cfg.max_versions_kept)
}
mountHoneybar(cfg.honeybar_origin, cfg.honeybar_key)
applyTheme(cfg.primary_color, cfg.accent_color)
applyThemeColor(cfg.primary_color)
document.title = cfg.cloud_name || 'Aether'
+1
View File
@@ -0,0 +1 @@
{"root":["./src/App.tsx","./src/honeybar.ts","./src/main.tsx","./src/test-setup.ts","./src/theme.ts","./src/api/client.ts","./src/components/AppPasswords.tsx","./src/components/AppSwitcher.tsx","./src/components/Breadcrumb.tsx","./src/components/ConfirmDialog.tsx","./src/components/ContextMenu.tsx","./src/components/Dialog.tsx","./src/components/FileCover.tsx","./src/components/FileInfoDialog.tsx","./src/components/FilePreview.tsx","./src/components/FileViews.tsx","./src/components/MoveTo.tsx","./src/components/OfficeEditor.tsx","./src/components/PageHeader.tsx","./src/components/PdfViewer.tsx","./src/components/SearchBox.tsx","./src/components/ShareDialog.tsx","./src/components/ShareWithDialog.tsx","./src/components/Shell.tsx","./src/components/Sidebar.tsx","./src/components/Tabs.tsx","./src/components/TagChip.tsx","./src/components/TagDialog.tsx","./src/components/TagEditDialog.tsx","./src/components/UploadQueue.tsx","./src/components/UserMenu.tsx","./src/components/VersionsDialog.tsx","./src/components/WebDavGuide.tsx","./src/components/filetypes.ts","./src/components/icons.tsx","./src/hooks/useAppSettings.tsx","./src/hooks/useAuth.ts","./src/hooks/useInternalDrag.ts","./src/hooks/useNavHistory.ts","./src/hooks/useOverlayDismiss.ts","./src/pages/AdminPage.tsx","./src/pages/FileBrowser.tsx","./src/pages/LoginPage.tsx","./src/pages/ProfilePage.tsx","./src/pages/SharePage.tsx","./src/pages/SharedPage.tsx","./src/pages/SharedWithMePage.tsx","./src/pages/TagsPage.tsx","./src/pages/TrashPage.tsx","./src/pages/admin/AuditTab.tsx","./src/pages/admin/GroupsTab.tsx","./src/pages/admin/ThemingTab.tsx","./src/pages/admin/UsersTab.tsx","./src/pages/admin/ui.tsx"],"version":"6.0.3"}
-21
View File
@@ -32,30 +32,9 @@ function pdfjsStandardFonts(): Plugin {
}
}
/**
* Den <script>-Tag der HoneyOS-Leiste entfernen, wenn kein Ursprung gesetzt ist.
*
* Ohne das bliebe nach der Ersetzung src="/honeybar/honeybar.js" stehen: der
* Service Worker beantwortet die Navigation mit index.html, der Browser meldet
* einen MIME-Fehler. Kein Ursprung heisst schlicht keine Leiste.
*/
function honeybar(): Plugin {
return {
name: 'aether-honeybar',
transformIndexHtml: {
order: 'pre',
handler(html) {
if (process.env.VITE_HONEYBAR_ORIGIN?.trim()) return html
return html.replace(/\s*<script[^>]*honeybar\.js[^>]*><\/script>/, '')
},
},
}
}
export default defineConfig({
plugins: [
pdfjsStandardFonts(),
honeybar(),
react(),
tailwindcss(),
VitePWA({