feat: Mini-Cloud Plattform - komplette Implementierung Phase 0-8

Selbstgehostete Web-Cloud mit Dateiverwaltung, Kalender, Kontakte,
Email-Webclient, Office-Viewer und Passwort-Manager.

Backend (Flask/Python):
- JWT-Auth mit Access/Refresh Tokens, Benutzerverwaltung
- Dateien: Upload/Download, Ordner, Berechtigungen, Share-Links
- Kalender: CRUD, Teilen, iCal-Export, CalDAV well-known URLs
- Kontakte: Adressbuecher, vCard-Export, Teilen
- Email: IMAP/SMTP-Proxy, Multi-Account
- Office-Viewer: DOCX/XLSX/PPTX/PDF Vorschau
- Passwort-Manager: AES-256-GCM clientseitig, KeePass-Import
- Sync-API fuer Desktop/Mobile-Clients
- SQLite mit WAL-Modus

Frontend (Vue 3 + PrimeVue):
- Datei-Explorer mit Breadcrumbs und Share-Dialogen
- Monatskalender mit Event-Verwaltung
- Kontaktliste mit Adressbuch-Sidebar
- Email-Client mit 3-Spalten-Layout
- Passwort-Manager mit TOTP und Passwort-Generator
- Admin-Panel, Settings, oeffentliche Share-Seite

Docker: Multi-Stage Build, Bind Mounts (keine Volumes)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Hacker
2026-04-11 14:53:28 +02:00
parent d4f7e90d0c
commit 62f550c373
56 changed files with 8047 additions and 0 deletions
+100
View File
@@ -0,0 +1,100 @@
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '../stores/auth'
const routes = [
{
path: '/login',
name: 'Login',
component: () => import('../views/LoginView.vue'),
meta: { guest: true },
},
{
path: '/register',
name: 'Register',
component: () => import('../views/RegisterView.vue'),
meta: { guest: true },
},
{
path: '/',
component: () => import('../views/AppLayout.vue'),
meta: { requiresAuth: true },
children: [
{
path: '',
redirect: '/files',
},
{
path: 'files/:folderId?',
name: 'Files',
component: () => import('../views/FilesView.vue'),
},
{
path: 'calendar',
name: 'Calendar',
component: () => import('../views/CalendarView.vue'),
},
{
path: 'contacts',
name: 'Contacts',
component: () => import('../views/ContactsView.vue'),
},
{
path: 'email',
name: 'Email',
component: () => import('../views/EmailView.vue'),
},
{
path: 'passwords',
name: 'Passwords',
component: () => import('../views/PasswordsView.vue'),
},
{
path: 'admin',
name: 'Admin',
component: () => import('../views/AdminView.vue'),
meta: { requiresAdmin: true },
},
{
path: 'settings',
name: 'Settings',
component: () => import('../views/SettingsView.vue'),
},
],
},
{
path: '/share/:token',
name: 'Share',
component: () => import('../views/ShareView.vue'),
},
]
const router = createRouter({
history: createWebHistory(),
routes,
})
router.beforeEach(async (to, from, next) => {
const auth = useAuthStore()
if (to.meta.requiresAuth && !auth.isAuthenticated) {
// Try to refresh token
try {
await auth.refreshToken()
await auth.fetchMe()
} catch {
return next('/login')
}
}
if (to.meta.guest && auth.isAuthenticated) {
return next('/')
}
if (to.meta.requiresAdmin && !auth.isAdmin) {
return next('/')
}
next()
})
export default router