better flyer icons + archive
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m25s
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m25s
This commit is contained in:
@@ -55,6 +55,17 @@ export const analyticsQueue = new Queue<AnalyticsJobData>('analytics-reporting',
|
||||
},
|
||||
});
|
||||
|
||||
export const cleanupQueue = new Queue<CleanupJobData>('file-cleanup', {
|
||||
connection,
|
||||
defaultJobOptions: {
|
||||
attempts: 3,
|
||||
backoff: {
|
||||
type: 'exponential',
|
||||
delay: 30000, // Retry cleanup after 30 seconds
|
||||
},
|
||||
removeOnComplete: true, // No need to keep successful cleanup jobs
|
||||
},
|
||||
});
|
||||
// --- Job Data Interfaces ---
|
||||
|
||||
interface FlyerJobData {
|
||||
@@ -78,6 +89,10 @@ interface AnalyticsJobData {
|
||||
reportDate: string; // e.g., '2024-10-26'
|
||||
}
|
||||
|
||||
interface CleanupJobData {
|
||||
flyerId: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main worker process for handling flyer jobs.
|
||||
* This should be run as a separate process.
|
||||
@@ -86,6 +101,8 @@ export const flyerWorker = new Worker<FlyerJobData>(
|
||||
'flyer-processing',
|
||||
async (job: Job<FlyerJobData>) => {
|
||||
const { filePath, originalFileName, checksum, userId } = job.data;
|
||||
const createdImagePaths: string[] = [];
|
||||
let jobSucceeded = false;
|
||||
logger.info(`[Worker] Processing job ${job.id} for file: ${originalFileName}`);
|
||||
|
||||
try {
|
||||
@@ -115,6 +132,9 @@ export const flyerWorker = new Worker<FlyerJobData>(
|
||||
|
||||
for (const img of generatedImages) {
|
||||
imagePaths.push({ path: path.join(outputDir, img), mimetype: 'image/jpeg' });
|
||||
const imagePath = path.join(outputDir, img);
|
||||
imagePaths.push({ path: imagePath, mimetype: 'image/jpeg' });
|
||||
createdImagePaths.push(imagePath); // Track generated images for cleanup
|
||||
}
|
||||
|
||||
logger.info(`[Worker] Converted PDF to ${imagePaths.length} images.`);
|
||||
@@ -156,6 +176,7 @@ export const flyerWorker = new Worker<FlyerJobData>(
|
||||
});
|
||||
|
||||
// TODO: Cleanup temporary files (original PDF and generated images)
|
||||
jobSucceeded = true; // Mark the job as successful before the finally block.
|
||||
|
||||
return { flyerId: newFlyer.flyer_id };
|
||||
} catch (error: unknown) {
|
||||
@@ -169,6 +190,26 @@ export const flyerWorker = new Worker<FlyerJobData>(
|
||||
await job.updateProgress({ message: `Error: ${errorMessage}` });
|
||||
// Re-throw the error to let BullMQ know the job has failed and should be retried or marked as failed.
|
||||
throw error;
|
||||
} finally {
|
||||
// This block will run after the try/catch, regardless of success or failure.
|
||||
if (jobSucceeded) {
|
||||
logger.info(`[Worker] Job ${job.id} succeeded. Cleaning up temporary files.`);
|
||||
try {
|
||||
// Delete the generated JPEG images from the PDF conversion.
|
||||
for (const imagePath of createdImagePaths) {
|
||||
await fs.unlink(imagePath);
|
||||
logger.debug(`[Worker] Deleted temporary image: ${imagePath}`);
|
||||
}
|
||||
// Finally, delete the original uploaded file (PDF or image).
|
||||
await fs.unlink(filePath);
|
||||
logger.debug(`[Worker] Deleted original upload: ${filePath}`);
|
||||
} catch (cleanupError) {
|
||||
logger.error(`[Worker] Job ${job.id} completed, but failed during file cleanup.`, { error: cleanupError });
|
||||
// We don't re-throw here because the main job was successful.
|
||||
}
|
||||
} else {
|
||||
logger.warn(`[Worker] Job ${job.id} failed. Temporary files will not be cleaned up to allow for manual inspection.`);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -236,4 +277,51 @@ export const analyticsWorker = new Worker<AnalyticsJobData>(
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* A dedicated worker for cleaning up flyer-related files from the filesystem.
|
||||
* This is triggered manually by an admin after a flyer has been reviewed.
|
||||
*/
|
||||
export const cleanupWorker = new Worker<CleanupJobData>(
|
||||
'file-cleanup',
|
||||
async (job: Job<CleanupJobData>) => {
|
||||
const { flyerId } = job.data;
|
||||
logger.info(`[CleanupWorker] Starting file cleanup for flyer ID: ${flyerId}`);
|
||||
|
||||
try {
|
||||
// 1. Fetch the flyer from the database to get its file paths.
|
||||
const flyer = await db.getFlyerById(flyerId);
|
||||
if (!flyer) {
|
||||
throw new Error(`Flyer with ID ${flyerId} not found. Cannot perform cleanup.`);
|
||||
}
|
||||
|
||||
// 2. Determine the base path for the flyer images.
|
||||
const storagePath = process.env.STORAGE_PATH || '/var/www/flyer-crawler.projectium.com/flyer-images';
|
||||
|
||||
// 3. Delete the main flyer image.
|
||||
const mainImagePath = path.join(storagePath, path.basename(flyer.image_url));
|
||||
await fs.unlink(mainImagePath).catch(err => logger.warn(`[CleanupWorker] Could not delete main image (may not exist): ${mainImagePath}`, { error: err.message }));
|
||||
logger.info(`[CleanupWorker] Deleted main image: ${mainImagePath}`);
|
||||
|
||||
// 4. Delete the flyer icon.
|
||||
if (flyer.icon_url) {
|
||||
const iconPath = path.join(storagePath, 'icons', path.basename(flyer.icon_url));
|
||||
await fs.unlink(iconPath).catch(err => logger.warn(`[CleanupWorker] Could not delete icon (may not exist): ${iconPath}`, { error: err.message }));
|
||||
logger.info(`[CleanupWorker] Deleted icon: ${iconPath}`);
|
||||
}
|
||||
|
||||
// Note: This process does not delete the original PDF, as its path is not stored.
|
||||
// A more advanced implementation could store the original path in the job data and pass it here.
|
||||
|
||||
} catch (error: unknown) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'An unknown cleanup error occurred';
|
||||
logger.error(`[CleanupWorker] Job ${job.id} for flyer ${flyerId} failed.`, { error: errorMessage });
|
||||
throw error; // Re-throw to let BullMQ handle the failure and retry.
|
||||
}
|
||||
},
|
||||
{
|
||||
connection,
|
||||
concurrency: 10, // Cleanup is not very resource-intensive.
|
||||
}
|
||||
);
|
||||
|
||||
logger.info('All workers started and listening for jobs.');
|
||||
Reference in New Issue
Block a user