feat: implement automatic purging of old media and schedule its daily execution via cron.

This commit is contained in:
Michael committed 2026-03-04 14:32:27 +01:00
1 parent f6376cb470
commit 60c830c88b
3 files changed
+85 -8

No files matched your search

+12
View File
@@ -1,6 +1,7 @@
import cron from 'node-cron';
import { TokenManager } from './services/token_manager';
import { AnalyticsService } from './services/analytics';
import { purgeOldMedia } from './services/media-purge';
export function startTokenCron() {
// Run every day at midnight (00:00) — Token health check
@@ -20,4 +21,15 @@ export function startTokenCron() {
}
});
console.log('[Cron] Analytics sync job scheduled (0 8,20 * * *).');
// Run every day at 03:00 AM — Cleanup old media from Cloudinary and DB
cron.schedule('0 3 * * *', async () => {
console.log('[Cron] Running daily media purge check...');
try {
await purgeOldMedia();
} catch (error) {
console.error('[Cron] Media purge check failed:', error);
}
});
console.log('[Cron] Media purge job scheduled (0 3 * * *).');
}
+9 -8
View File
@@ -339,12 +339,13 @@ reelsRouter.post('/reels/preview', async (req: Request, res: Response) => {
}
let watermarkUrl: string | undefined = undefined;
if (req.user) {
const user = req.user as User;
const [userCloudinary] = await db.select().from(cloudinaryConfig).where(eq(cloudinaryConfig.userId, user.id));
if (userCloudinary && userCloudinary.logoPublicId) {
watermarkUrl = `https://res.cloudinary.com/${userCloudinary.cloudName}/image/upload/${userCloudinary.logoPublicId}`;
try {
const config = await storage.getCloudinaryConfig();
if (config && config.logoPublicId) {
watermarkUrl = `https://res.cloudinary.com/${config.cloudName}/image/upload/${config.logoPublicId}`;
}
} catch (e) {
console.error('Error fetching watermark configuration', e);
}
// Traiter la vidéo via FFmpeg
@@ -549,9 +550,9 @@ async function processReelBackground(
let watermarkUrl: string | undefined = undefined;
try {
const [userCloudinary] = await db.select().from(cloudinaryConfig).where(eq(cloudinaryConfig.userId, media.userId));
if (userCloudinary && userCloudinary.logoPublicId) {
watermarkUrl = `https://res.cloudinary.com/${userCloudinary.cloudName}/image/upload/${userCloudinary.logoPublicId}`;
const config = await storage.getCloudinaryConfig();
if (config && config.logoPublicId) {
watermarkUrl = `https://res.cloudinary.com/${config.cloudName}/image/upload/${config.logoPublicId}`;
}
} catch (e) {
console.error('Error fetching watermark configuration', e);
+64
View File
@@ -0,0 +1,64 @@
import { db } from '../db';
import { media } from '@shared/schema';
import { eq, lt, isNotNull, and } from 'drizzle-orm';
import { cloudinaryService } from './cloudinary';
/**
* Automates the cleanup of old media to save Cloudinary storage space.
* Scans the database for media older than 60 days, deletes the physical
* files from Cloudinary, and removes the database records (cascading to post_media).
*/
export async function purgeOldMedia() {
console.log('[Media Purge] Starting automatic purge of old media...');
try {
const sixtyDaysAgo = new Date();
sixtyDaysAgo.setDate(sixtyDaysAgo.getDate() - 60);
// Find all media older than 60 days that have a cloudinaryPublicId
const oldMedia = await db.query.media.findMany({
where: (media, { and, lt, isNotNull }) => and(
lt(media.createdAt, sixtyDaysAgo),
isNotNull(media.cloudinaryPublicId)
)
});
if (oldMedia.length === 0) {
console.log('[Media Purge] No old media found to purge.');
return;
}
console.log(`[Media Purge] Found ${oldMedia.length} media items older than 60 days to purge.`);
let successCount = 0;
let errorCount = 0;
for (const item of oldMedia) {
if (!item.cloudinaryPublicId) continue;
try {
// Delete from Cloudinary (using global config, userId is passed but ignored internally)
await cloudinaryService.deleteMedia(
item.cloudinaryPublicId,
item.userId,
item.type === 'video' ? 'video' : 'image'
);
// Delete from Database (cascades to post_media, effectively leaving the post text intact)
await db.delete(media).where(eq(media.id, item.id));
successCount++;
console.log(`[Media Purge] Successfully deleted media ${item.id} (Cloudinary ID: ${item.cloudinaryPublicId})`);
} catch (err) {
// Cloudinary file might already be deleted or missing, but we still might want to clean DB if it's consistently failing.
// For safety, we only delete DB row if Cloudinary deletion succeeds or throws 'not found' (which we assume is fine).
console.error(`[Media Purge] Failed to delete media ${item.id} from Cloudinary:`, err);
errorCount++;
}
}
console.log(`[Media Purge] Purge completed. Successfully deleted: ${successCount}, Failed: ${errorCount}.`);
} catch (error) {
console.error('[Media Purge] Fatal error during media purge process:', error);
}
}