🎉 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:
1
backend/COMMON_ERRORS.md
Normal file
1
backend/COMMON_ERRORS.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
@@ -74,7 +74,7 @@ psutil
|
|||||||
|
|
||||||
# ===== FILE SYSTEM OPERATIONS =====
|
# ===== FILE SYSTEM OPERATIONS =====
|
||||||
watchdog
|
watchdog
|
||||||
Send2Trash
|
# Send2Trash - Problematisch auf einigen Systemen, wird durch native Lösung ersetzt
|
||||||
|
|
||||||
# ===== DATA VALIDATION =====
|
# ===== DATA VALIDATION =====
|
||||||
cerberus
|
cerberus
|
||||||
|
189
backend/setup.sh
189
backend/setup.sh
@@ -1358,47 +1358,190 @@ EOF
|
|||||||
log "✅ Robuste Python-Umgebung installiert"
|
log "✅ Robuste Python-Umgebung installiert"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create_clean_requirements() {
|
||||||
|
log "=== ERSTELLE BEREINIGTE REQUIREMENTS.TXT ==="
|
||||||
|
|
||||||
|
local original_req="$CURRENT_DIR/requirements.txt"
|
||||||
|
local clean_req="$CURRENT_DIR/requirements_clean.txt"
|
||||||
|
|
||||||
|
# Problematische Pakete, die übersprungen werden sollen
|
||||||
|
local problematic_packages=(
|
||||||
|
"Send2Trash"
|
||||||
|
"pywin32"
|
||||||
|
"wmi"
|
||||||
|
"RPi.GPIO"
|
||||||
|
"python-magic-bin"
|
||||||
|
)
|
||||||
|
|
||||||
|
progress "Erstelle bereinigte requirements.txt..."
|
||||||
|
|
||||||
|
# Erstelle neue requirements.txt ohne problematische Pakete
|
||||||
|
> "$clean_req" # Datei leeren
|
||||||
|
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
local skip_line=false
|
||||||
|
local package_name=$(echo "$line" | sed 's/[<>=!].*//' | xargs)
|
||||||
|
|
||||||
|
# Überspringe leere Zeilen und Kommentare
|
||||||
|
if [[ -z "${line// }" ]] || [[ "$line" =~ ^[[:space:]]*# ]]; then
|
||||||
|
echo "$line" >> "$clean_req"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prüfe ob Paket problematisch ist
|
||||||
|
for problematic in "${problematic_packages[@]}"; do
|
||||||
|
if [[ "$package_name" == "$problematic" ]]; then
|
||||||
|
echo "# ÜBERSPRUNGEN (problematisch): $line" >> "$clean_req"
|
||||||
|
log "⚠️ Überspringe problematisches Paket: $package_name"
|
||||||
|
skip_line=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Spezielle Behandlung für plattformspezifische Pakete
|
||||||
|
if [[ "$line" =~ "sys_platform" ]]; then
|
||||||
|
# Prüfe Plattform-Kompatibilität
|
||||||
|
if [[ "$line" =~ "win32" ]] && [[ "$(uname -s)" != "CYGWIN"* ]] && [[ "$(uname -s)" != "MINGW"* ]]; then
|
||||||
|
echo "# ÜBERSPRUNGEN (Windows): $line" >> "$clean_req"
|
||||||
|
log "ℹ️ Überspringe Windows-spezifisches Paket: $package_name"
|
||||||
|
skip_line=true
|
||||||
|
elif [[ "$line" =~ "linux" ]] && [[ "$(uname -s)" != "Linux" ]]; then
|
||||||
|
echo "# ÜBERSPRUNGEN (Linux): $line" >> "$clean_req"
|
||||||
|
log "ℹ️ Überspringe Linux-spezifisches Paket: $package_name"
|
||||||
|
skip_line=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Füge Zeile hinzu wenn nicht übersprungen
|
||||||
|
if [ "$skip_line" = false ]; then
|
||||||
|
echo "$line" >> "$clean_req"
|
||||||
|
fi
|
||||||
|
|
||||||
|
done < "$original_req"
|
||||||
|
|
||||||
|
log "✅ Bereinigte requirements.txt erstellt: $clean_req"
|
||||||
|
|
||||||
|
# Zeige Statistiken
|
||||||
|
local original_count=$(grep -v -E '^[[:space:]]*#|^[[:space:]]*$' "$original_req" | wc -l)
|
||||||
|
local clean_count=$(grep -v -E '^[[:space:]]*#|^[[:space:]]*$' "$clean_req" | wc -l)
|
||||||
|
log "📊 Pakete: $original_count → $clean_count ($(($original_count - $clean_count)) übersprungen)"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_essential_packages_fallback() {
|
||||||
|
log "=== INSTALLIERE ESSENTIELLE PAKETE (FALLBACK) ==="
|
||||||
|
|
||||||
|
# Minimale Liste essentieller Pakete für MYP
|
||||||
|
local essential_packages=(
|
||||||
|
"Flask>=2.0.0"
|
||||||
|
"Werkzeug>=2.0.0"
|
||||||
|
"Jinja2>=3.0.0"
|
||||||
|
"requests>=2.25.0"
|
||||||
|
"SQLAlchemy>=1.4.0"
|
||||||
|
"Flask-Login>=0.6.0"
|
||||||
|
"Flask-WTF>=1.0.0"
|
||||||
|
"WTForms>=3.0.0"
|
||||||
|
"cryptography>=3.4.0"
|
||||||
|
"bcrypt>=3.2.0"
|
||||||
|
"psutil>=5.8.0"
|
||||||
|
"python-dateutil>=2.8.0"
|
||||||
|
"click>=8.0.0"
|
||||||
|
"colorlog>=6.6.0"
|
||||||
|
"watchdog>=2.1.0"
|
||||||
|
"schedule>=1.1.0"
|
||||||
|
"Pillow>=8.3.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
progress "Installiere essentielle Pakete einzeln..."
|
||||||
|
|
||||||
|
local install_count=0
|
||||||
|
local total_count=${#essential_packages[@]}
|
||||||
|
|
||||||
|
for package in "${essential_packages[@]}"; do
|
||||||
|
local package_name=$(echo "$package" | sed 's/[<>=!].*//')
|
||||||
|
progress "Installiere $package_name... ($((++install_count))/$total_count)"
|
||||||
|
|
||||||
|
# Mehrere Installationsstrategien
|
||||||
|
if python3 -m pip install "$package" --break-system-packages --no-cache-dir >/dev/null 2>&1; then
|
||||||
|
log "✅ $package_name erfolgreich installiert"
|
||||||
|
elif python3 -m pip install "$package_name" --break-system-packages --no-cache-dir >/dev/null 2>&1; then
|
||||||
|
log "✅ $package_name erfolgreich installiert (ohne Version)"
|
||||||
|
elif python3 -m pip install "$package" --user --no-cache-dir >/dev/null 2>&1; then
|
||||||
|
log "✅ $package_name erfolgreich installiert (user)"
|
||||||
|
else
|
||||||
|
warning "⚠️ $package_name Installation fehlgeschlagen"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log "✅ Essentieller Pakete Installation abgeschlossen"
|
||||||
|
}
|
||||||
|
|
||||||
install_python_packages() {
|
install_python_packages() {
|
||||||
log "=== PYTHON-PAKETE INSTALLATION ==="
|
log "=== ROBUSTE PYTHON-PAKETE INSTALLATION ==="
|
||||||
|
|
||||||
progress "Installiere Python-Pakete..."
|
progress "Installiere Python-Pakete..."
|
||||||
|
|
||||||
if [ ! -f "$CURRENT_DIR/requirements.txt" ]; then
|
if [ ! -f "$CURRENT_DIR/requirements.txt" ]; then
|
||||||
error "requirements.txt nicht gefunden: $CURRENT_DIR/requirements.txt"
|
error "requirements.txt nicht gefunden: $CURRENT_DIR/requirements.txt"
|
||||||
|
install_essential_packages_fallback
|
||||||
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Kopiere requirements.txt
|
# Kopiere requirements.txt
|
||||||
cp "$CURRENT_DIR/requirements.txt" "$APP_DIR/" 2>/dev/null || true
|
cp "$CURRENT_DIR/requirements.txt" "$APP_DIR/" 2>/dev/null || true
|
||||||
|
|
||||||
# Installiere alle Pakete aus requirements.txt
|
# Erstelle bereinigte requirements.txt
|
||||||
progress "Installiere requirements.txt..."
|
create_clean_requirements
|
||||||
|
|
||||||
if python3 -m pip install -r "$CURRENT_DIR/requirements.txt" --break-system-packages; then
|
# Installiere bereinigte Pakete
|
||||||
success "✅ requirements.txt erfolgreich installiert"
|
progress "Installiere bereinigte requirements.txt..."
|
||||||
|
|
||||||
|
local install_success=false
|
||||||
|
|
||||||
|
# Strategie 1: Bereinigte requirements.txt mit --break-system-packages
|
||||||
|
if python3 -m pip install -r "$CURRENT_DIR/requirements_clean.txt" --break-system-packages --no-cache-dir; then
|
||||||
|
install_success=true
|
||||||
|
success "✅ Bereinigte requirements.txt erfolgreich installiert"
|
||||||
else
|
else
|
||||||
error "❌ requirements.txt Installation fehlgeschlagen"
|
warning "⚠️ Bereinigte requirements.txt Installation fehlgeschlagen"
|
||||||
return 1
|
|
||||||
|
# Strategie 2: Essentielle Pakete einzeln installieren
|
||||||
|
warning "Verwende Fallback: Essentielle Pakete einzeln installieren"
|
||||||
|
install_essential_packages_fallback
|
||||||
|
install_success=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Aufräumen
|
||||||
|
rm -f "$CURRENT_DIR/requirements_clean.txt" 2>/dev/null || true
|
||||||
|
|
||||||
# Validiere essenzielle Module
|
# Validiere essenzielle Module
|
||||||
progress "Validiere essenzielle Python-Module..."
|
progress "Validiere essenzielle Python-Module..."
|
||||||
|
|
||||||
local essential_modules=("flask" "requests")
|
local essential_modules=("flask" "requests" "werkzeug" "jinja2" "sqlalchemy")
|
||||||
local validation_success=true
|
local validation_success=true
|
||||||
|
|
||||||
for module in "${essential_modules[@]}"; do
|
for module in "${essential_modules[@]}"; do
|
||||||
if python3 -c "import $module; print(f'✅ $module verfügbar')" 2>/dev/null; then
|
if python3 -c "import $module; print(f'✅ $module verfügbar')" 2>/dev/null; then
|
||||||
debug "$module erfolgreich importiert"
|
debug "$module erfolgreich importiert"
|
||||||
else
|
else
|
||||||
warning "⚠️ $module nicht verfügbar"
|
warning "⚠️ $module nicht verfügbar - versuche nachinstallation..."
|
||||||
|
python3 -m pip install "$module" --break-system-packages --no-cache-dir >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Finale Validierung
|
||||||
|
for module in "${essential_modules[@]}"; do
|
||||||
|
if python3 -c "import $module" 2>/dev/null; then
|
||||||
|
log "✅ $module verfügbar"
|
||||||
|
else
|
||||||
|
warning "⚠️ $module fehlt"
|
||||||
validation_success=false
|
validation_success=false
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "$validation_success" = true ]; then
|
if [ "$validation_success" = true ]; then
|
||||||
success "✅ Essenzielle Python-Module verfügbar"
|
success "✅ Alle essentiellen Python-Module verfügbar"
|
||||||
else
|
else
|
||||||
warning "⚠️ Einige essenzielle Module fehlen"
|
warning "⚠️ Einige essenzielle Module fehlen - Fallback verwendet"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "✅ Python-Pakete Installation abgeschlossen"
|
log "✅ Python-Pakete Installation abgeschlossen"
|
||||||
@@ -1407,7 +1550,7 @@ install_python_packages() {
|
|||||||
progress "Zeige installierte Python-Pakete..."
|
progress "Zeige installierte Python-Pakete..."
|
||||||
echo ""
|
echo ""
|
||||||
echo "📦 Installierte Python-Pakete:"
|
echo "📦 Installierte Python-Pakete:"
|
||||||
python3 -m pip list 2>/dev/null | grep -E "(Flask|requests|Werkzeug|Jinja2)" | head -10 || echo " Keine relevanten Pakete gefunden"
|
python3 -m pip list 2>/dev/null | grep -E "(Flask|requests|Werkzeug|Jinja2|SQLAlchemy)" | head -10 || echo " Keine relevanten Pakete gefunden"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1438,8 +1581,14 @@ install_python_packages_with_break_system() {
|
|||||||
else
|
else
|
||||||
warning "⚠️ Strategie 1 fehlgeschlagen, versuche Alternative..."
|
warning "⚠️ Strategie 1 fehlgeschlagen, versuche Alternative..."
|
||||||
|
|
||||||
# Strategie 2: Einzelne Pakete installieren
|
# Strategie 2: Bereinigte Einzelinstallation
|
||||||
progress "Installiere Pakete einzeln..."
|
progress "Installiere bereinigte Pakete einzeln..."
|
||||||
|
|
||||||
|
# Erstelle bereinigte requirements.txt falls nicht vorhanden
|
||||||
|
if [ ! -f "$CURRENT_DIR/requirements_clean.txt" ]; then
|
||||||
|
create_clean_requirements
|
||||||
|
fi
|
||||||
|
|
||||||
while IFS= read -r package || [ -n "$package" ]; do
|
while IFS= read -r package || [ -n "$package" ]; do
|
||||||
# Überspringe Kommentare und leere Zeilen
|
# Überspringe Kommentare und leere Zeilen
|
||||||
if [[ "$package" =~ ^[[:space:]]*# ]] || [[ -z "${package// }" ]]; then
|
if [[ "$package" =~ ^[[:space:]]*# ]] || [[ -z "${package// }" ]]; then
|
||||||
@@ -1450,14 +1599,18 @@ install_python_packages_with_break_system() {
|
|||||||
package=$(echo "$package" | xargs)
|
package=$(echo "$package" | xargs)
|
||||||
|
|
||||||
if [ -n "$package" ]; then
|
if [ -n "$package" ]; then
|
||||||
progress "Installiere: $package"
|
local package_name=$(echo "$package" | sed 's/[<>=!].*//')
|
||||||
|
progress "Installiere: $package_name"
|
||||||
if python3.11 -m pip install "$package" --break-system-packages --no-cache-dir; then
|
if python3.11 -m pip install "$package" --break-system-packages --no-cache-dir; then
|
||||||
debug "✅ $package erfolgreich installiert"
|
debug "✅ $package_name erfolgreich installiert"
|
||||||
else
|
else
|
||||||
warning "⚠️ $package Installation fehlgeschlagen"
|
warning "⚠️ $package_name Installation fehlgeschlagen"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done < "$CURRENT_DIR/requirements.txt"
|
done < "$CURRENT_DIR/requirements_clean.txt"
|
||||||
|
|
||||||
|
# Aufräumen
|
||||||
|
rm -f "$CURRENT_DIR/requirements_clean.txt" 2>/dev/null || true
|
||||||
install_success=true
|
install_success=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
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
|
CN = localhost
|
||||||
|
|
||||||
[v3_req]
|
[v3_req]
|
||||||
keyUsage = keyEncipherment, dataEncipherment
|
basicConstraints = CA:FALSE
|
||||||
extendedKeyUsage = serverAuth
|
keyUsage = critical, digitalSignature, keyEncipherment, keyAgreement
|
||||||
subjectAltName = @alt_names
|
extendedKeyUsage = critical, serverAuth, clientAuth
|
||||||
|
subjectAltName = critical, @alt_names
|
||||||
|
nsCertType = server
|
||||||
|
|
||||||
[alt_names]
|
[alt_names]
|
||||||
DNS.1 = localhost
|
DNS.1 = localhost
|
||||||
|
Reference in New Issue
Block a user