From 33d374304e18c4f4b710cf5e1f2e8829f893178d Mon Sep 17 00:00:00 2001 From: SEB Fotografie - soeren Date: Wed, 12 Nov 2025 11:20:12 +0100 Subject: [PATCH] =?UTF-8?q?Download-Button=20erg=C3=A4nzt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/DownloadController.php | 64 ++++++++++++++++++++ resources/js/Components/ImageContextMenu.vue | 15 ++++- resources/js/Pages/Home.vue | 37 +++++++++++ resources/lang/de/api.php | 3 + resources/lang/en/api.php | 3 + routes/api.php | 2 + 6 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 app/Http/Controllers/DownloadController.php diff --git a/app/Http/Controllers/DownloadController.php b/app/Http/Controllers/DownloadController.php new file mode 100644 index 0000000..3166293 --- /dev/null +++ b/app/Http/Controllers/DownloadController.php @@ -0,0 +1,64 @@ +validate([ + 'image_path' => 'required|string', + ]); + + $imagePath = $request->input('image_path'); + + // Check if it's a relative path and make it absolute + if (!filter_var($imagePath, FILTER_VALIDATE_URL)) { + $imagePath = public_path(str_replace(url('/'), '', $imagePath)); + } + + // Validate file exists + if (!file_exists($imagePath)) { + Log::error("DownloadController: Image file not found at {$imagePath}"); + return response()->json(['error' => 'Image file not found.'], 404); + } + + // Get file info + $fileName = basename($imagePath); + $fileExtension = pathinfo($fileName, PATHINFO_EXTENSION); + $mimeType = $this->getMimeType($fileExtension); + + try { + Log::info("DownloadController: Serving download for {$imagePath}"); + + // Return the file with proper headers for download + return response()->download($imagePath, $fileName, [ + 'Content-Type' => $mimeType, + 'Content-Disposition' => 'attachment; filename="' . $fileName . '"', + ]); + + } catch (\Exception $e) { + Log::error("DownloadController: Error serving download: " . $e->getMessage()); + return response()->json(['error' => 'Failed to serve download.'], 500); + } + } + + private function getMimeType(string $extension): string + { + $mimeTypes = [ + 'jpg' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'png' => 'image/png', + 'gif' => 'image/gif', + 'webp' => 'image/webp', + 'bmp' => 'image/bmp', + 'svg' => 'image/svg+xml', + ]; + + return $mimeTypes[strtolower($extension)] ?? 'application/octet-stream'; + } +} \ No newline at end of file diff --git a/resources/js/Components/ImageContextMenu.vue b/resources/js/Components/ImageContextMenu.vue index 9c38d5c..ec226a6 100644 --- a/resources/js/Components/ImageContextMenu.vue +++ b/resources/js/Components/ImageContextMenu.vue @@ -21,6 +21,10 @@ Stil ändern + { + const hostname = window.location.hostname; + return hostname !== 'localhost' && + hostname !== '127.0.0.1' && + !hostname.startsWith('192.168.') && + !hostname.startsWith('10.') && + !hostname.startsWith('172.'); +}; const showStyleSelectorView = ref(false); diff --git a/resources/js/Pages/Home.vue b/resources/js/Pages/Home.vue index 01a18c3..001eab3 100644 --- a/resources/js/Pages/Home.vue +++ b/resources/js/Pages/Home.vue @@ -25,6 +25,7 @@ :image="selectedImage" @close="currentOverlayComponent = null; selectedImage = null" @print="printImage" + @download="downloadImage" @changeStyle="showStyleSelector" @styleSelected="applyStyle" /> @@ -144,6 +145,42 @@ const printImage = () => { currentOverlayComponent.value = 'printQuantityModal'; }; +const downloadImage = (image) => { + console.log('Starting download for image:', image); + + // Show loading message + showError('Download wird vorbereitet...'); + + // Use axios to make a POST request to trigger backend download + axios.post('/api/download-image', { + image_path: image.path, + }, { + responseType: 'blob' // Important for file downloads + }) + .then(response => { + // Create blob URL from response + const blob = new Blob([response.data]); + const url = window.URL.createObjectURL(blob); + + // Create temporary link and trigger download + const link = document.createElement('a'); + link.href = url; + link.download = image.name || 'image'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + // Clean up blob URL + window.URL.revokeObjectURL(url); + + showError('Download gestartet!'); + }) + .catch(error => { + console.error('Error downloading image:', error); + showError(error.response?.data?.error || 'Download fehlgeschlagen.'); + }); +}; + const handlePrintConfirmed = (quantity) => { console.log(`Printing ${quantity} copies of image:`, selectedImage.value); currentOverlayComponent.value = null; // Close the modal diff --git a/resources/lang/de/api.php b/resources/lang/de/api.php index 1c871b0..aaf8efb 100644 --- a/resources/lang/de/api.php +++ b/resources/lang/de/api.php @@ -24,4 +24,7 @@ return [ 'api.print_dialog.quantity_prompt' => 'Wie viele Kopien möchten Sie drucken?', 'api.print_dialog.cancel_button' => 'Abbrechen', 'api.print_dialog.print_button' => 'Drucken', + 'api.download_button' => 'Herunterladen', + 'api.download_success' => 'Download gestartet!', + 'api.download_error' => 'Download fehlgeschlagen.', ]; diff --git a/resources/lang/en/api.php b/resources/lang/en/api.php index b3455b0..6f09f66 100644 --- a/resources/lang/en/api.php +++ b/resources/lang/en/api.php @@ -24,4 +24,7 @@ return [ 'print_dialog.quantity_prompt' => 'How many copies would you like to print?', 'print_dialog.cancel_button' => 'Cancel', 'print_dialog.print_button' => 'Print', + 'download_button' => 'Download', + 'download_success' => 'Download started!', + 'download_error' => 'Download failed.', ]; diff --git a/routes/api.php b/routes/api.php index 9c6b70d..e6aeb11 100644 --- a/routes/api.php +++ b/routes/api.php @@ -42,4 +42,6 @@ Route::middleware('auth:sanctum')->group(function () { Route::get('/images/fetch-styled/{prompt_id}', [ImageController::class, 'fetchStyledImage']); use App\Http\Controllers\PrintController; +use App\Http\Controllers\DownloadController; Route::post('/print-image', [PrintController::class, 'printImage']); +Route::post('/download-image', [DownloadController::class, 'downloadImage']);