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
+146
View File
@@ -0,0 +1,146 @@
<template>
<div class="auth-container">
<div class="auth-card">
<div class="auth-header">
<i class="pi pi-cloud" style="font-size: 2.5rem; color: var(--p-primary-color)"></i>
<h1>Mini-Cloud</h1>
<p>Anmelden</p>
</div>
<form @submit.prevent="handleLogin" class="auth-form">
<div class="field">
<label for="username">Benutzername</label>
<InputText
id="username"
v-model="username"
placeholder="Benutzername"
:invalid="!!error"
autofocus
fluid
/>
</div>
<div class="field">
<label for="password">Passwort</label>
<Password
id="password"
v-model="password"
placeholder="Passwort"
:feedback="false"
:invalid="!!error"
toggle-mask
fluid
/>
</div>
<Message v-if="error" severity="error" :closable="false">{{ error }}</Message>
<Button
type="submit"
label="Anmelden"
:loading="loading"
fluid
/>
</form>
<div class="auth-footer">
<router-link to="/register">Noch kein Konto? Registrieren</router-link>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useAuthStore } from '../stores/auth'
import InputText from 'primevue/inputtext'
import Password from 'primevue/password'
import Button from 'primevue/button'
import Message from 'primevue/message'
const router = useRouter()
const auth = useAuthStore()
const username = ref('')
const password = ref('')
const error = ref('')
const loading = ref(false)
async function handleLogin() {
error.value = ''
loading.value = true
try {
await auth.login(username.value, password.value)
router.push('/')
} catch (err) {
error.value = err.response?.data?.error || 'Anmeldung fehlgeschlagen'
} finally {
loading.value = false
}
}
</script>
<style scoped>
.auth-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: var(--p-surface-50);
}
.auth-card {
background: var(--p-surface-0);
border-radius: 12px;
padding: 2.5rem;
width: 100%;
max-width: 400px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
.auth-header {
text-align: center;
margin-bottom: 2rem;
}
.auth-header h1 {
margin: 0.5rem 0 0.25rem;
font-size: 1.5rem;
}
.auth-header p {
color: var(--p-text-muted-color);
margin: 0;
}
.auth-form .field {
margin-bottom: 1rem;
}
.auth-form .field label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
font-size: 0.875rem;
}
.auth-form button {
margin-top: 0.5rem;
}
.auth-footer {
text-align: center;
margin-top: 1.5rem;
font-size: 0.875rem;
}
.auth-footer a {
color: var(--p-primary-color);
text-decoration: none;
}
.auth-footer a:hover {
text-decoration: underline;
}
</style>