Spaces:
Sleeping
Sleeping
| // 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<void> { | |
| 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<void> { | |
| 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<string[]> { | |
| // TODO: implement image storage in Phase 10 | |
| console.log(`[executor] fetchCarouselImages stub for ${carouselId} - returning empty`); | |
| return []; | |
| } | |