From 128452f580de2771032e0a036c7dbb5091bf2e33 Mon Sep 17 00:00:00 2001 From: paul Date: Thu, 3 Jul 2025 16:34:19 +0200 Subject: [PATCH] Add file watcher service --- backend/src/services/fileWatcher.js | 84 +++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 backend/src/services/fileWatcher.js diff --git a/backend/src/services/fileWatcher.js b/backend/src/services/fileWatcher.js new file mode 100644 index 0000000..a9e064d --- /dev/null +++ b/backend/src/services/fileWatcher.js @@ -0,0 +1,84 @@ +const chokidar = require('chokidar'); +const path = require('path'); +const fs = require('fs').promises; +const { db } = require('../database/db'); +const { generateThumbnail } = require('./imageProcessor'); +const logger = require('../utils/logger'); + +const WATCH_PATH = path.join(__dirname, '../../../storage/events/active'); + +function startFileWatcher() { + const watcher = chokidar.watch(WATCH_PATH, { + ignored: /(^|[\/\\])\../, // ignore dotfiles + persistent: true, + awaitWriteFinish: { + stabilityThreshold: 2000, + pollInterval: 100 + } + }); + + watcher + .on('add', async (filePath) => { + try { + await processNewPhoto(filePath); + } catch (error) { + logger.error('Error processing new photo:', error); + } + }) + .on('unlink', async (filePath) => { + try { + await removePhoto(filePath); + } catch (error) { + logger.error('Error removing photo:', error); + } + }); + + logger.info('File watcher started'); +} + +async function processNewPhoto(filePath) { + const relativePath = path.relative(WATCH_PATH, filePath); + const pathParts = relativePath.split(path.sep); + + if (pathParts.length < 2) return; // Not in correct folder structure + + const eventSlug = pathParts[0]; + const photoType = pathParts[1] === 'collages' ? 'collage' : 'individual'; + + // Check if this is an image file + const ext = path.extname(filePath).toLowerCase(); + if (!['.jpg', '.jpeg', '.png', '.webp'].includes(ext)) return; + + // Find the event + const event = await db('events').where({ slug: eventSlug, is_active: true }).first(); + if (!event) return; + + // Get file stats + const stats = await fs.stat(filePath); + + // Generate thumbnail + const thumbnailPath = await generateThumbnail(filePath); + + // Add to database + await db('photos').insert({ + event_id: event.id, + filename: path.basename(filePath), + path: relativePath, + thumbnail_path: thumbnailPath, + type: photoType, + size_bytes: stats.size + }); + + logger.info(`Added new photo: ${relativePath}`); +} + +async function removePhoto(filePath) { + const relativePath = path.relative(WATCH_PATH, filePath); + + // Remove from database + await db('photos').where({ path: relativePath }).delete(); + + logger.info(`Removed photo: ${relativePath}`); +} + +module.exports = { startFileWatcher };