File size: 3,513 Bytes
49316e9
 
 
9a43362
49316e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf2fa9e
9a43362
bf2fa9e
 
9a43362
bf2fa9e
49316e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// 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 [];
}