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']);