carouselforge / src /lib /scheduler /executor.ts
CarouselForge Developer
fix: resolve TypeScript and test configuration issues for Phase 13
9a43362
// 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 [];
}