diff --git a/packages/api/package.json b/packages/api/package.json index d49892339..5edd9493f 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -38,6 +38,7 @@ "lodash": "^4.17.15", "ncp": "^2.0.0", "nedb-promises": "^4.0.1", + "node-cron": "^2.0.3", "tar": "^6.0.5", "uuid": "^3.4.0" }, diff --git a/packages/api/src/controllers/files.js b/packages/api/src/controllers/files.js index c69e36642..35399c7d2 100644 --- a/packages/api/src/controllers/files.js +++ b/packages/api/src/controllers/files.js @@ -2,6 +2,7 @@ const fs = require('fs-extra'); const path = require('path'); const { filesdir } = require('../utility/directories'); const socket = require('../utility/socket'); +const scheduler = require('./scheduler'); function serialize(format, data) { if (format == 'text') return data; @@ -44,5 +45,8 @@ module.exports = { } await fs.writeFile(path.join(dir, file), serialize(format, data)); socket.emitChanged(`files-changed-${folder}`); + if (folder == 'shell') { + scheduler.reload(); + } }, }; diff --git a/packages/api/src/controllers/scheduler.js b/packages/api/src/controllers/scheduler.js new file mode 100644 index 000000000..3a68e9922 --- /dev/null +++ b/packages/api/src/controllers/scheduler.js @@ -0,0 +1,41 @@ +const { filesdir } = require('../utility/directories'); +const fs = require('fs-extra'); +const path = require('path'); +const cron = require('node-cron'); +const runners = require('./runners'); + +const scheduleRegex = /\s*\/\/\s*@schedule\s+([^\n]+)\n/; + +module.exports = { + tasks: [], + + async unload() { + this.tasks.forEach((x) => x.destroy()); + this.tasks = []; + }, + + async processFile(file) { + const text = await fs.readFile(file, { encoding: 'utf-8' }); + const match = text.match(scheduleRegex); + if (!match) return; + const pattern = match[1]; + if (!cron.validate(pattern)) return; + console.log(`Schedule script ${file} with pattern ${pattern}`); + const task = cron.schedule(pattern, () => runners.start({ script: text })); + this.tasks.push(task); + }, + + async reload() { + const shellDir = path.join(filesdir(), 'shell'); + await this.unload(); + if (!(await fs.exists(shellDir))) return; + const files = await fs.readdir(shellDir); + for (const file of files) { + await this.processFile(path.join(shellDir, file)); + } + }, + + async _init() { + this.reload(); + }, +}; diff --git a/packages/api/src/main.js b/packages/api/src/main.js index cbc0eaed8..16cacd4f0 100644 --- a/packages/api/src/main.js +++ b/packages/api/src/main.js @@ -24,6 +24,7 @@ const archive = require('./controllers/archive'); const uploads = require('./controllers/uploads'); const plugins = require('./controllers/plugins'); const files = require('./controllers/files'); +const scheduler = require('./controllers/scheduler'); const { rundir } = require('./utility/directories'); @@ -69,6 +70,7 @@ function start(argument = null) { useController(app, '/uploads', uploads); useController(app, '/plugins', plugins); useController(app, '/files', files); + useController(app, '/scheduler', scheduler); if (process.env.PAGES_DIRECTORY) { app.use('/pages', express.static(process.env.PAGES_DIRECTORY)); diff --git a/packages/web/src/tabs/QueryTab.js b/packages/web/src/tabs/QueryTab.js index f5bcd52d7..79ab4809a 100644 --- a/packages/web/src/tabs/QueryTab.js +++ b/packages/web/src/tabs/QueryTab.js @@ -32,9 +32,10 @@ export default function QueryTab({ tabid, conid, database, initialArgs, tabVisib const extensions = useExtensions(); const { editorData, setEditorData, isLoading } = useEditorData({ tabid, - loadFromArgs: initialArgs && initialArgs.sqlTemplate - ? () => applySqlTemplate(initialArgs.sqlTemplate, extensions, { conid, database, ...other }) - : null, + loadFromArgs: + initialArgs && initialArgs.sqlTemplate + ? () => applySqlTemplate(initialArgs.sqlTemplate, extensions, { conid, database, ...other }) + : null, }); const editorRef = React.useRef(null); @@ -113,12 +114,13 @@ export default function QueryTab({ tabid, conid, database, initialArgs, tabVisib editorRef.current.editor.clearSelection(); }; - if (isLoading) + if (isLoading) { return (