← back to SmokeShop
lib/scheduler.js
87 lines
/**
* Scheduler — node-cron based posting scheduler
* Checks every 15 minutes for due posts and publishes them
*/
const cron = require('node-cron');
const path = require('path');
const ig = require('./instagram-api');
let db = null;
let cronJob = null;
function init(database) {
db = database;
}
function getSetting(key) {
const row = db.prepare('SELECT value FROM settings WHERE key = ?').get(key);
return row ? row.value : null;
}
async function processDuePosts() {
const now = new Date().toISOString();
const duePosts = db.prepare(
"SELECT * FROM posts WHERE status = 'scheduled' AND scheduled_at <= ? ORDER BY scheduled_at ASC"
).all(now);
if (duePosts.length === 0) return;
const accessToken = getSetting('ig_access_token');
const igAccountId = getSetting('ig_business_account_id');
const appUrl = getSetting('app_url') || process.env.APP_URL || 'http://45.61.58.125:8401';
if (!accessToken || !igAccountId) {
console.log(`[Scheduler] ${duePosts.length} posts due but Instagram not connected`);
return;
}
for (const post of duePosts) {
try {
// The image must be publicly accessible for Meta Graph API
const imageUrl = `${appUrl}/generated/${path.basename(post.image_path || '')}`;
if (!post.image_path) {
console.log(`[Scheduler] Post ${post.id} has no image, skipping`);
db.prepare("UPDATE posts SET status = 'failed' WHERE id = ?").run(post.id);
continue;
}
const result = await ig.publishPost(igAccountId, accessToken, imageUrl, post.caption);
db.prepare(
"UPDATE posts SET status = 'posted', posted_at = ?, ig_media_id = ? WHERE id = ?"
).run(new Date().toISOString(), result.mediaId, post.id);
console.log(`[Scheduler] Posted ${post.id}: "${post.title}" → IG media ${result.mediaId}`);
} catch (err) {
console.error(`[Scheduler] Failed to post ${post.id}:`, err.message);
db.prepare("UPDATE posts SET status = 'failed' WHERE id = ?").run(post.id);
}
}
}
function start() {
if (cronJob) return;
// Run every 15 minutes
cronJob = cron.schedule('*/15 * * * *', async () => {
console.log('[Scheduler] Checking for due posts...');
try {
await processDuePosts();
} catch (err) {
console.error('[Scheduler] Error:', err.message);
}
});
console.log('[Scheduler] Started — checking every 15 minutes');
}
function stop() {
if (cronJob) {
cronJob.stop();
cronJob = null;
console.log('[Scheduler] Stopped');
}
}
module.exports = { init, start, stop, processDuePosts };