@@ -6,34 +6,39 @@ import { snakeCase } from '@stacksjs/strings'
6
6
7
7
interface JobSchedule {
8
8
jobName : string
9
- times : string [ ]
10
- rate : string ,
11
- path : string ,
9
+ rate : string
10
+ path : string
11
+ nextRunTime : string | null
12
12
}
13
13
14
14
const scheduleFile = path . storagePath ( 'framework/core/scheduler/src/schedules/jobSchedule.json' )
15
15
16
- export async function runScheduler ( threshold = 3 , regenerationCount = 10 ) : Promise < void > {
17
- const now = new Date ( ) . toISOString ( )
16
+ export async function runScheduler ( ) : Promise < void > {
17
+ const now = new Date ( )
18
18
let schedules = loadSchedule ( )
19
19
20
20
const jobFiles = globSync ( [ path . appPath ( 'Jobs/*.ts' ) ] , { absolute : true } )
21
+
22
+ // Process job files and initialize schedules if missing
21
23
for ( const jobFile of jobFiles ) {
22
24
try {
23
25
const jobModule = await import ( jobFile )
24
26
const job = jobModule . default as JobOptions
25
-
26
- const jobName = snakeCase ( job . name || 'UnkownJob' )
27
+ const jobName = snakeCase ( job . name || 'UnknownJob' )
27
28
28
29
if ( job . rate ) {
29
- if ( ! schedules . some ( schedule => schedule . jobName === jobName ) ) {
30
- const initialSchedule = generateSchedule ( jobName || '' , getJobInterval ( job . rate ) , regenerationCount )
30
+ const existingSchedule = schedules . find ( schedule => schedule . jobName === jobName )
31
+
32
+ if ( ! existingSchedule ) {
33
+ // Prefill: Set the initial nextRunTime based on job's rate and current time
34
+ const intervalMinutes = getJobInterval ( job . rate )
35
+ const nextRunTime = new Date ( now . getTime ( ) + intervalMinutes * 60000 ) . toISOString ( )
31
36
32
37
schedules . push ( {
33
- jobName : jobName || '' ,
38
+ jobName,
34
39
rate : job . rate ,
35
- times : initialSchedule . times ,
36
- path : jobFile
40
+ nextRunTime , // Set nextRunTime here
41
+ path : jobFile ,
37
42
} )
38
43
}
39
44
}
@@ -42,31 +47,28 @@ export async function runScheduler(threshold = 3, regenerationCount = 10): Promi
42
47
}
43
48
}
44
49
50
+ // Process schedules and run jobs as needed
45
51
for ( const schedule of schedules ) {
46
- const dueTimes = schedule . times . filter ( time => time <= now )
52
+ const nextRunTime = new Date ( schedule . nextRunTime || now . toISOString ( ) )
53
+ const intervalMinutes = getJobInterval ( schedule . rate )
47
54
48
- // Run due jobs
49
- for ( const time of dueTimes ) {
50
- console . log ( `Running job: ${ schedule . jobName } at scheduled time: ${ time } ` )
51
- await runJob ( schedule . path )
52
- }
55
+ const isDue = isDueToRun ( nextRunTime , now )
53
56
54
- // Remove executed times
55
- schedule . times = schedule . times . filter ( time => time > now )
56
-
57
- // Regenerate schedule if below threshold
58
- if ( schedule . times . length < threshold ) {
59
- console . log ( `Regenerating schedule for job: ${ schedule . jobName } ` )
60
- const lastTime = new Date ( schedule . times [ schedule . times . length - 1 ] || now )
61
- const intervalMinutes = getJobInterval ( schedule . rate )
62
- const newSchedule = generateSchedule ( schedule . jobName , intervalMinutes , regenerationCount , lastTime )
63
-
64
- schedule . times . push ( ...newSchedule . times )
57
+ if ( isDue ) {
58
+ console . log ( `Running job: ${ schedule . jobName } at ${ now . toISOString ( ) } ` )
59
+ await runJob ( schedule . path )
60
+
61
+ // Refresh the next run time based on the interval
62
+ schedule . nextRunTime = new Date ( nextRunTime . getTime ( ) + intervalMinutes * 60000 ) . toISOString ( )
65
63
}
66
64
}
67
65
68
- // Save updated schedule
69
- saveSchedule ( schedules . filter ( schedule => schedule . times . length > 0 ) )
66
+ // Save updated schedules
67
+ saveSchedule ( schedules )
68
+ }
69
+
70
+ function isDueToRun ( nextRunTime : Date , currentDate : Date ) : boolean {
71
+ return currentDate >= nextRunTime
70
72
}
71
73
72
74
export function getJobInterval ( rate : string ) : number {
@@ -83,6 +85,18 @@ export function getJobInterval(rate: string): number {
83
85
return 15
84
86
case Every . ThirtyMinutes :
85
87
return 30
88
+ case Every . HalfHour :
89
+ return 30
90
+ case Every . Hour :
91
+ return 60
92
+ case Every . Day :
93
+ return 60 * 24 // 1 day
94
+ case Every . Week :
95
+ return 60 * 24 * 7 // 1 week
96
+ case Every . Month :
97
+ return 60 * 24 * 30 // Approximate 1 month (30 days)
98
+ case Every . Year :
99
+ return 60 * 24 * 365 // Approximate 1 year (365 days)
86
100
default :
87
101
throw new Error ( `Unsupported rate: ${ rate } ` )
88
102
}
@@ -106,16 +120,4 @@ function saveSchedule(schedule: JobSchedule[]): void {
106
120
function loadSchedule ( ) : JobSchedule [ ] {
107
121
if ( ! fs . existsSync ( scheduleFile ) ) return [ ]
108
122
return JSON . parse ( fs . readFileSync ( scheduleFile , 'utf-8' ) )
109
- }
110
-
111
- export function generateSchedule ( jobName : string , intervalMinutes : number , count : number , startTime = new Date ( ) ) : { jobName : string ; times : string [ ] } {
112
- const times : string [ ] = [ ]
113
- let nextTime = new Date ( startTime )
114
-
115
- for ( let i = 0 ; i < count ; i ++ ) {
116
- nextTime . setMinutes ( nextTime . getMinutes ( ) + intervalMinutes )
117
- times . push ( nextTime . toISOString ( ) )
118
- }
119
-
120
- return { jobName, times }
121
- }
123
+ }
0 commit comments