Files
Aether/frontend/vite.config.ts
T
marcandClaude Opus 5 3d9a4832f9 fix(pwa): geaenderte Kopfzeilen erreichen den Browser wieder
Ein Eintrag im Cache ist eine ganze Antwort, mitsamt ihren Kopfzeilen. Weil
index.html vorab im Cache lag und von dort auch „/" beantwortete, bekam der
Browser dauerhaft die CSP von damals: eine Aenderung an HONEYBAR_ORIGIN kam
nie an, denn der Inhalt der Datei aendert sich dabei nicht, und der
Vorab-Cache holt nur Geaendertes nach. Sichtbar als Widerspruch, an dem
nichts falsch aussah — das Skript laedt den neuen Ursprung, die CSP erlaubt
den alten.

Navigationen laufen jetzt Netz zuerst, Cache als Rueckfall; index.html bleibt
vorab abgelegt, aber nur noch als letzte Reihe (precacheFallback). Dass sie
nicht mehr als Verzeichnisseite fuer „/" einspringt, ist der eigentliche
Punkt: sonst kaeme die neue Regel nie zum Zug.

Nachgemessen: Wechsel des Ursprungs kommt beim zweiten Aufruf an, und
offline laden „/", „/admin" und „/s/<token>" weiterhin die Anwendung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 23:54:25 +02:00

145 lines
6.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { cpSync, mkdirSync } from 'node:fs'
import { createRequire } from 'node:module'
import { dirname, join } from 'node:path'
import { defineConfig, type Plugin } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import { VitePWA } from 'vite-plugin-pwa'
/**
* Die 14 Standardschriften von PDF.js nach public/ kopieren.
*
* Aeltere PDFs betten Helvetica, Times und Courier nicht ein, sondern setzen
* sie voraus. Ohne diese Dateien malt PDF.js mit einer Ersatzschrift – lesbar,
* aber Zeilen brechen an anderer Stelle als im Original.
*
* Kopiert statt eingecheckt: 800 KB fremde Binaerdateien gehoeren nicht in den
* Quelltext, und die Fassung soll der Abhaengigkeit folgen. public/pdfjs steht
* deshalb in .gitignore.
*/
function pdfjsStandardFonts(): Plugin {
return {
name: 'aether-pdfjs-standard-fonts',
// Nicht 'buildStart': Im Entwicklungsbetrieb liest der Server public/
// beim Start ein, das Kopieren muss vorher passiert sein.
config() {
const require = createRequire(import.meta.url)
const from = join(dirname(require.resolve('pdfjs-dist/package.json')), 'standard_fonts')
const to = join(import.meta.dirname, 'public', 'pdfjs', 'standard_fonts')
mkdirSync(to, { recursive: true })
cpSync(from, to, { recursive: true })
},
}
}
export default defineConfig({
plugins: [
pdfjsStandardFonts(),
react(),
tailwindcss(),
VitePWA({
registerType: 'autoUpdate',
workbox: {
// Der Service Worker beantwortet Navigationen — also den Aufruf der
// Seite selbst — **Netz zuerst, Cache als Rueckfall**.
//
// Frueher lag index.html im Vorab-Cache und wurde von dort geliefert.
// Das war schnell, hatte aber eine unscheinbare Folge: Ein Eintrag im
// Cache ist eine ganze Antwort, **mitsamt ihren Kopfzeilen**. Der
// Browser bekam also dauerhaft die CSP von damals. Eine Aenderung an
// HONEYBAR_ORIGIN erreichte ihn nie — der Inhalt der Datei aendert sich
// ja nicht, und der Vorab-Cache holt nur nach, was sich geaendert hat.
// Sichtbar wurde das als Widerspruch, an dem nichts falsch aussah: Das
// Skript laedt den neuen Ursprung, die CSP erlaubt den alten.
//
// precacheFallback haelt dabei, was navigateFallback vorher leistete:
// Ist weder Netz noch Cache da, antwortet die vorab abgelegte
// index.html — Adressen wie /admin oder /s/<token> funktionieren also
// weiterhin offline.
//
// Ausgenommen sind Downloads und WebDAV: Ein Klick auf „Download"
// oeffnet eine Navigation zu /api/v1/files/<id>/download. Faengt der
// Worker die ab, liefert er die Anwendung statt der Datei — und es
// passiert sichtbar nichts. Sie sollen am Worker vorbeigehen, nicht
// von ihm beantwortet werden.
// Der Vorab-Cache beantwortet sonst auch den Aufruf von „/" — er
// rechnet index.html als Verzeichnisseite an. Genau daran scheiterte
// der erste Anlauf: Die Regel unten kam nie zum Zug, weil die Seite
// schon vorher aus dem Vorab-Cache beantwortet wurde, mitsamt alter
// CSP. Als reiner Rueckfall (precacheFallback) bleibt index.html dort
// liegen, nur eben nicht mehr als Antwort auf jede Navigation.
directoryIndex: null,
// vite-plugin-pwa setzt sonst von sich aus navigateFallback: 'index.html'
// — also genau die Vorab-Cache-Antwort, die wir hier los sein wollen.
// Die Sperrliste nimmt jede Adresse davon aus; zustaendig ist dann die
// Regel darunter.
navigateFallbackDenylist: [/./],
runtimeCaching: [
{
urlPattern: ({ request, url }) =>
request.mode === 'navigate' &&
!/^\/(api|dav|share|branding|wopi)\//.test(url.pathname),
handler: 'NetworkFirst',
options: {
cacheName: 'aether-seiten',
// Ohne Grenze haengt der Start am langsamsten Netz.
networkTimeoutSeconds: 3,
precacheFallback: { fallbackURL: 'index.html' },
},
},
],
// PDF.js bleibt aussen vor: Betrachter (435 KB), Arbeiter (1,3 MB)
// und Standardschriften (800 KB) sind zusammen zweieinhalb Megabyte,
// die nur braucht, wer ein PDF oeffnet. Vorab in den Cache zu legen
// hiesse, das jeder Installation aufzuladen.
//
// Offline waere davon ohnehin nichts gewonnen: Die Dokumente selbst
// liegen auf dem Server und werden nicht mitgecacht – ein Betrachter
// ohne Datei nuetzt nichts.
globIgnores: [
'**/pdfjs/standard_fonts/**',
'**/pdf.worker*.mjs',
'**/PdfViewer-*.js',
'**/PdfViewer-*.css',
],
maximumFileSizeToCacheInBytes: 3 * 1024 * 1024,
},
// Das Manifest liefert der Server (/manifest.webmanifest): Name, Farbe
// und Symbol stehen in der Datenbank und sollen dem Admin-Bereich
// folgen. Gebaut waeren sie fest verdrahtet, und ein hochgeladenes
// Symbol erreichte die installierte App nie.
manifest: false,
}),
],
server: {
host: true, // auf allen Interfaces lauschen (IPv4 + IPv6), nötig für VS-Code-Port-Forwarding
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
proxyTimeout: 60 * 60 * 1000,
timeout: 60 * 60 * 1000,
},
'/share': {
target: 'http://localhost:8080',
changeOrigin: true,
},
// Logo, App-Symbol und Manifest kommen ebenfalls vom Backend – ohne
// diese beiden Eintraege fehlen sie im Entwicklungsbetrieb.
'/branding': {
target: 'http://localhost:8080',
changeOrigin: true,
},
'/manifest.webmanifest': {
target: 'http://localhost:8080',
changeOrigin: true,
},
},
},
test: {
environment: 'jsdom',
globals: true,
setupFiles: './src/test-setup.ts',
},
})