Skip to content

Commit 5245473

Browse files
authored
fix(server): add validation for cron expression (#815)
1 parent 6e80f0c commit 5245473

File tree

6 files changed

+157
-46
lines changed

6 files changed

+157
-46
lines changed

server/package-lock.json

+92-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@prisma/client": "^4.9.0",
4343
"class-validator": "^0.14.0",
4444
"compression": "^1.7.4",
45+
"cron-validate": "^1.4.5",
4546
"database-proxy": "^0.8.2",
4647
"dotenv": "^16.0.3",
4748
"fast-json-patch": "^3.1.1",

server/src/trigger/cron-job.service.ts

+46-33
Original file line numberDiff line numberDiff line change
@@ -30,42 +30,47 @@ export class CronJobService {
3030
const batchApi = this.clusterService.makeBatchV1Api(region)
3131
const name = `cron-${trigger.id}`
3232
const command = await this.getTriggerCommand(trigger)
33-
const res = await batchApi.createNamespacedCronJob(ns, {
34-
metadata: {
35-
name,
36-
labels: {
37-
appid,
38-
id: trigger.id,
33+
const res = await batchApi
34+
.createNamespacedCronJob(ns, {
35+
metadata: {
36+
name,
37+
labels: {
38+
appid,
39+
id: trigger.id,
40+
},
3941
},
40-
},
41-
spec: {
42-
schedule: trigger.cron,
43-
successfulJobsHistoryLimit: 1,
44-
failedJobsHistoryLimit: 1,
45-
concurrencyPolicy: 'Allow',
46-
startingDeadlineSeconds: 60,
47-
jobTemplate: {
48-
spec: {
49-
activeDeadlineSeconds: 60,
50-
template: {
51-
spec: {
52-
restartPolicy: 'Never',
53-
terminationGracePeriodSeconds: 30,
54-
automountServiceAccountToken: false,
55-
containers: [
56-
{
57-
name: name,
58-
image: 'curlimages/curl:7.87.0',
59-
command: ['sh', '-c', command],
60-
imagePullPolicy: 'IfNotPresent',
61-
},
62-
],
42+
spec: {
43+
schedule: trigger.cron,
44+
successfulJobsHistoryLimit: 1,
45+
failedJobsHistoryLimit: 1,
46+
concurrencyPolicy: 'Allow',
47+
startingDeadlineSeconds: 60,
48+
jobTemplate: {
49+
spec: {
50+
activeDeadlineSeconds: 60,
51+
template: {
52+
spec: {
53+
restartPolicy: 'Never',
54+
terminationGracePeriodSeconds: 30,
55+
automountServiceAccountToken: false,
56+
containers: [
57+
{
58+
name: name,
59+
image: 'curlimages/curl:7.87.0',
60+
command: ['sh', '-c', command],
61+
imagePullPolicy: 'IfNotPresent',
62+
},
63+
],
64+
},
6365
},
6466
},
6567
},
6668
},
67-
},
68-
})
69+
})
70+
.catch((err) => {
71+
this.logger.error(`create cronjob ${name} failed:`, err)
72+
return null
73+
})
6974

7075
this.logger.debug(`create cronjob ${name} success`)
7176
return res.body
@@ -77,7 +82,10 @@ export class CronJobService {
7782
const region = await this.regionService.findByAppId(appid)
7883
const batchApi = this.clusterService.makeBatchV1Api(region)
7984
const name = `cron-${trigger.id}`
80-
const res = await batchApi.readNamespacedCronJob(name, ns)
85+
const res = await batchApi.readNamespacedCronJob(name, ns).catch((err) => {
86+
this.logger.error(`read cronjob ${name} failed:`, err)
87+
return null
88+
})
8189
return res.body
8290
}
8391

@@ -87,7 +95,12 @@ export class CronJobService {
8795
const region = await this.regionService.findByAppId(appid)
8896
const batchApi = this.clusterService.makeBatchV1Api(region)
8997
const name = `cron-${trigger.id}`
90-
const res = await batchApi.deleteNamespacedCronJob(name, ns)
98+
const res = await batchApi
99+
.deleteNamespacedCronJob(name, ns)
100+
.catch((err) => {
101+
this.logger.error(`delete cronjob ${name} failed:`, err)
102+
return null
103+
})
91104
return res.body
92105
}
93106

server/src/trigger/trigger-task.service.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { Injectable, Logger } from '@nestjs/common'
2-
import { RegionService } from 'src/region/region.service'
3-
import * as assert from 'node:assert'
42
import { Cron, CronExpression } from '@nestjs/schedule'
53
import { times } from 'lodash'
64
import { TASK_LOCK_INIT_TIME } from 'src/constants'
75
import { SystemDatabase } from 'src/database/system-database'
86
import { CronJobService } from './cron-job.service'
97
import { CronTrigger, TriggerPhase, TriggerState } from '@prisma/client'
8+
import * as assert from 'node:assert'
109

1110
@Injectable()
1211
export class TriggerTaskService {
@@ -125,7 +124,7 @@ export class TriggerTaskService {
125124

126125
// delete cron job
127126
const ret = await this.cronService.delete(doc)
128-
if (ret.status !== 'Success') return
127+
if (ret?.status !== 'Success') return
129128

130129
this.logger.debug('cron job deleted:', doc._id)
131130

server/src/trigger/trigger.controller.ts

+6
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ export class TriggerController {
3838
@UseGuards(JwtAuthGuard, ApplicationAuthGuard)
3939
@Post()
4040
async create(@Param('appid') appid: string, @Body() dto: CreateTriggerDto) {
41+
// check cron expression
42+
const valid = this.triggerService.isValidCronExpression(dto.cron)
43+
if (!valid) {
44+
return ResponseUtil.error('Invalid cron expression')
45+
}
46+
4147
const res = await this.triggerService.create(appid, dto)
4248
return ResponseUtil.ok(res)
4349
}

0 commit comments

Comments
 (0)