// src/lib/scheduler/executor.ts import { getScheduledCarousels, updateScheduledCarousel } from './scheduler'; import type { ScheduledCarousel } from '@/types/schedule'; import type { PlatformConfig } from '@/types/platform'; const POLL_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes let executorRunning = false; /** * Start polling for scheduled carousels that are due * Runs in background, checks every 5 minutes */ export function startScheduleExecutor(): void { if (executorRunning) { console.log('[executor] already running'); return; } executorRunning = true; console.log('[executor] started'); // Run immediately, then at interval void executeScheduledCarousels(); setInterval(executeScheduledCarousels, POLL_INTERVAL_MS); } export function stopScheduleExecutor(): void { executorRunning = false; console.log('[executor] stopped'); } /** * Check for carousels that are due (scheduledFor <= now) * Publish them, update status */ async function executeScheduledCarousels(): Promise { try { const now = new Date().toISOString(); const pending = await getScheduledCarousels('pending'); const due = pending.filter(c => c.scheduledFor <= now); if (due.length === 0) { console.log('[executor] no carousels due'); return; } console.log(`[executor] found ${due.length} carousels to publish`); for (const carousel of due) { await publishScheduledCarousel(carousel); } } catch (error) { console.error('[executor] error executing scheduled carousels:', error); } } /** * Publish a single scheduled carousel */ async function publishScheduledCarousel(scheduled: ScheduledCarousel): Promise { try { console.log(`[executor] publishing carousel ${scheduled.id}`); // Fetch carousel images from storage/DB // For now, assume carousel exists in DB with images // Phase 10+ will add image storage const carouselImages = await fetchCarouselImages(scheduled.carouselId); if (!carouselImages || carouselImages.length === 0) { throw new Error('carousel has no images'); } // Publish to selected platforms // Using direct import to avoid circular dependency const { publish } = await import('@/lib/publisher/publish'); const platformConfigs: PlatformConfig[] = scheduled.platforms.map(p => ({ platform: p as any })); const result = await publish( carouselImages, platformConfigs ); if (result.success) { await updateScheduledCarousel(scheduled.id, { status: 'published' }); console.log(`[executor] published carousel ${scheduled.id}`); } else { await updateScheduledCarousel(scheduled.id, { status: 'failed', error: result.error, }); console.error(`[executor] failed to publish carousel ${scheduled.id}:`, result.error); } } catch (error) { const errorMsg = error instanceof Error ? error.message : String(error); await updateScheduledCarousel(scheduled.id, { status: 'failed', error: errorMsg, }); console.error(`[executor] error publishing carousel ${scheduled.id}:`, error); } } /** * Placeholder: fetch carousel images from DB * This will be implemented in Phase 10 (image storage) * For now, return empty array */ async function fetchCarouselImages(carouselId: string): Promise { // TODO: implement image storage in Phase 10 console.log(`[executor] fetchCarouselImages stub for ${carouselId} - returning empty`); return []; }