This commit is contained in:
Giancarmine Salucci
2025-12-21 02:03:05 +01:00
parent 167cd1f4bb
commit 9357bd483a
36 changed files with 6251 additions and 1547 deletions

182
src/lib/server/scheduler.ts Normal file
View File

@@ -0,0 +1,182 @@
import fs from 'fs';
import path from 'path';
import { getBrowser } from './browser';
export interface SchedulerConfig {
enabled: boolean;
intervalHours: number;
}
interface SchedulerState {
intervalId: NodeJS.Timer | null;
lastRenewalTime: number | null;
isRenewing: boolean;
}
const state: SchedulerState = {
intervalId: null,
lastRenewalTime: null,
isRenewing: false
};
/**
* Get scheduler configuration from environment variables
*/
function getConfig(): SchedulerConfig {
const enabled = process.env.AUTH_SCHEDULER_ENABLED === 'true';
const intervalHours = parseInt(process.env.AUTH_SCHEDULER_INTERVAL_HOURS || '12', 10);
return {
enabled,
intervalHours
};
}
/**
* Resolve authentication storage path
*/
function resolveAuthPath(): string {
const authPathDocker = '/app/secrets/auth.json';
const authPathLocal = './secrets/auth.json';
if (fs.existsSync(authPathDocker)) {
return authPathDocker;
}
if (fs.existsSync(authPathLocal)) {
return authPathLocal;
}
// Default to local path if neither exists yet
return authPathLocal;
}
/**
* Renew Instagram authentication by loading existing auth and refreshing the session
* Inspired by gen-auth.js - reuses existing stored credentials without manual input
*/
async function renewInstagramAuth(): Promise<boolean> {
if (state.isRenewing) {
console.log('[Scheduler] Auth renewal already in progress, skipping');
return false;
}
const authPath = resolveAuthPath();
if (!fs.existsSync(authPath)) {
console.warn('[Scheduler] No existing auth.json found. Run gen-auth.js first to set up initial authentication.');
return false;
}
state.isRenewing = true;
try {
console.log('[Scheduler] Starting Instagram authentication renewal...');
console.log(`[Scheduler] Loading existing auth from: ${authPath}`);
const browser = await getBrowser();
// Load existing authentication state
const context = await browser.newContext({ storageState: authPath });
const page = await context.newPage();
// Navigate to Instagram homepage - the existing auth will be used automatically
await page.goto('https://www.instagram.com/', { waitUntil: 'domcontentloaded' });
// Wait for the "Home" icon to appear (indicates successful login)
try {
await page.waitForSelector('svg[aria-label="Home"]', { timeout: 30000 });
console.log('[Scheduler] Successfully authenticated with Instagram');
} catch (e) {
console.warn('[Scheduler] Home icon not found - session may be expired or invalid');
await page.close();
await context.close();
state.isRenewing = false;
return false;
}
// Save the refreshed authentication state
const authDir = path.dirname(authPath);
// Ensure directory exists
if (!fs.existsSync(authDir)) {
fs.mkdirSync(authDir, { recursive: true });
}
// Update auth.json with refreshed session
await context.storageState({ path: authPath });
await page.close();
await context.close();
state.lastRenewalTime = Date.now();
console.log(`[Scheduler] Instagram authentication renewed successfully at ${new Date().toISOString()}`);
console.log(`[Scheduler] Auth state updated at: ${authPath}`);
return true;
} catch (error) {
console.error('[Scheduler] Instagram authentication renewal failed:', error);
return false;
} finally {
state.isRenewing = false;
}
}
/**
* Start the authentication renewal scheduler
*/
export async function startScheduler(): Promise<void> {
const config = getConfig();
if (!config.enabled) {
console.log('[Scheduler] Authentication scheduler is disabled (set AUTH_SCHEDULER_ENABLED=true to enable)');
return;
}
if (state.intervalId !== null) {
console.warn('[Scheduler] Scheduler is already running');
return;
}
const intervalMs = config.intervalHours * 60 * 60 * 1000;
console.log(`[Scheduler] Starting authentication scheduler with ${config.intervalHours}h interval`);
// Schedule periodic renewals
state.intervalId = setInterval(async () => {
await renewInstagramAuth();
}, intervalMs);
// Ensure interval is not blocking (set it as unreferenceable so it doesn't keep the process alive)
if (state.intervalId.unref) {
state.intervalId.unref();
}
// Optional: Perform initial renewal on startup (uncomment to enable)
// await renewInstagramAuth();
}
/**
* Stop the authentication renewal scheduler
*/
export async function stopScheduler(): Promise<void> {
if (state.intervalId === null) {
console.log('[Scheduler] Scheduler is not running');
return;
}
console.log('[Scheduler] Stopping authentication scheduler...');
clearInterval(state.intervalId);
state.intervalId = null;
}
/**
* Get scheduler status information
*/
export function getSchedulerStatus() {
return {
running: state.intervalId !== null,
lastRenewalTime: state.lastRenewalTime ? new Date(state.lastRenewalTime).toISOString() : null,
isRenewing: state.isRenewing,
config: getConfig()
};
}