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

+54
View File
@@ -0,0 +1,54 @@
import { useState, useEffect } from 'react'
import { adminApi, type AdminUser } from '../api/client'
import PageHeader from '../components/PageHeader'
import Tabs from '../components/Tabs'
type Tab = 'users' | 'groups' | 'theming' | 'audit'
import UsersTab from './admin/UsersTab'
import GroupsTab from './admin/GroupsTab'
import ThemingTab from './admin/ThemingTab'
import AuditTab from './admin/AuditTab'
export default function AdminPage() {
const [tab, setTab] = useState<Tab>('users')
const [allUsers, setAllUsers] = useState<AdminUser[]>([])
useEffect(() => {
adminApi.listUsers().then(data => setAllUsers(data ?? [])).catch(() => {})
}, [])
const tabs: { id: Tab; label: string }[] = [
{ id: 'users', label: 'Users' },
{ id: 'groups', label: 'Groups' },
{ id: 'theming', label: 'Theming' },
// „Activity" statt „Audit log": Der Reiter zeigt nicht mehr nur das
// Protokoll, sondern davor auch den Stand der Texterkennung und die
// Speicherbereinigung – alles, was im Hintergrund läuft. Die Kennung
// bleibt 'audit', sie steht in keiner Adresse und muss nicht wandern.
{ id: 'audit', label: 'Activity' },
]
return (
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
<PageHeader
icon={
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 2l8 4v6c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10V6z" />
<path d="M9.5 12.5l1.8 1.8 3.4-3.6" />
</svg>
}
title="Admin panel"
meta="Users, groups, appearance and activity"
/>
<div style={{ flex: 1, overflowY: 'auto', padding: 20 }}>
<Tabs tabs={tabs} active={tab} onChange={setTab} />
{tab === 'users' && <UsersTab />}
{tab === 'groups' && <GroupsTab allUsers={allUsers} />}
{tab === 'theming' && <ThemingTab />}
{tab === 'audit' && <AuditTab />}
</div>
</div>
)
}