diff --git a/back/api/cron.ts b/back/api/cron.ts index 4955476e..b69f366c 100644 --- a/back/api/cron.ts +++ b/back/api/cron.ts @@ -51,18 +51,16 @@ export default (app: Router) => { }, ); - route.get( - '/crons/:id/run', + route.put( + '/crons/run', celebrate({ - params: Joi.object({ - id: Joi.string().required(), - }), + body: Joi.array().items(Joi.string().required()), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { const cronService = Container.get(CronService); - const data = await cronService.run(req.params.id); + const data = await cronService.run(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -71,18 +69,16 @@ export default (app: Router) => { }, ); - route.get( - '/crons/:id/disable', + route.put( + '/crons/disable', celebrate({ - params: Joi.object({ - id: Joi.string().required(), - }), + body: Joi.array().items(Joi.string().required()), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { const cronService = Container.get(CronService); - const data = await cronService.disabled(req.params.id); + const data = await cronService.disabled(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -91,18 +87,16 @@ export default (app: Router) => { }, ); - route.get( - '/crons/:id/enable', + route.put( + '/crons/enable', celebrate({ - params: Joi.object({ - id: Joi.string().required(), - }), + body: Joi.array().items(Joi.string().required()), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { const cronService = Container.get(CronService); - const data = await cronService.enabled(req.params.id); + const data = await cronService.enabled(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -159,17 +153,15 @@ export default (app: Router) => { ); route.delete( - '/crons/:id', + '/crons', celebrate({ - params: Joi.object({ - id: Joi.string().required(), - }), + body: Joi.array().items(Joi.string().required()), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { const cronService = Container.get(CronService); - const data = await cronService.remove(req.params.id); + const data = await cronService.remove(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); diff --git a/back/schedule.ts b/back/schedule.ts index 1b99e5e6..960b2488 100644 --- a/back/schedule.ts +++ b/back/schedule.ts @@ -33,7 +33,7 @@ const run = async () => { ) { schedule.scheduleJob(task.schedule, function () { let command = task.command as string; - if (!command.startsWith('task ') && !command.startsWith('ql ')) { + if (!command.includes('task ') && !command.includes('ql ')) { command = `task ${command}`; } exec(command); diff --git a/back/services/cron.ts b/back/services/cron.ts index da933818..b1853656 100644 --- a/back/services/cron.ts +++ b/back/services/cron.ts @@ -74,8 +74,8 @@ export default class CronService { this.cronDb.update({ _id }, { $set: { stopped, saved: false } }); } - public async remove(_id: string) { - this.cronDb.remove({ _id }, {}); + public async remove(ids: string[]) { + this.cronDb.remove({ _id: { $in: ids } }, { multi: true }); await this.set_crontab(); } @@ -112,69 +112,84 @@ export default class CronService { }); } - public async run(_id: string) { - this.cronDb.find({ _id }).exec((err, docs: Crontab[]) => { - let res = docs[0]; - - this.logger.silly('Running job'); - this.logger.silly('ID: ' + _id); - this.logger.silly('Original command: ' + res.command); - - let logFile = `${config.manualLogPath}${res._id}.log`; - fs.writeFileSync(logFile, `开始执行...\n\n${new Date().toString()}\n`); - - let cmdStr = res.command; - if (!cmdStr.startsWith('task') && !cmdStr.startsWith('ql ')) { - cmdStr = `task ${cmdStr}`; + public async run(ids: string[]) { + this.cronDb.find({ _id: { $in: ids } }).exec((err, docs: Crontab[]) => { + for (let i = 0; i < docs.length; i++) { + const doc = docs[i]; + this.runSingle(doc); } - if (cmdStr.endsWith('.js')) { - cmdStr = `${cmdStr} now`; - } - const cmd = spawn(cmdStr, { shell: true }); - - this.cronDb.update({ _id }, { $set: { status: CrontabStatus.running } }); - - cmd.stdout.on('data', (data) => { - this.logger.silly(`stdout: ${data}`); - fs.appendFileSync(logFile, data); - }); - - cmd.stderr.on('data', (data) => { - this.logger.error(`stderr: ${data}`); - fs.appendFileSync(logFile, data); - }); - - cmd.on('close', (code) => { - this.logger.silly(`child process exited with code ${code}`); - this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); - }); - - cmd.on('error', (err) => { - this.logger.silly(err); - fs.appendFileSync(logFile, err.stack); - }); - - cmd.on('exit', (code: number, signal: any) => { - this.logger.silly(`cmd exit ${code}`); - this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); - fs.appendFileSync(logFile, `\n\n执行结束...`); - }); - - cmd.on('disconnect', () => { - this.logger.silly(`cmd disconnect`); - this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); - fs.appendFileSync(logFile, `\n\n连接断开...`); - }); }); } - public async disabled(_id: string) { - this.cronDb.update({ _id }, { $set: { status: CrontabStatus.disabled } }); + private async runSingle(cron: Crontab) { + let { _id, command } = cron; + + this.logger.silly('Running job'); + this.logger.silly('ID: ' + _id); + this.logger.silly('Original command: ' + command); + + let logFile = `${config.manualLogPath}${_id}.log`; + fs.writeFileSync(logFile, `开始执行...\n\n${new Date().toString()}\n`); + + let cmdStr = command; + if (!cmdStr.includes('task ') && !cmdStr.includes('ql ')) { + cmdStr = `task ${cmdStr}`; + } + if (cmdStr.endsWith('.js')) { + cmdStr = `${cmdStr} now`; + } + const cmd = spawn(cmdStr, { shell: true }); + + this.cronDb.update({ _id }, { $set: { status: CrontabStatus.running } }); + + cmd.stdout.on('data', (data) => { + this.logger.silly(`stdout: ${data}`); + fs.appendFileSync(logFile, data); + }); + + cmd.stderr.on('data', (data) => { + this.logger.error(`stderr: ${data}`); + fs.appendFileSync(logFile, data); + }); + + cmd.on('close', (code) => { + this.logger.silly(`child process exited with code ${code}`); + this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); + }); + + cmd.on('error', (err) => { + this.logger.silly(err); + fs.appendFileSync(logFile, err.stack); + }); + + cmd.on('exit', (code: number, signal: any) => { + this.logger.silly(`cmd exit ${code}`); + this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); + fs.appendFileSync(logFile, `\n\n执行结束...`); + }); + + cmd.on('disconnect', () => { + this.logger.silly(`cmd disconnect`); + this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); + fs.appendFileSync(logFile, `\n\n连接断开...`); + }); + } + + public async disabled(ids: string[]) { + this.cronDb.update( + { _id: { $in: ids } }, + { $set: { status: CrontabStatus.disabled } }, + { multi: true }, + ); await this.set_crontab(); } - public async enabled(_id: string) { - this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } }); + public async enabled(ids: string[]) { + this.cronDb.update( + { _id: { $in: ids } }, + { $set: { status: CrontabStatus.idle } }, + { multi: true }, + ); } public async log(_id: string) { diff --git a/src/pages/cookie/index.tsx b/src/pages/cookie/index.tsx index f2faf344..254478aa 100644 --- a/src/pages/cookie/index.tsx +++ b/src/pages/cookie/index.tsx @@ -434,7 +434,6 @@ const Config = () => { addCookie()}> 添加Cookie @@ -463,9 +462,9 @@ const Config = () => { dataSource={value} rowKey="value" size="middle" - bordered scroll={{ x: 768 }} components={components} + loading={loading} onRow={(record, index) => { return { index, diff --git a/src/pages/crontab/index.tsx b/src/pages/crontab/index.tsx index 9a01e5dc..2091270b 100644 --- a/src/pages/crontab/index.tsx +++ b/src/pages/crontab/index.tsx @@ -39,6 +39,18 @@ enum CrontabStatus { 'disabled', } +enum OperationName { + '启用', + '禁用', + '运行', +} + +enum OperationPath { + 'enable', + 'disable', + 'run', +} + const Crontab = () => { const columns = [ { @@ -142,6 +154,7 @@ const Crontab = () => { const [searchText, setSearchText] = useState(''); const [isLogModalVisible, setIsLogModalVisible] = useState(false); const [logCron, setLogCron] = useState(); + const [selectedRowIds, setSelectedRowIds] = useState([]); const getCrons = () => { setLoading(true); @@ -177,7 +190,7 @@ const Crontab = () => { ), onOk() { request - .delete(`${config.apiPrefix}crons/${record._id}`) + .delete(`${config.apiPrefix}crons`, { data: [record._id] }) .then((data: any) => { if (data.code === 200) { notification.success({ @@ -213,7 +226,7 @@ const Crontab = () => { ), onOk() { request - .get(`${config.apiPrefix}crons/${record._id}/run`) + .put(`${config.apiPrefix}crons/run`, { data: [record._id] }) .then((data: any) => { if (data.code === 200) { const result = [...value]; @@ -252,21 +265,16 @@ const Crontab = () => { ), onOk() { request - .get( - `${config.apiPrefix}crons/${record._id}/${ + .put( + `${config.apiPrefix}crons/${ record.status === CrontabStatus.disabled ? 'enable' : 'disable' }`, { - data: { _id: record._id }, + data: [record._id], }, ) .then((data: any) => { if (data.code === 200) { - notification.success({ - message: `${ - record.status === CrontabStatus.disabled ? '启用' : '禁用' - }成功`, - }); const newStatus = record.status === CrontabStatus.disabled ? CrontabStatus.idle @@ -296,7 +304,7 @@ const Crontab = () => { }> = ({ record, index }) => ( action(key, record, index)}> }> @@ -383,6 +391,74 @@ const Crontab = () => { .finally(() => setLoading(false)); }; + const onSelectChange = (selectedIds: any[]) => { + setSelectedRowIds(selectedIds); + }; + + const rowSelection = { + selectedRowIds, + onChange: onSelectChange, + selections: [ + Table.SELECTION_ALL, + Table.SELECTION_INVERT, + Table.SELECTION_NONE, + ], + }; + + const delCrons = () => { + Modal.confirm({ + title: '确认删除', + content: <>确认删除选中的定时任务吗, + onOk() { + request + .delete(`${config.apiPrefix}crons`, { data: selectedRowIds }) + .then((data: any) => { + if (data.code === 200) { + notification.success({ + message: '批量删除成功', + }); + getCrons(); + } else { + notification.error({ + message: data, + }); + } + }); + }, + onCancel() { + console.log('Cancel'); + }, + }); + }; + + const operateCrons = (operationStatus: number) => { + Modal.confirm({ + title: `确认${OperationName[operationStatus]}`, + content: <>确认{OperationName[operationStatus]}选中的定时任务吗, + onOk() { + request + .put(`${config.apiPrefix}crons/${OperationPath[operationStatus]}`, { + data: selectedRowIds, + }) + .then((data: any) => { + if (data.code === 200) { + notification.success({ + message: `批量${OperationName[operationStatus]}成功`, + }); + getCrons(); + } else { + notification.error({ + message: data, + }); + } + }); + }, + onCancel() { + console.log('Cancel'); + }, + }); + }; + useEffect(() => { if (logCron) { localStorage.setItem('logCron', logCron._id); @@ -410,7 +486,6 @@ const Crontab = () => { { height: '100vh', }} > + {selectedRowIds.length > 0 && ( +
+ + + + + + 已选择 + {selectedRowIds?.length}项 + +
+ )} { dataSource={value} rowKey="_id" size="middle" - bordered scroll={{ x: 768 }} + loading={loading} + rowSelection={rowSelection} />