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:
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div class="view-container">
|
||||
<div class="view-header">
|
||||
<h2>Einstellungen</h2>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<h3>Profil</h3>
|
||||
<div class="settings-info">
|
||||
<div class="info-row">
|
||||
<span class="label">Benutzername:</span>
|
||||
<span>{{ auth.user?.username }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="label">E-Mail:</span>
|
||||
<span>{{ auth.user?.email || 'Nicht angegeben' }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="label">Rolle:</span>
|
||||
<Tag :value="auth.user?.role" :severity="auth.user?.role === 'admin' ? 'danger' : 'info'" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<h3>Passwort aendern</h3>
|
||||
<form @submit.prevent="handleChangePassword" class="password-form">
|
||||
<div class="field">
|
||||
<label>Aktuelles Passwort</label>
|
||||
<Password v-model="currentPassword" :feedback="false" toggle-mask fluid />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Neues Passwort</label>
|
||||
<Password v-model="newPassword" toggle-mask fluid />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Neues Passwort wiederholen</label>
|
||||
<Password v-model="newPassword2" :feedback="false" toggle-mask fluid />
|
||||
</div>
|
||||
<Message v-if="pwError" severity="error" :closable="false">{{ pwError }}</Message>
|
||||
<Message v-if="pwSuccess" severity="success" :closable="false">{{ pwSuccess }}</Message>
|
||||
<Button type="submit" label="Passwort aendern" :loading="pwLoading" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
import Password from 'primevue/password'
|
||||
import Button from 'primevue/button'
|
||||
import Message from 'primevue/message'
|
||||
import Tag from 'primevue/tag'
|
||||
|
||||
const auth = useAuthStore()
|
||||
const currentPassword = ref('')
|
||||
const newPassword = ref('')
|
||||
const newPassword2 = ref('')
|
||||
const pwError = ref('')
|
||||
const pwSuccess = ref('')
|
||||
const pwLoading = ref(false)
|
||||
|
||||
async function handleChangePassword() {
|
||||
pwError.value = ''
|
||||
pwSuccess.value = ''
|
||||
|
||||
if (newPassword.value !== newPassword2.value) {
|
||||
pwError.value = 'Neue Passwoerter stimmen nicht ueberein'
|
||||
return
|
||||
}
|
||||
|
||||
pwLoading.value = true
|
||||
try {
|
||||
await auth.changePassword(currentPassword.value, newPassword.value)
|
||||
pwSuccess.value = 'Passwort erfolgreich geaendert'
|
||||
currentPassword.value = ''
|
||||
newPassword.value = ''
|
||||
newPassword2.value = ''
|
||||
} catch (err) {
|
||||
pwError.value = err.response?.data?.error || 'Fehler beim Aendern des Passworts'
|
||||
} finally {
|
||||
pwLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.view-container { padding: 1.5rem; }
|
||||
.view-header h2 { margin: 0 0 1.5rem; }
|
||||
.settings-section {
|
||||
background: var(--p-surface-0);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.settings-section h3 { margin: 0 0 1rem; font-size: 1.125rem; }
|
||||
.settings-info { display: flex; flex-direction: column; gap: 0.5rem; }
|
||||
.info-row { display: flex; align-items: center; gap: 0.5rem; }
|
||||
.info-row .label { font-weight: 500; min-width: 120px; }
|
||||
.password-form { max-width: 400px; }
|
||||
.password-form .field { margin-bottom: 1rem; }
|
||||
.password-form .field label { display: block; margin-bottom: 0.5rem; font-weight: 500; font-size: 0.875rem; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user