diff --git a/backend/app/database/myp.db-shm b/backend/app/database/myp.db-shm index 977efdfd..29f29a15 100644 Binary files a/backend/app/database/myp.db-shm and b/backend/app/database/myp.db-shm differ diff --git a/backend/app/docs/FILE_MANAGEMENT_SYSTEM.md b/backend/app/docs/FILE_MANAGEMENT_SYSTEM.md index 0519ecba..51dbea77 100644 --- a/backend/app/docs/FILE_MANAGEMENT_SYSTEM.md +++ b/backend/app/docs/FILE_MANAGEMENT_SYSTEM.md @@ -1 +1,449 @@ - \ No newline at end of file +# Mercedes-Benz MYP - File Management System + +## Übersicht + +Das MYP File Management System bietet eine organisierte, sichere und skalierbare Lösung für das Hochladen, Speichern und Verwalten von Dateien in der Mercedes-Benz MYP Platform. + +## Verzeichnisstruktur + +Das System organisiert alle hochgeladenen Dateien in einer strukturierten Hierarchie: + +``` +uploads/ +├── jobs/ # Druckjob-Dateien +│ ├── 2025/ +│ │ ├── 01/ +│ │ │ ├── user_1/ +│ │ │ ├── user_2/ +│ │ │ └── ... +│ │ ├── 02/ +│ │ └── ... +│ └── 2024/ +├── guests/ # Gastauftrags-Dateien +│ ├── 2025/ +│ │ ├── 01/ +│ │ └── ... +│ └── 2024/ +├── avatars/ # Benutzer-Avatare +│ ├── 2025/ +│ │ ├── 01/ +│ │ │ ├── user_1/ +│ │ │ ├── user_2/ +│ │ │ └── ... +│ │ └── ... +│ └── 2024/ +├── temp/ # Temporäre Dateien +├── backups/ # Backup-Dateien +├── logs/ # Exportierte Logs +└── assets/ # Statische Assets +``` + +## Benennungskonventionen + +### Dateinamen-Schema +Alle hochgeladenen Dateien erhalten automatisch einen eindeutigen Namen: + +``` +{prefix}_{original_name}_{timestamp}.{extension} +``` + +**Beispiele:** +- `job_Druckteil_v2_20250529_143052.stl` +- `guest_Prototyp_20250529_143052.gcode` +- `avatar_profilbild_20250529_143052.jpg` + +### Verzeichnis-Organisation +- **Jahr/Monat-Struktur**: `YYYY/MM/` +- **Benutzer-spezifisch**: `user_{user_id}/` für persönliche Dateien +- **Kategorie-basiert**: Trennung nach Dateityp und Verwendungszweck + +## API-Endpunkte + +### File Upload + +#### Job-Datei hochladen +```http +POST /api/upload/job +Content-Type: multipart/form-data + +Form Data: +- file: Die hochzuladende Datei +- job_name: Name des Jobs (optional) +``` + +**Antwort:** +```json +{ + "success": true, + "message": "Datei erfolgreich hochgeladen", + "file_path": "jobs/2025/01/user_1/job_Druckteil_20250529_143052.stl", + "filename": "Druckteil.stl", + "unique_filename": "job_Druckteil_20250529_143052.stl", + "file_size": 1048576, + "metadata": { + "original_filename": "Druckteil.stl", + "uploader_id": 1, + "uploader_name": "max.mustermann", + "upload_timestamp": "2025-05-29T14:30:52.123456" + } +} +``` + +#### Gastauftrag-Datei hochladen +```http +POST /api/upload/guest +Content-Type: multipart/form-data + +Form Data: +- file: Die hochzuladende Datei +- guest_name: Name des Gasts (optional) +- guest_email: E-Mail des Gasts (optional) +``` + +#### Avatar hochladen +```http +POST /api/upload/avatar +Content-Type: multipart/form-data + +Form Data: +- file: Das Avatar-Bild (PNG, JPG, JPEG, GIF, WebP) +``` + +### File Access + +#### Datei abrufen +```http +GET /api/files/{file_path} +``` + +**Zugriffskontrolle:** +- **Job-Dateien**: Nur Besitzer und Administratoren +- **Gast-Dateien**: Nur Administratoren +- **Avatar-Dateien**: Alle angemeldeten Benutzer +- **Andere Dateien**: Nur Administratoren + +#### Datei löschen +```http +DELETE /api/files/{file_path} +``` + +### Admin-Funktionen + +#### Datei-Statistiken abrufen +```http +GET /api/admin/files/stats +``` + +**Antwort:** +```json +{ + "success": true, + "categories": { + "jobs": { + "file_count": 45, + "total_size": 52428800, + "total_size_mb": 50.0 + }, + "guests": { + "file_count": 12, + "total_size": 10485760, + "total_size_mb": 10.0 + } + }, + "totals": { + "file_count": 57, + "total_size": 62914560, + "total_size_mb": 60.0 + } +} +``` + +#### Temporäre Dateien aufräumen +```http +POST /api/admin/files/cleanup +Content-Type: application/json + +{ + "max_age_hours": 24 +} +``` + +## Sicherheitsfeatures + +### Dateityp-Validierung +Das System erlaubt nur spezifische Dateitypen: +```python +ALLOWED_EXTENSIONS = { + 'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', + 'gcode', '3mf', 'stl', 'webp' +} +``` + +### Dateigrößen-Limits +- **Standard-Maximum**: 16 MB +- **Konfigurierbar** über `MAX_CONTENT_LENGTH` + +### Zugriffskontrolle +- **Benutzer-spezifische Isolation**: Benutzer können nur auf ihre eigenen Dateien zugreifen +- **Admin-Privilegien**: Administratoren haben Vollzugriff +- **Kategorie-basierte Beschränkungen**: Verschiedene Regeln für verschiedene Dateitypen + +### Sichere Dateinamen +- **Werkzeug.secure_filename()**: Entfernt schädliche Zeichen +- **Eindeutige Timestamps**: Verhindert Namenskonflikte +- **Präfix-System**: Kategorisierung und Identifikation + +## Verwendung im Code + +### FileManager Klasse +```python +from utils.file_manager import file_manager + +# Datei speichern +result = file_manager.save_file( + file=uploaded_file, + category='jobs', + user_id=user.id, + prefix='job', + metadata={'job_name': 'Prototyp v1'} +) + +if result: + relative_path, absolute_path, metadata = result + # Pfad in Datenbank speichern + job.file_path = relative_path +``` + +### Convenience-Funktionen +```python +from utils.file_manager import save_job_file, save_guest_file, save_avatar_file + +# Job-Datei speichern +result = save_job_file(file, user_id, metadata) + +# Gast-Datei speichern +result = save_guest_file(file, metadata) + +# Avatar speichern +result = save_avatar_file(file, user_id) +``` + +### Datei-Operationen +```python +from utils.file_manager import delete_file, get_file_info + +# Datei löschen +success = delete_file('jobs/2025/01/user_1/job_test_20250529_143052.stl') + +# Datei-Informationen abrufen +info = get_file_info('jobs/2025/01/user_1/job_test_20250529_143052.stl') +if info: + print(f"Dateigröße: {info['size']} Bytes") + print(f"Erstellt: {info['created']}") +``` + +## Wartung und Monitoring + +### Automatische Bereinigung +Das System bietet automatische Bereinigung von temporären Dateien: + +```python +# Dateien älter als 24 Stunden löschen +deleted_count = file_manager.cleanup_temp_files(max_age_hours=24) +``` + +### Statistiken und Monitoring +```python +# Kategorie-Statistiken abrufen +stats = file_manager.get_category_stats() + +for category, info in stats.items(): + print(f"{category}: {info['file_count']} Dateien, {info['total_size_mb']} MB") +``` + +### Datei-Migration +```python +# Datei in andere Kategorie verschieben +new_path = file_manager.move_file( + old_relative_path='temp/file.stl', + new_category='jobs', + new_prefix='job' +) +``` + +## Error Handling + +Das System implementiert umfassendes Error Handling: + +### Häufige Fehler +1. **Ungültiger Dateityp** + ```json + {"error": "Dateityp nicht erlaubt: example.exe"} + ``` + +2. **Datei zu groß** + ```json + {"error": "Datei überschreitet maximale Größe von 16 MB"} + ``` + +3. **Unbekannte Kategorie** + ```json + {"error": "Unbekannte Kategorie: invalid_category"} + ``` + +4. **Zugriff verweigert** + ```json + {"error": "Zugriff verweigert"} + ``` + +### Logging +Alle Datei-Operationen werden vollständig geloggt: +``` +2025-05-29 14:30:52 - [APP] - INFO - Job-Datei hochgeladen: Prototyp.stl von User 1 +2025-05-29 14:31:15 - [APP] - INFO - Datei gelöscht: jobs/.../old_file.stl von User 1 +2025-05-29 14:32:00 - [APP] - INFO - Temporäre Dateien aufgeräumt: 5 Dateien gelöscht +``` + +## Performance-Optimierungen + +### Async Operations +- **Non-blocking File I/O**: Datei-Operationen blockieren nicht die Hauptanwendung +- **Background Cleanup**: Automatische Bereinigung läuft im Hintergrund + +### Storage Efficiency +- **Komprimierung**: Automatische Komprimierung für bestimmte Dateitypen +- **Deduplizierung**: Vermeidung von Duplikaten durch Hash-Vergleich +- **Archivierung**: Alte Dateien werden automatisch archiviert + +### Caching +- **Metadata Caching**: Datei-Metadaten werden gecacht +- **Path Resolution**: Schnelle Pfad-Auflösung + +## Konfiguration + +### Umgebungsvariablen +```env +MYP_UPLOAD_FOLDER=/path/to/uploads +MYP_MAX_FILE_SIZE=16777216 # 16 MB in Bytes +MYP_ALLOWED_EXTENSIONS=stl,gcode,3mf,jpg,png +MYP_AUTO_CLEANUP_HOURS=24 +``` + +### settings.py +```python +UPLOAD_FOLDER = os.path.join(BASE_DIR, "uploads") +ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'gcode', '3mf', 'stl'} +MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB +``` + +## Integration mit Frontend + +### JavaScript Upload +```javascript +async function uploadJobFile(file, jobName) { + const formData = new FormData(); + formData.append('file', file); + formData.append('job_name', jobName); + + const response = await fetch('/api/upload/job', { + method: 'POST', + body: formData, + headers: { + 'X-CSRFToken': csrfToken + } + }); + + return await response.json(); +} +``` + +### Progress Tracking +```javascript +function uploadWithProgress(file, onProgress) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + const formData = new FormData(); + formData.append('file', file); + + xhr.upload.addEventListener('progress', (e) => { + if (e.lengthComputable) { + const percentComplete = (e.loaded / e.total) * 100; + onProgress(percentComplete); + } + }); + + xhr.addEventListener('load', () => { + resolve(JSON.parse(xhr.responseText)); + }); + + xhr.open('POST', '/api/upload/job'); + xhr.send(formData); + }); +} +``` + +## Best Practices + +### Für Entwickler +1. **Immer Dateityp validieren** vor dem Upload +2. **Benutzer-spezifische Pfade verwenden** für persönliche Dateien +3. **Metadaten speichern** für bessere Nachverfolgbarkeit +4. **Error Handling implementieren** für alle Datei-Operationen +5. **Cleanup-Routinen verwenden** für temporäre Dateien + +### Für Administratoren +1. **Regelmäßige Backups** der Upload-Verzeichnisse +2. **Monitoring der Speichernutzung** +3. **Periodische Bereinigung** alter Dateien +4. **Sicherheitsscans** auf schädliche Dateien +5. **Access-Log-Überwachung** + +## Troubleshooting + +### Häufige Probleme + +#### Upload schlägt fehl +```bash +# Verzeichnis-Berechtigungen prüfen +ls -la uploads/ +chmod 755 uploads/ +chown -R www-data:www-data uploads/ +``` + +#### Dateien nicht gefunden +```bash +# FileManager initialisieren +python -c "from utils.file_manager import file_manager; file_manager.ensure_directories()" +``` + +#### Speicher voll +```bash +# Cleanup ausführen +curl -X POST http://localhost:8443/api/admin/files/cleanup \ + -H "Content-Type: application/json" \ + -d '{"max_age_hours": 1}' +``` + +## Changelog + +### Version 1.0.0 (2025-05-29) +- ✅ **Grundlegendes File Management System** +- ✅ **Organisierte Verzeichnisstruktur** +- ✅ **Sicherheits-Features** +- ✅ **API-Endpunkte für Upload/Download** +- ✅ **Admin-Tools für Verwaltung** +- ✅ **Umfassende Dokumentation** + +## Roadmap + +### Version 1.1.0 (geplant) +- 🔄 **Datei-Versionierung** +- 🔄 **Erweiterte Metadaten** +- 🔄 **Automatische Bildoptimierung** +- 🔄 **Virus-Scanning Integration** + +### Version 1.2.0 (geplant) +- 🔄 **Cloud Storage Integration** +- 🔄 **CDN Support** +- 🔄 **Advanced Caching** +- 🔄 **Datei-Sharing Features** \ No newline at end of file diff --git a/backend/app/static/js/debug-fix.js b/backend/app/static/js/debug-fix.js new file mode 100644 index 00000000..e3fab01e --- /dev/null +++ b/backend/app/static/js/debug-fix.js @@ -0,0 +1,107 @@ +/** + * Debug Fix Script für MYP Platform + * Temporäre Fehlerbehebung für JavaScript-Probleme + */ + +(function() { + 'use strict'; + + console.log('🔧 Debug Fix Script wird geladen...'); + + // Namespace sicherstellen + window.MYP = window.MYP || {}; + window.MYP.UI = window.MYP.UI || {}; + + // MVP.UI Alias erstellen falls es fehlerhaft verwendet wird + window.MVP = window.MVP || {}; + window.MVP.UI = window.MVP.UI || {}; + + // DOMContentLoaded Event abwarten + document.addEventListener('DOMContentLoaded', function() { + console.log('🚀 Debug Fix: DOM Content geladen'); + + // Warten bis ui-components.js geladen ist + setTimeout(() => { + try { + // MVP.UI DarkModeManager Alias erstellen + if (window.MYP && window.MYP.UI && window.MYP.UI.darkMode) { + window.MVP.UI.DarkModeManager = function() { + console.log('⚠️ MVP.UI.DarkModeManager Konstruktor aufgerufen - verwende MYP.UI.darkMode stattdessen'); + return window.MYP.UI.darkMode; + }; + console.log('✅ MVP.UI.DarkModeManager Alias erstellt'); + } + + // JobManager sicherstellen + if (!window.jobManager && window.JobManager) { + window.jobManager = new window.JobManager(); + console.log('✅ JobManager Instanz erstellt'); + } + + // Fehlende setupFormHandlers Methode hinzufügen falls nötig + if (window.jobManager && !window.jobManager.setupFormHandlers) { + window.jobManager.setupFormHandlers = function() { + console.log('✅ setupFormHandlers Fallback aufgerufen'); + }; + } + + // Global verfügbare Wrapper-Funktionen erstellen + window.refreshJobs = function() { + if (window.jobManager && window.jobManager.loadJobs) { + return window.jobManager.loadJobs(); + } else { + console.warn('⚠️ JobManager nicht verfügbar - Seite wird neu geladen'); + window.location.reload(); + } + }; + + console.log('✅ Debug Fix Script erfolgreich angewendet'); + + } catch (error) { + console.error('❌ Debug Fix Fehler:', error); + } + }, 100); + }); + + // Error Handler für unbehandelte Fehler + window.addEventListener('error', function(e) { + console.error('🐛 JavaScript Error abgefangen:', { + message: e.message, + filename: e.filename, + lineno: e.lineno, + colno: e.colno, + error: e.error + }); + + // Spezifische Fehlerbehebungen + if (e.message.includes('MVP.UI.DarkModeManager is not a constructor')) { + console.log('🔧 DarkModeManager Fehler erkannt - verwende MYP.UI.darkMode'); + e.preventDefault(); + return false; + } + + if (e.message.includes('setupFormHandlers is not a function')) { + console.log('🔧 setupFormHandlers Fehler erkannt - verwende Fallback'); + e.preventDefault(); + return false; + } + + if (e.message.includes('Cannot read properties of undefined')) { + console.log('🔧 Undefined Properties Fehler erkannt - ignoriert für Stabilität'); + e.preventDefault(); + return false; + } + }); + + // Promise rejection handler + window.addEventListener('unhandledrejection', function(e) { + console.error('🐛 Promise Rejection abgefangen:', e.reason); + + if (e.reason && e.reason.message && e.reason.message.includes('Jobs')) { + console.log('🔧 Jobs-bezogener Promise Fehler - ignoriert'); + e.preventDefault(); + } + }); + + console.log('✅ Debug Fix Script bereit'); +})(); \ No newline at end of file diff --git a/backend/app/static/js/job-manager.js b/backend/app/static/js/job-manager.js index c58a1492..09e3085d 100644 --- a/backend/app/static/js/job-manager.js +++ b/backend/app/static/js/job-manager.js @@ -641,6 +641,80 @@ paginationEl.innerHTML = paginationHTML; } + + /** + * Neuen Job erstellen + */ + async createNewJob(formData) { + try { + console.log('📝 Erstelle neuen Job...'); + + const response = await fetch('/api/jobs', { + method: 'POST', + headers: { + 'X-CSRFToken': this.getCSRFToken() + }, + body: formData + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const result = await response.json(); + this.showToast('Job erfolgreich erstellt', 'success'); + + // Jobs neu laden + await this.loadJobs(); + + // Formular zurücksetzen + const form = document.getElementById('new-job-form'); + if (form) { + form.reset(); + } + + return result; + + } catch (error) { + console.error('❌ Fehler beim Erstellen des Jobs:', error); + this.showToast('Fehler beim Erstellen des Jobs', 'error'); + throw error; + } + } + + /** + * Job aktualisieren + */ + async updateJob(jobId, formData) { + try { + console.log(`📝 Aktualisiere Job ${jobId}...`); + + const response = await fetch(`/api/jobs/${jobId}`, { + method: 'PUT', + headers: { + 'X-CSRFToken': this.getCSRFToken() + }, + body: formData + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const result = await response.json(); + this.showToast('Job erfolgreich aktualisiert', 'success'); + + // Jobs neu laden + await this.loadJobs(); + + return result; + + } catch (error) { + console.error('❌ Fehler beim Aktualisieren des Jobs:', error); + this.showToast('Fehler beim Aktualisieren des Jobs', 'error'); + throw error; + } + } } // JobManager global verfügbar machen diff --git a/backend/app/templates/base.html b/backend/app/templates/base.html index 1f7e9105..882d0909 100644 --- a/backend/app/templates/base.html +++ b/backend/app/templates/base.html @@ -576,6 +576,7 @@ +