🎉 Added COMMON_ERRORS.md, updated requirements.txt, setup.sh, and introduced file_utils.py & ssl_config.py in backend/utils 🎨
This commit is contained in:
320
backend/utils/file_utils.py
Normal file
320
backend/utils/file_utils.py
Normal file
@@ -0,0 +1,320 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
File Utilities für MYP Platform
|
||||
Ersetzt Send2Trash mit nativen Lösungen für alle Plattformen
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import platform
|
||||
import subprocess
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Union, List, Optional
|
||||
|
||||
# Logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class SafeFileHandler:
|
||||
"""
|
||||
Sichere Datei-Operationen ohne externe Abhängigkeiten
|
||||
Ersetzt Send2Trash mit nativen Lösungen
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.platform = platform.system().lower()
|
||||
self.is_windows = self.platform == 'windows'
|
||||
self.is_linux = self.platform == 'linux'
|
||||
self.is_macos = self.platform == 'darwin'
|
||||
|
||||
def move_to_trash(self, file_path: Union[str, Path]) -> bool:
|
||||
"""
|
||||
Verschiebt eine Datei in den Papierkorb/Trash
|
||||
|
||||
Args:
|
||||
file_path: Pfad zur Datei oder zum Ordner
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich, False bei Fehler
|
||||
"""
|
||||
file_path = Path(file_path)
|
||||
|
||||
if not file_path.exists():
|
||||
logger.warning(f"Datei nicht gefunden: {file_path}")
|
||||
return False
|
||||
|
||||
try:
|
||||
if self.is_windows:
|
||||
return self._move_to_trash_windows(file_path)
|
||||
elif self.is_linux:
|
||||
return self._move_to_trash_linux(file_path)
|
||||
elif self.is_macos:
|
||||
return self._move_to_trash_macos(file_path)
|
||||
else:
|
||||
# Fallback: Direkte Löschung
|
||||
logger.warning(f"Unbekanntes System: {self.platform} - verwende direkte Löschung")
|
||||
return self._delete_permanently(file_path)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Verschieben in Papierkorb: {e}")
|
||||
return False
|
||||
|
||||
def _move_to_trash_windows(self, file_path: Path) -> bool:
|
||||
"""Windows-spezifische Papierkorb-Implementation"""
|
||||
try:
|
||||
# Verwende PowerShell für Windows-Papierkorb
|
||||
cmd = [
|
||||
'powershell', '-Command',
|
||||
f'Add-Type -AssemblyName Microsoft.VisualBasic; '
|
||||
f'[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile("{file_path}", '
|
||||
f'"OnlyErrorDialogs", "SendToRecycleBin")'
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
logger.info(f"Datei erfolgreich in Papierkorb verschoben: {file_path}")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"PowerShell-Fehler: {result.stderr}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Windows Papierkorb-Fehler: {e}")
|
||||
return False
|
||||
|
||||
def _move_to_trash_linux(self, file_path: Path) -> bool:
|
||||
"""Linux-spezifische Papierkorb-Implementation"""
|
||||
try:
|
||||
# Prüfe verfügbare Tools
|
||||
tools = ['gio', 'gvfs-trash', 'kioclient5', 'trash-put']
|
||||
|
||||
for tool in tools:
|
||||
if shutil.which(tool):
|
||||
if tool == 'gio':
|
||||
cmd = ['gio', 'trash', str(file_path)]
|
||||
elif tool == 'gvfs-trash':
|
||||
cmd = ['gvfs-trash', str(file_path)]
|
||||
elif tool == 'kioclient5':
|
||||
cmd = ['kioclient5', 'move', str(file_path), 'trash:/']
|
||||
elif tool == 'trash-put':
|
||||
cmd = ['trash-put', str(file_path)]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
logger.info(f"Datei erfolgreich in Papierkorb verschoben ({tool}): {file_path}")
|
||||
return True
|
||||
else:
|
||||
logger.warning(f"{tool} fehlgeschlagen: {result.stderr}")
|
||||
continue
|
||||
|
||||
# Fallback: Manueller Trash-Ordner
|
||||
return self._move_to_trash_manual_linux(file_path)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Linux Papierkorb-Fehler: {e}")
|
||||
return False
|
||||
|
||||
def _move_to_trash_manual_linux(self, file_path: Path) -> bool:
|
||||
"""Manueller Linux-Papierkorb nach XDG-Standard"""
|
||||
try:
|
||||
# XDG Trash-Verzeichnis ermitteln
|
||||
xdg_data_home = os.getenv('XDG_DATA_HOME')
|
||||
if not xdg_data_home:
|
||||
home = Path.home()
|
||||
xdg_data_home = home / '.local' / 'share'
|
||||
else:
|
||||
xdg_data_home = Path(xdg_data_home)
|
||||
|
||||
trash_dir = xdg_data_home / 'Trash'
|
||||
files_dir = trash_dir / 'files'
|
||||
info_dir = trash_dir / 'info'
|
||||
|
||||
# Erstelle Trash-Verzeichnisse
|
||||
files_dir.mkdir(parents=True, exist_ok=True)
|
||||
info_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Eindeutigen Namen generieren
|
||||
import time
|
||||
timestamp = int(time.time())
|
||||
trash_name = f"{file_path.name}_{timestamp}"
|
||||
|
||||
# Verschiebe Datei
|
||||
trash_file = files_dir / trash_name
|
||||
shutil.move(str(file_path), str(trash_file))
|
||||
|
||||
# Erstelle .trashinfo Datei
|
||||
info_file = info_dir / f"{trash_name}.trashinfo"
|
||||
info_content = f"""[Trash Info]
|
||||
Path={file_path.absolute()}
|
||||
DeletionDate={time.strftime('%Y-%m-%dT%H:%M:%S')}
|
||||
"""
|
||||
info_file.write_text(info_content)
|
||||
|
||||
logger.info(f"Datei manuell in Linux-Papierkorb verschoben: {file_path}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Manueller Linux-Papierkorb-Fehler: {e}")
|
||||
return False
|
||||
|
||||
def _move_to_trash_macos(self, file_path: Path) -> bool:
|
||||
"""macOS-spezifische Papierkorb-Implementation"""
|
||||
try:
|
||||
# Verwende osascript für macOS-Papierkorb
|
||||
cmd = [
|
||||
'osascript', '-e',
|
||||
f'tell application "Finder" to delete POSIX file "{file_path.absolute()}"'
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
logger.info(f"Datei erfolgreich in Papierkorb verschoben: {file_path}")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"osascript-Fehler: {result.stderr}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"macOS Papierkorb-Fehler: {e}")
|
||||
return False
|
||||
|
||||
def _delete_permanently(self, file_path: Path) -> bool:
|
||||
"""Permanente Löschung als Fallback"""
|
||||
try:
|
||||
if file_path.is_file():
|
||||
file_path.unlink()
|
||||
elif file_path.is_dir():
|
||||
shutil.rmtree(file_path)
|
||||
else:
|
||||
logger.warning(f"Unbekannter Dateityp: {file_path}")
|
||||
return False
|
||||
|
||||
logger.info(f"Datei permanent gelöscht: {file_path}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Permanente Löschung fehlgeschlagen: {e}")
|
||||
return False
|
||||
|
||||
def safe_delete(self, file_path: Union[str, Path], use_trash: bool = True) -> bool:
|
||||
"""
|
||||
Sichere Datei-Löschung mit konfigurierbarer Papierkorb-Nutzung
|
||||
|
||||
Args:
|
||||
file_path: Pfad zur Datei
|
||||
use_trash: True für Papierkorb, False für permanente Löschung
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich
|
||||
"""
|
||||
file_path = Path(file_path)
|
||||
|
||||
if not file_path.exists():
|
||||
logger.warning(f"Datei existiert nicht: {file_path}")
|
||||
return True # Bereits gelöscht = Erfolg
|
||||
|
||||
if use_trash:
|
||||
# Versuche Papierkorb zuerst
|
||||
if self.move_to_trash(file_path):
|
||||
return True
|
||||
else:
|
||||
logger.warning("Papierkorb fehlgeschlagen - verwende permanente Löschung")
|
||||
return self._delete_permanently(file_path)
|
||||
else:
|
||||
# Direkte permanente Löschung
|
||||
return self._delete_permanently(file_path)
|
||||
|
||||
def clean_temp_files(self, temp_dir: Union[str, Path], max_age_hours: int = 24) -> int:
|
||||
"""
|
||||
Bereinigt temporäre Dateien älter als max_age_hours
|
||||
|
||||
Args:
|
||||
temp_dir: Temporäres Verzeichnis
|
||||
max_age_hours: Maximales Alter in Stunden
|
||||
|
||||
Returns:
|
||||
int: Anzahl gelöschter Dateien
|
||||
"""
|
||||
temp_dir = Path(temp_dir)
|
||||
|
||||
if not temp_dir.exists():
|
||||
return 0
|
||||
|
||||
import time
|
||||
current_time = time.time()
|
||||
max_age_seconds = max_age_hours * 3600
|
||||
deleted_count = 0
|
||||
|
||||
try:
|
||||
for item in temp_dir.rglob('*'):
|
||||
if item.is_file():
|
||||
file_age = current_time - item.stat().st_mtime
|
||||
|
||||
if file_age > max_age_seconds:
|
||||
if self.safe_delete(item, use_trash=False):
|
||||
deleted_count += 1
|
||||
logger.debug(f"Temporäre Datei gelöscht: {item}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Bereinigen temporärer Dateien: {e}")
|
||||
|
||||
if deleted_count > 0:
|
||||
logger.info(f"{deleted_count} temporäre Dateien bereinigt")
|
||||
|
||||
return deleted_count
|
||||
|
||||
# Globale Instanz für einfache Nutzung
|
||||
file_handler = SafeFileHandler()
|
||||
|
||||
# Convenience-Funktionen
|
||||
def move_to_trash(file_path: Union[str, Path]) -> bool:
|
||||
"""Verschiebt Datei in Papierkorb"""
|
||||
return file_handler.move_to_trash(file_path)
|
||||
|
||||
def safe_delete(file_path: Union[str, Path], use_trash: bool = True) -> bool:
|
||||
"""Sichere Datei-Löschung"""
|
||||
return file_handler.safe_delete(file_path, use_trash)
|
||||
|
||||
def clean_temp_files(temp_dir: Union[str, Path], max_age_hours: int = 24) -> int:
|
||||
"""Bereinigt temporäre Dateien"""
|
||||
return file_handler.clean_temp_files(temp_dir, max_age_hours)
|
||||
|
||||
# Rückwärtskompatibilität mit Send2Trash API
|
||||
def send2trash(path: Union[str, Path]) -> None:
|
||||
"""
|
||||
Kompatibilitätsfunktion für Send2Trash
|
||||
|
||||
Args:
|
||||
path: Pfad zur Datei/zum Ordner
|
||||
|
||||
Raises:
|
||||
OSError: Bei Fehlern beim Löschen
|
||||
"""
|
||||
if not move_to_trash(path):
|
||||
raise OSError(f"Konnte Datei nicht in Papierkorb verschieben: {path}")
|
||||
|
||||
# Beispiel-Usage:
|
||||
if __name__ == "__main__":
|
||||
# Test der Funktionalität
|
||||
import tempfile
|
||||
|
||||
# Erstelle Testdatei
|
||||
with tempfile.NamedTemporaryFile(delete=False) as tmp:
|
||||
tmp.write(b"Test-Inhalt")
|
||||
test_file = tmp.name
|
||||
|
||||
print(f"Teste Papierkorb-Funktionalität mit: {test_file}")
|
||||
|
||||
# Teste Papierkorb
|
||||
if move_to_trash(test_file):
|
||||
print("✅ Datei erfolgreich in Papierkorb verschoben")
|
||||
else:
|
||||
print("❌ Papierkorb-Verschiebung fehlgeschlagen")
|
||||
|
||||
# Aufräumen falls Papierkorb nicht funktioniert
|
||||
if os.path.exists(test_file):
|
||||
os.unlink(test_file)
|
||||
print("🗑️ Datei direkt gelöscht")
|
@@ -117,9 +117,11 @@ OU = MYP Druckerverwaltung
|
||||
CN = localhost
|
||||
|
||||
[v3_req]
|
||||
keyUsage = keyEncipherment, dataEncipherment
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectAltName = @alt_names
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = critical, digitalSignature, keyEncipherment, keyAgreement
|
||||
extendedKeyUsage = critical, serverAuth, clientAuth
|
||||
subjectAltName = critical, @alt_names
|
||||
nsCertType = server
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = localhost
|
||||
|
Reference in New Issue
Block a user