{"id":28407237,"url":"https://github.com/vincezk/node-background-job","last_synced_at":"2025-07-07T08:34:14.415Z","repository":{"id":65303552,"uuid":"335646874","full_name":"VinceZK/node-background-job","owner":"VinceZK","description":"A job server which allows to schedule your nodejs scripts.","archived":false,"fork":false,"pushed_at":"2022-05-25T14:51:25.000Z","size":20697,"stargazers_count":12,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-25T22:03:25.317Z","etag":null,"topics":["background-jobs","cron","job-scheduler","recurring-jobs","recurring-tasks","task-monitor","task-scheduler"],"latest_commit_sha":null,"homepage":"https://darkhouse.com.cn/job","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/VinceZK.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-02-03T14:14:50.000Z","updated_at":"2025-04-18T14:54:13.000Z","dependencies_parsed_at":"2023-01-16T17:30:17.400Z","dependency_job_id":null,"html_url":"https://github.com/VinceZK/node-background-job","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/VinceZK/node-background-job","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fnode-background-job","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fnode-background-job/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fnode-background-job/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fnode-background-job/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/VinceZK","download_url":"https://codeload.github.com/VinceZK/node-background-job/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fnode-background-job/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262600424,"owners_count":23335071,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["background-jobs","cron","job-scheduler","recurring-jobs","recurring-tasks","task-monitor","task-scheduler"],"created_at":"2025-06-02T00:37:33.864Z","updated_at":"2025-07-07T08:34:14.410Z","avatar_url":"https://github.com/VinceZK.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Node Background Job\n\nNode background job is a job server which allows you to schedule Node.js scripts. \nIt is not just for the developers, but more for the end users.\n\n## Features\n* Web UI for job definition, scheduling, and monitoring.\n* Support 3 different job modes: Immediately, Specific-time, and Recursively.\n* Customize job programs with flexible parameters.\n* Multiple steps can be defined in a single job.\n* Application logs and outputs can be checked and persisted.\n* With/without DB as persistence. \n* Recover from the job server restarting.\n\n## Example\nYou may experience yourself in this [Live Demo](https://darkhouse.com.cn/job/jobs).\n\nIn the job list page, you can search the existing jobs.\nClick `New` to create a new Job.\n![Job List](docs/JobList.png)\n\nIn the tab \"Steps\", you can add steps. \nA job step is defined from a job program and its parameters.\nThe job program is pre-developed and registered in the job server.\n![Add Step](docs/AddStep.png)\n\nYou can add multiple steps, and they will be executed in sequence.\nCheck \"Store console log into application log\" will store the `console.log()`\ninformation into the job application log.\n![Step List](docs/StepList.png)\n\nIn the second tab \"Start Condition\", you can define the job in one of the 3 modes:\n1. *Immediately*: the job will be executed immediately after scheduled.\n2. *At Specific Time*: the job will be executed at a specific time in future. \n3. *Recursively*: the job will be executed recursively based on [Cron](https://www.gnu.org/software/mcron/manual/html_node/Crontab-file.html).\n\nThe cron expression in the below example means the job will be executed in 8:00 and 20:00 every day\nstarting from now to the given end time.\n![Start Condition](docs/StartCondition.png)\n\nOnce you saved and scheduled the job, \nthe job occurrences will be generated and shown in the \"Occurrence\" tab.\nYou can search and filter the occurrences with statuses and timespan. \nIn the occurrence detail page, you can check the application logs and the output of the occurrence run. \n![Occurrences](docs/Occurrences.png)\n\n## To Begin\n\n1. Install it:\n   ```bash\n    $ npm install node-background-job --save\n   ```\n2. Copy the content in `node_modules\\node-background-job\\app\\job` to `app\\job`.\n   The content is the compiled web UI for the job server.    \n   \n3. Create a job program and register it for testing purpose. \n   This is an optional step. You can also use the pre-delivered job programs: [httpRequest](server/jobPrograms/httpRequest.js).\n   ``` javascript\n   import { JobProgram } from \"node-background-job\";\n   \n   // Extend the super class JobProgram\n   export default class TestProgram extends JobProgram {\n     async run(applicationLog) { // Redefine the method run\n       console.log(\"start running testJobProgram...\");\n       return new Promise( (resolve, reject) =\u003e {\n         setTimeout( () =\u003e {\n           console.log(this.name, this.parameters);\n           if (this.parameters.PARAM1 === 'value1'){\n             applicationLog.info('application log 1');\n             reject(new Error('error happened in testJobProgram'));\n           } else {\n             console.log('testJobProgram finished!');\n           }\n           resolve(this.name);\n         }, 500);\n       })\n     }\n   }\n   \n   // Register the job program\n   JobProgram.registerJobProgram('TestProgram', {\n     className: 'TestProgram',\n     class: TestProgram,\n     description: {\n       DEFAULT: 'Test job program',\n       EN: 'Test job program'\n     },\n     parameterDefinitions: {\n       GROUP1: {\n         text: 'Group One',\n         parameters: {\n           PARAM1: {\n             type: 1,\n             text: 'Parameter 1',\n             mandatory: true,\n           }\n         }\n       }\n     }\n   });\n   ```\n4. Create a `server.mjs` in your project root folder:\n   ``` javascript\n   // Create a expressjs app\n   import express from 'express';\n   const app = express();\n   \n   // Register your Angular built files as static\n   import * as path from 'path';\n   import { fileURLToPath } from 'url';\n   const __filename = fileURLToPath(import.meta.url);\n   const __dirname = path.dirname(__filename);\n   app.use(express.static(path.join(__dirname, 'app')));\n   app.get('/', (req, res) =\u003e { // The default index.html\n     res.sendFile(path.join(__dirname, 'app/job/index.html'));\n   });\n   \n   // Allow compression and json parser\n   import compression from 'compression';\n   app.use(express.json());\n   app.use(compression());\n   \n   // Route to the job web UI\n   import { Scheduler, router, HttpRequest } from 'node-background-job';\n   // In case you want use DB persistence, uncomment below 2 lines.\n   // import * as jor from 'json-on-relations';\n   // router.use(jor.Routes);\n   router.get('/job/*', (req, res) =\u003e {\n     res.sendFile(path.join(__dirname, 'app/job/index.html'));\n   });\n   app.use('/', router);\n   \n   // Import the job programs\n   import TestProgram from \"./server/jobPrograms/testProgram.mjs\";\n   \n   // Bootstrap the server\n   Scheduler.getPreviousPIDs()\n     .catch( errors =\u003e console.error(errors))\n     .then((prevNumNodes) =\u003e {\n       process.env.previousPID = Scheduler.previousPIDs[0];\n       Scheduler.on()\n         .then( () =\u003e {app.listen(process.env.PORT || 3000, () =\u003e console.log('Example app listening on port 3000!'));})\n         .catch( error =\u003e console.error(`PID ${process.pid}: `, error.message || error));\n     });\n   ``` \n5. Start the job server:\n   ```bash\n   $ node server.mjs\n   ```   \n6. If you want to use the DB persistence,\n   then you should install the [json-on-relations](https://github.com/VinceZK/json-on-relations/wiki/Setup)\n   (You can ignore the step 3).\n   The main benefit is to allow recovering from the job server restarting.\n   \n   Start the job server:\n   ```bash\n   $ node server.mjs JOB_SERVER=server1; USE_DB=true\n   ```   \n   \n## httpRequest \n\nThe job program `httpRequest` is pre-delivered which allows you schedule jobs that can raise http requests.\nThis is an easy way to achieve a job scheduler if your business logic has already been exposed as web services. \n![Using httpRequest](docs/HttpRequest.png)\n\nHowever, this program cannot be compared with [Postman](https://www.postman.com/).\nBe aware of the following limitations:\n1. The http methods are limited to 'GET', 'POST', 'PUT', 'PATCH', and 'DELETE'.\n2. Authentication now only supports the basic authentication. \n\nNevertheless, it is not a recommended approach as it cannot monitor the job status, \ncheck the application logs, and read the outputs in an integral way. \nIn practice, orchestration in multiple actions is very usual.\nFor example, you may have a process like this: \nfirst authentication, then you execute action1; based on the outputs of action1, you execute action2, and thereafter. \nThus, it is not realistic for a solution like combination of http calls. \n\nIt is much recommended you write your own job programs,\nin which you orchestrate multiple actions. \nThen [httpRequest](server/jobPrograms/httpRequest.js) is a good reference. \nBesides you write your logic by redefining the method `run`, you should define the parameters. \nThe following specification describes how the parameters are defined and rendered on the UI. \n ```js\n// Parameter definitions\nparameterDefinitions: {\n\n  // A unique group ID\n  GROUP1: {\n    // Label text of the group. Using language codes to support multiple languages\n    text?: {DEFAULT: 'text of group1', EN: 'text of group1', ZH: '组1'},\n\n    // How is the group rendered in the UI.\n    // Allowed values are 'block' and 'row'.\n    // 'block' means the parameters in this group will be displayed in block.\n    // 'row' means all the parameters are lined in a row. In this case, the group label won't be shown.\n    displayIn?: 'block', // default 'block'\n\n    // Definition of the parameters\n    parameters: {\n      // A unique parameter name\n      PARAM_NAME: {\n        // The data type of the parameter\n        // Allowed values are '1: char', '2: integer', '3: boolean', '4: decimal', '5: string', '6: binary', '7: date', '8: timestamp'\n        type?: 1, // default '1: char'\n\n        // Allowed values for the parameter. If domain is provided, the parameter will be rendered as dropdown box\n        // Description of a value supports multiple languages\n        domain?: {\n          value1: {DEFAULT: 'Label1'}, // multiple language representation\n          value2: 'label2' // simple string\n        },\n\n        // Regex to validate the parameter value\n        pattern?: '',\n\n        // Label text of the parameter, supports multiple languages\n        text?: {DEFAULT: 'Label1'},\n\n        // The default value of the parameter\n        defaultValue?: '',\n\n        // How is the parameter rendered\n        // Allowed values are 'input', 'dropdown', 'checkbox', 'text', 'password', 'date', and 'datetime'.\n        displayAs?: 'input',  // default 'input'\n\n        // If json-on-relation is used, then data element can be assigned.\n        // If attributes like type, domain, text, defaultValue and displayAs are provided, then they will override the settings in the data element.\n        dataElement?: 'DATA_ELEMENT',\n\n        // Control whether the parameter is mandatory\n        mandatory: true,\n\n        // Control whether the parameter is readonly\n        // Besides given a boolean value, an expression can be provided.\n        // For example, if the string \"parameters.TYPE === 'GET'\" is provided, then during runtime, \n        // whether this parameter is readonly or not depends on the value of parameter TYPE.\n        readOnly?: false,\n\n        // Control whether the parameter is hidden or shown.\n        // Besides given a boolean value, an expression can be provided\n        hidden?: false,\n\n        // How many columns are spanned for the parameter. The total number of columns is 12.\n        columnSpan?: 2,  // From 1~12, default 12\n\n        // Whether to hide the label\n        hideLabel?: false\n      }\n    }\n  }\n}\n ```\n\n## License\n[The MIT License](http://opensource.org/licenses/MIT)\n   \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvincezk%2Fnode-background-job","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvincezk%2Fnode-background-job","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvincezk%2Fnode-background-job/lists"}