From 0ea3ee837ad69cc9422aa4235af7fa0ffddcb39a Mon Sep 17 00:00:00 2001 From: paul Date: Thu, 3 Jul 2025 16:37:14 +0200 Subject: [PATCH] Add archive service for automatic ZIP creation --- backend/src/services/archiveService.js | 73 ++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 backend/src/services/archiveService.js diff --git a/backend/src/services/archiveService.js b/backend/src/services/archiveService.js new file mode 100644 index 0000000..b00c34d --- /dev/null +++ b/backend/src/services/archiveService.js @@ -0,0 +1,73 @@ +const archiver = require('archiver'); +const fs = require('fs').promises; +const path = require('path'); +const { db } = require('../database/db'); +const logger = require('../utils/logger'); + +const ACTIVE_PATH = path.join(__dirname, '../../../storage/events/active'); +const ARCHIVE_PATH = path.join(__dirname, '../../../storage/events/archived'); + +async function archiveEvent(event) { + try { + const eventPath = path.join(ACTIVE_PATH, event.slug); + const archiveName = `${event.slug}.zip`; + const archivePath = path.join(ARCHIVE_PATH, archiveName); + + // Ensure archive directory exists + await fs.mkdir(ARCHIVE_PATH, { recursive: true }); + + // Create archive + const output = require('fs').createWriteStream(archivePath); + const archive = archiver('zip', { + zlib: { level: 9 } // Maximum compression + }); + + archive.on('error', (err) => { + throw err; + }); + + output.on('close', async () => { + logger.info(`Archive created: ${archiveName} (${archive.pointer()} bytes)`); + + // Update database + await db('events').where('id', event.id).update({ + is_archived: true, + archive_path: path.relative(path.join(__dirname, '../../../storage'), archivePath), + archived_at: new Date() + }); + + // Delete original files + await fs.rm(eventPath, { recursive: true }); + + // Delete thumbnails + const photos = await db('photos').where('event_id', event.id); + for (const photo of photos) { + if (photo.thumbnail_path) { + const thumbPath = path.join(__dirname, '../../../storage', photo.thumbnail_path); + await fs.unlink(thumbPath).catch(() => {}); // Ignore if already deleted + } + } + + // Queue completion email + await db('email_queue').insert({ + event_id: event.id, + recipient_email: event.admin_email, + email_type: 'archive_complete', + email_data: JSON.stringify({ + event_name: event.event_name, + archive_size: archive.pointer() + }) + }); + }); + + archive.pipe(output); + archive.directory(eventPath, false); + await archive.finalize(); + + } catch (error) { + logger.error(`Error archiving event ${event.slug}:`, error); + throw error; + } +} + +module.exports = { archiveEvent };