diff --git a/backend/src/constants.js b/backend/src/constants.js index 66a5991..12a89a0 100644 --- a/backend/src/constants.js +++ b/backend/src/constants.js @@ -2,6 +2,7 @@ const endpoints = { UPLOAD_STATIC_DIRECTORY: '/upload', UPLOAD_FILE: '/upload', UPLOAD_BATCH: '/upload/batch', + PREVIEW_STATIC_DIRECTORY: '/previews', DOWNLOAD_FILE: '/download/:id', GET_GROUP: '/groups/:groupId', GET_ALL_GROUPS: '/groups', diff --git a/backend/src/repositories/GroupRepository.js b/backend/src/repositories/GroupRepository.js index c4f9527..718c17d 100644 --- a/backend/src/repositories/GroupRepository.js +++ b/backend/src/repositories/GroupRepository.js @@ -22,8 +22,8 @@ class GroupRepository { if (groupData.images && groupData.images.length > 0) { for (const image of groupData.images) { await db.run(` - INSERT INTO images (group_id, file_name, original_name, file_path, upload_order, file_size, mime_type) - VALUES (?, ?, ?, ?, ?, ?, ?) + INSERT INTO images (group_id, file_name, original_name, file_path, upload_order, file_size, mime_type, preview_path) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) `, [ groupData.groupId, image.fileName, @@ -31,7 +31,8 @@ class GroupRepository { image.filePath, image.uploadOrder, image.fileSize || null, - image.mimeType || null + image.mimeType || null, + image.previewPath || null ]); } } @@ -67,6 +68,7 @@ class GroupRepository { fileName: img.file_name, originalName: img.original_name, filePath: img.file_path, + previewPath: img.preview_path, uploadOrder: img.upload_order, fileSize: img.file_size, mimeType: img.mime_type diff --git a/backend/src/routes/batchUpload.js b/backend/src/routes/batchUpload.js index b794f9b..c8ee91c 100644 --- a/backend/src/routes/batchUpload.js +++ b/backend/src/routes/batchUpload.js @@ -5,6 +5,7 @@ const { endpoints } = require('../constants'); const UploadGroup = require('../models/uploadGroup'); const GroupRepository = require('../repositories/GroupRepository'); const dbManager = require('../database/DatabaseManager'); +const ImagePreviewService = require('../services/ImagePreviewService'); const router = Router(); @@ -64,6 +65,37 @@ router.post(endpoints.UPLOAD_BATCH, async (req, res) => { }); } + // Generate previews for all uploaded images asynchronously + const previewDir = path.join(__dirname, '..', require('../constants').PREVIEW_FS_DIR); + const uploadDir = path.join(__dirname, '..', require('../constants').UPLOAD_FS_DIR); + + // Generate previews in background (don't wait) + ImagePreviewService.generatePreviewsForGroup( + processedFiles.map(f => ({ file_name: f.fileName, file_path: `/upload/${f.fileName}` })), + uploadDir, + previewDir + ).then(results => { + const successCount = results.filter(r => r.success).length; + console.log(`Preview generation completed: ${successCount}/${results.length} successful`); + + // Update preview_path in database for successful previews + results.forEach(async (result) => { + if (result.success) { + try { + await dbManager.run(` + UPDATE images + SET preview_path = ? + WHERE group_id = ? AND file_name = ? + `, [result.previewPath, group.groupId, result.fileName]); + } catch (err) { + console.error(`Failed to update preview_path for ${result.fileName}:`, err); + } + } + }); + }).catch(err => { + console.error('Preview generation failed:', err); + }); + // Speichere Gruppe in SQLite await GroupRepository.createGroup({ groupId: group.groupId, diff --git a/backend/src/routes/upload.js b/backend/src/routes/upload.js index d4876be..c03ee24 100644 --- a/backend/src/routes/upload.js +++ b/backend/src/routes/upload.js @@ -1,15 +1,19 @@ const generateId = require("shortid"); const express = require('express'); const { Router } = require('express'); -const { endpoints, UPLOAD_FS_DIR } = require('../constants'); +const { endpoints, UPLOAD_FS_DIR, PREVIEW_FS_DIR } = require('../constants'); const path = require('path'); +const ImagePreviewService = require('../services/ImagePreviewService'); const router = Router(); // Serve uploaded images via URL /upload but store files under data/images router.use(endpoints.UPLOAD_STATIC_DIRECTORY, express.static( path.join(__dirname, '..', UPLOAD_FS_DIR) )); -router.post(endpoints.UPLOAD_FILE, (req, res) => { +// Serve preview images via URL /previews but store files under data/previews +router.use(endpoints.PREVIEW_STATIC_DIRECTORY, express.static( path.join(__dirname, '..', PREVIEW_FS_DIR) )); + +router.post(endpoints.UPLOAD_FILE, async (req, res) => { if(req.files === null){ console.log('No file uploaded'); return res.status(400).json({ msg: 'No file uploaded' }); @@ -22,14 +26,40 @@ router.post(endpoints.UPLOAD_FILE, (req, res) => { fileName = generateId() + '.' + fileEnding const savePath = path.join(__dirname, '..', UPLOAD_FS_DIR, fileName); - file.mv(savePath, err => { - if(err) { - console.error(err); - return res.status(500).send(err); - } + + try { + // Save the uploaded file + await new Promise((resolve, reject) => { + file.mv(savePath, err => { + if(err) reject(err); + else resolve(); + }); + }); - res.json({ filePath: `${endpoints.UPLOAD_STATIC_DIRECTORY}/${fileName}`}); - }); + // Generate preview asynchronously (don't wait for it) + const previewFileName = ImagePreviewService._getPreviewFileName(fileName); + const previewPath = ImagePreviewService.getPreviewPath(previewFileName); + + ImagePreviewService.generatePreview(savePath, previewPath) + .then(result => { + if (!result.success) { + console.warn(`Preview generation failed for ${fileName}:`, result.error); + } + }) + .catch(err => { + console.error(`Unexpected error during preview generation for ${fileName}:`, err); + }); + + // Return immediately with file path + res.json({ + filePath: `${endpoints.UPLOAD_STATIC_DIRECTORY}/${fileName}`, + fileName: fileName + }); + + } catch(err) { + console.error(err); + return res.status(500).send(err); + } }); module.exports = router; \ No newline at end of file diff --git a/backend/src/utils/groupFormatter.js b/backend/src/utils/groupFormatter.js index 6b8dd60..8ec5a02 100644 --- a/backend/src/utils/groupFormatter.js +++ b/backend/src/utils/groupFormatter.js @@ -29,6 +29,7 @@ function formatGroupDetail(groupRow, images) { fileName: img.file_name, originalName: img.original_name, filePath: img.file_path, + previewPath: img.preview_path || null, uploadOrder: img.upload_order, fileSize: img.file_size || null, mimeType: img.mime_type || null