feat(aether): initial commit – self-hosted Cloud, Phasen 1–7

Go-Backend (chi, pgx, golang-migrate) + React/TS-Frontend als PWA.
Enthalten: Auth inkl. OIDC, Dateien mit Versionen und Papierkorb,
Freigaben (Links + intern), WebDAV, Gruppenordner, Admin/Theming,
Volltextsuche mit OCR, eigener PDF-Betrachter und WOPI-Office
(Collabora/OnlyOffice).

Nicht im Repo: deploy/.env (Sitzungsschlüssel, DB- und Admin-Passwort)
sowie alle Daten- und Build-Verzeichnisse – siehe .gitignore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Opus 5 committed 2026-08-24 09:25:36 +02:00
commit 31a027c93a
225 files changed
+38042

No files matched your search

+104
View File
@@ -0,0 +1,104 @@
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 jede Navigation aus dem Cache mit
// index.html – nötig, damit Adressen wie /admin oder /s/<token> auch
// offline funktionieren. Ohne diese Ausnahmen erwischt er aber auch
// Downloads: Ein Klick auf „Download" öffnet eine Navigation zu
// /api/v1/files/<id>/download, der Worker liefert die Anwendung statt
// der Datei, und es passiert sichtbar nichts.
navigateFallback: 'index.html',
navigateFallbackDenylist: [/^\/api\//, /^\/dav\//, /^\/share\//, /^\/branding\//, /^\/wopi\//],
// 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',
},
})