feat: File Locking System (Ein-/Auschecken) + Konflikt-Email

Backend - FileLock Model + API:
- POST /files/<id>/lock - Datei auschecken (sperren)
- POST /files/<id>/unlock - Datei einchecken (entsperren)
- POST /files/<id>/heartbeat - "Datei noch offen" (alle 60s)
- GET /files/<id>/lock-status - Sperrstatus abfragen
- GET /files/locks - Alle aktiven Sperren auflisten
- Auto-Unlock: Kein Heartbeat seit 5 Min -> Sperre wird freigegeben
- 423 Locked wenn bereits von anderem User gesperrt
- Admin kann fremde Sperren aufheben

Dateiliste + Sync-API:
- Lock-Info (locked, locked_by, locked_at) pro Datei mitgeliefert
- Sync-Tree enthaelt Lock-Status fuer Desktop/Mobile-Clients

Web-UI:
- Schloss-Icon mit Benutzername bei gesperrten Dateien
- Tooltip: "Ausgecheckt von Adam seit 14:30"
- Gesperrte Dateien: "Oeffnen nicht moeglich" Toast-Meldung
  (eigene Sperren sind erlaubt)

Konflikt-Email an Admin:
- Wer hat die Konflikt-Kopie erstellt (Name + Email)
- Welche Datei (Name + Ordnerpfad)
- Name der Konflikt-Kopie
- Von wem gesperrt (Name + Email + seit wann)
- Erklaerungstext was passiert ist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Hacker
2026-04-11 23:20:55 +02:00
parent 33156f9431
commit 748537b9f5
5 changed files with 216 additions and 0 deletions
+29
View File
@@ -179,3 +179,32 @@ def notify_user_created(user, created_by_username):
f'Deine Mini-Cloud'
)
send_system_email(user.email, subject, body)
def notify_conflict_to_admin(conflict_user, conflict_file_name, conflict_copy_name,
folder_path, lock_user_name, lock_user_email, locked_since):
"""Notify admin about a sync conflict (user edited a locked file)."""
from app.models.settings import AppSettings
admin_email = AppSettings.get('system_email_from', '')
if not admin_email:
return
subject = f'Mini-Cloud: Datei-Konflikt - {conflict_file_name}'
body = (
f'Datei-Konflikt in der Mini-Cloud!\n\n'
f'Benutzer: {conflict_user.username}'
f'{" (" + conflict_user.email + ")" if conflict_user.email else ""}\n'
f'Hat bearbeitet: {conflict_file_name}\n'
f'Ordner: {folder_path}\n'
f'Konflikt-Kopie: {conflict_copy_name}\n\n'
f'Gesperrt von: {lock_user_name}'
f'{" (" + lock_user_email + ")" if lock_user_email else ""}\n'
f'Gesperrt seit: {locked_since}\n\n'
f'Ursache: {conflict_user.username} hat die Datei lokal bearbeitet '
f'waehrend {lock_user_name} sie ausgecheckt hatte.\n\n'
f'Die Aenderungen von {conflict_user.username} wurden als '
f'Konflikt-Kopie gespeichert und muessen manuell zusammengefuehrt werden.\n\n'
f'Deine Mini-Cloud'
)
send_system_email(admin_email, subject, body)