171 lines
4.5 KiB
TypeScript
171 lines
4.5 KiB
TypeScript
import * as cron from "node-cron";
|
|
import { logger } from "../server.js";
|
|
import { S3Sync, createS3SyncFromEnv } from "./s3Sync.js";
|
|
|
|
export class DailyS3Scheduler {
|
|
private s3Sync: S3Sync | null = null;
|
|
private cronJob: cron.ScheduledTask | null = null;
|
|
|
|
constructor() {
|
|
this.s3Sync = createS3SyncFromEnv();
|
|
}
|
|
|
|
/**
|
|
* Start the daily S3 sync scheduler
|
|
* Runs at midnight PST (00:00 PST = 08:00 UTC during standard time, 07:00 UTC during daylight time)
|
|
*/
|
|
async start(): Promise<void> {
|
|
if (!this.s3Sync) {
|
|
logger.warn("S3 sync not configured. Skipping scheduler setup.");
|
|
return;
|
|
}
|
|
|
|
// Test S3 connection before starting scheduler
|
|
// const connectionTest = await this.s3Sync.testConnection();
|
|
// if (!connectionTest) {
|
|
// logger.error("S3 connection test failed. S3 sync scheduler will not be started.");
|
|
// return;
|
|
// }
|
|
|
|
// Cron expression for midnight PST
|
|
// Note: This uses PST timezone. During PDT (daylight time), it will still run at midnight local time
|
|
const cronExpression = "0 0 * * *"; // Every day at midnight
|
|
const timezone = "America/Los_Angeles"; // PST/PDT timezone
|
|
|
|
this.cronJob = cron.schedule(
|
|
cronExpression,
|
|
async () => {
|
|
//await this.performDailySync();
|
|
await this.triggerJobAnalysis();
|
|
},
|
|
{
|
|
timezone: timezone
|
|
}
|
|
);
|
|
|
|
logger.info(`Daily scheduler started. Will run at midnight PST/PDT.`);
|
|
logger.info(`Next sync scheduled for: ${this.getNextRunTime()}`);
|
|
}
|
|
|
|
/**
|
|
* Stop the scheduler
|
|
*/
|
|
stop(): void {
|
|
if (this.cronJob) {
|
|
this.cronJob.stop();
|
|
this.cronJob = null;
|
|
logger.info("Daily S3 sync scheduler stopped.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform the daily sync operation
|
|
*/
|
|
private async performDailySync(): Promise<void> {
|
|
if (!this.s3Sync) {
|
|
logger.error("S3 sync not available for daily sync");
|
|
return;
|
|
}
|
|
|
|
const startTime = new Date();
|
|
logger.info(`Starting daily S3 sync at ${startTime.toISOString()}`);
|
|
|
|
try {
|
|
await this.s3Sync.syncJobsToS3();
|
|
const endTime = new Date();
|
|
const duration = endTime.getTime() - startTime.getTime();
|
|
logger.info(`Daily S3 sync completed successfully in ${duration}ms`);
|
|
} catch (error) {
|
|
logger.error("Daily S3 sync failed:", error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Manually trigger a sync (useful for testing)
|
|
*/
|
|
async triggerManualSync(): Promise<void> {
|
|
if (!this.s3Sync) {
|
|
logger.error("S3 sync not configured");
|
|
return;
|
|
}
|
|
|
|
logger.info("Triggering manual S3 sync...");
|
|
await this.performDailySync();
|
|
}
|
|
|
|
async triggerJobAnalysis(): Promise<void> {
|
|
if (!this.s3Sync) {
|
|
logger.error("S3 sync not configured");
|
|
return;
|
|
}
|
|
|
|
logger.info("Triggering jobs directory analysis...");
|
|
try {
|
|
const analysis = await this.s3Sync.analyzeJobsDirectory();
|
|
logger.info("Jobs directory analysis completed:", analysis);
|
|
} catch (error) {
|
|
logger.error("Jobs directory analysis failed:", error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the next scheduled run time
|
|
*/
|
|
private getNextRunTime(): string {
|
|
if (!this.cronJob) {
|
|
return "Not scheduled";
|
|
}
|
|
|
|
// Create a date object for midnight PST today
|
|
const now = new Date();
|
|
const pstNow = new Date(now.toLocaleString("en-US", { timeZone: "America/Los_Angeles" }));
|
|
|
|
// If it's past midnight today, next run is tomorrow at midnight
|
|
const nextRun = new Date(pstNow);
|
|
if (pstNow.getHours() > 0 || pstNow.getMinutes() > 0 || pstNow.getSeconds() > 0) {
|
|
nextRun.setDate(nextRun.getDate() + 1);
|
|
}
|
|
nextRun.setHours(0, 0, 0, 0);
|
|
|
|
return nextRun.toLocaleString("en-US", {
|
|
timeZone: "America/Los_Angeles",
|
|
weekday: "long",
|
|
year: "numeric",
|
|
month: "long",
|
|
day: "numeric",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
timeZoneName: "short"
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get scheduler status
|
|
*/
|
|
async getStatus(): Promise<{
|
|
isConfigured: boolean;
|
|
isRunning: boolean;
|
|
nextRun: string;
|
|
syncStats?: { bucketName: string; region: string; keyPrefix: string; available: boolean };
|
|
}> {
|
|
let syncStats;
|
|
if (this.s3Sync) {
|
|
try {
|
|
syncStats = await this.s3Sync.getSyncStats();
|
|
} catch (error) {
|
|
logger.error("Failed to get sync stats:", error);
|
|
}
|
|
}
|
|
|
|
return {
|
|
isConfigured: this.s3Sync !== null,
|
|
isRunning: this.cronJob !== null,
|
|
nextRun: this.getNextRunTime(),
|
|
syncStats
|
|
};
|
|
}
|
|
}
|
|
|
|
// Export a singleton instance
|
|
export const dailyS3Scheduler = new DailyS3Scheduler();
|