diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..c51286e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +.idea +node_modules +package-lock.json + +/data +/logs +/conf +/plugins +/queue + +/htdocs/index.html +/htdocs/index.html.gz + +/htdocs/css +/htdocs/fonts +!/htdocs/css/style.css + +/htdocs/js +!/htdocs/js/pages +!/htdocs/js/app.js +!/htdocs/js/home-worker.js diff --git a/README.md b/README.md index ed6d639f..0ce62657 100644 --- a/README.md +++ b/README.md @@ -3188,6 +3188,201 @@ Example response: See the [Standard Response Format](#standard-response-format) for details. +### get_categories + +``` +/api/app/get_categories/v1 +``` + +This fetches categories and returns details about them. It supports pagination to fetch chunks, with the default being the first 50 categories. Both HTTP GET (query string) or HTTP POST (JSON data) are acceptable. Parameters: + +| Parameter Name | Description | +|----------------|-------------| +| `offset` | (Optional) The offset into the data to start returning records, defaults to 0. | +| `limit` | (Optional) The number of records to return, defaults to 50. | + +Example request: + +```json +{ + "offset": 0, + "limit": 0 +} +``` + +Example response: + +```json +{ + "code": 0, + "rows": [ + { + "id": "new-category", + "title": "New Category", + "enabled": 1, + "username": "admin", + "description": "A description", + "max_children": 0, + "modified": 1602511976, + "created": 1602510152, + "api_key": "fee3566c8f7081bb2cdfbd82c3a52ab6", + "color": "red", + "notify_success": "admin@cronicle.com", + "notify_fail": "admin@cronicle.com", + "web_hook": "http://myserver.com/notify-chronos.php", + "cpu_limit": 100, + "cpu_sustain": 0, + "memory_limit": 0, + "memory_sustain": 0, + "log_max_size": 0 + }, + { + "id": "general", + "title": "General", + "enabled": 1, + "username": "admin", + "modified": 1602508912, + "created": 1602508912, + "description": "For events that don't fit anywhere else.", + "max_children": 0 + } + ], + "list": { + "page_size": 50, + "first_page": 0, + "last_page": 0, + "length": 2, + "type": "list" + } +} +``` + +### create_category + +``` +/api/app/create_category/v1 +``` + +This creates a new category. API Keys require the `create_categories` privilege to use this API. Only HTTP POST (JSON data) is acceptable. The required parameters are as follows: + +| Parameter Name | Description | +|----------------|-------------| +| `title` | **(Required)** A display name for the category. | +| `max_children` | **(Required)** Specifies the maximum number of jobs allowed to run concurrently in this category. Use 0 for no limit. | + +In addition to the required parameters, almost anything in the [Category Data Object](#category-data-object) can also be included here. Example request: + +```json +{ + "title": "New Category", + "enabled": 1, + "description": "A description", + "max_children": 0, + "color": "red", + "notify_success": "admin@cronicle.com", + "notify_fail": "admin@cronicle.com", + "web_hook": "http://myserver.com/notify-chronos.php", + "cpu_limit": 100, + "cpu_sustain": 0, + "memory_limit": 0, + "memory_sustain": 0, + "log_max_size": 0 +} +``` + +Example response: + +```json +{ + "code": 0, + "id": "ckg6mh66k01" +} +``` + +In addition to the [Standard Response Format](#standard-response-format), the ID of the new event will be returned in the `id` property. + +### update_category + +``` +/api/app/update_category/v1 +``` + +This updates an existing category given its ID, replacing any properties you specify. API Keys require the `update_categories` privilege to use this API. Only HTTP POST (JSON data) is acceptable. The parameters are as follows: + +| Parameter Name | Description | +|----------------|-------------| +| `id` | **(Required)** The ID of the category you wish to update. | + +Include anything from the [Category Data Object](#category-data-object) to update (i.e. replace) the values. Anything omitted is preserved. Example request: + +```json +{ + "id": "ckg6mh66k01", + "enabled": 0 +} +``` + +Example request with everything updated: + +```json +{ + "id": "ckg6mh66k01", + "title": "Updated Category", + "enabled": 1, + "description": "A description", + "max_children": 0, + "color": "red", + "notify_success": "admin@cronicle.com", + "notify_fail": "admin@cronicle.com", + "web_hook": "http://myserver.com/notify-chronos.php", + "cpu_limit": 100, + "cpu_sustain": 0, + "memory_limit": 0, + "memory_sustain": 0, + "log_max_size": 0 +} +``` + +Example response: + +```json +{ + "code": 0 +} +``` + +See the [Standard Response Format](#standard-response-format) for details. + +### delete_category + +``` +/api/app/delete_category/v1 +``` + +This deletes an existing category given its ID. Note that the category must not contain any event (or else an error will be returned). API Keys require the `delete_categories` privilege to use this API. Only HTTP POST (JSON data) is acceptable. The parameters are as follows: + +| Parameter Name | Description | +|----------------|-------------| +| `id` | **(Required)** The ID of the category you wish to delete. | + +Example request: + +```json +{ + "id": "ckg6mh66k01" +} +``` + +Example response: + +```json +{ + "code": 0 +} +``` + +See the [Standard Response Format](#standard-response-format) for details. + ## Event Data Format Here are descriptions of all the properties in the event object, which is common in many API calls: @@ -3277,6 +3472,31 @@ Here is a list of all the timing object properties and their descriptions: | `hours` | 0 - 23 | One or more hours in 24-hour time, from 0 to 23. | | `minutes` | 0 - 59 | One or more minutes, from 0 to 59. | +## Category Data Format + +Here are descriptions of all the properties in the category object, which is common in many API calls: + +| Category Property | Format | Description | +|----------------|--------|-------------| +| `api_key` | String | The API Key of the application that originally created the category (if created via API). | +| `color` | String | A highlight color for the category, which will show on the schedule.| +| `cpu_limit` | Number | Limit the CPU to the specified percentage (100 = 1 core), abort if exceeded. See [Event Resource Limits](#event-resource-limits). A default value for all events in the category. | +| `cpu_sustain` | Number | Only abort if the CPU limit is exceeded for this many seconds. See [Event Resource Limits](#event-resource-limits). A default value for all events in the category. | +| `created` | Number | The date/time of the category's initial creation, in Epoch seconds. | +| `description` | String | A description for the category. | +| `enabled` | Boolean | Specifies whether events in this category should be enabled or disabled in the schedule. | +| `id` | String | A unique ID assigned to the category when it was first created. | +| `log_max_size` | Number | Limit the job log file size to the specified amount, in bytes. See [Event Resource Limits](#event-resource-limits). A default value for all events in the category. | +| `max_children` | Number | The maximum number of jobs allowed to run concurrently in this category. See [Event Concurrency](#event-concurrency). | +| `memory_limit` | Number | Limit the memory usage to the specified amount, in bytes. See [Event Resource Limits](#event-resource-limits). A default value for all events in the category. | +| `memory_sustain` | Number | Only abort if the memory limit is exceeded for this many seconds. See [Event Resource Limits](#event-resource-limits). A default value for all events in the category. | +| `modified` | Number | The date/time of the category's last modification, in Epoch seconds. | +| `notify_fail` | String | List of e-mail recipients to notify upon job failure (CSV). See [Event Notification](#event-notification). A default value for all events in the category. | +| `notify_success` | String | List of e-mail recipients to notify upon job success (CSV). See [Event Notification](#event-notification). A default value for all events in the category. | +| `title` | String | A display name for the category. | +| `username` | String | The username of the user who originally created the category (if created in the UI). | +| `web_hook` | String | An optional URL to hit for the start and end of each job. See [Event Web Hook](#event-web-hook). A default value for all events in the category. | + # Development Cronicle runs as a component in the [pixl-server](https://www.npmjs.com/package/pixl-server) framework. It is highly recommended to read and understand that module and its component system before attempting to develop Cronicle. The following server components are also used: diff --git a/lib/api/category.js b/lib/api/category.js index 33e30e62..a9d7a639 100644 --- a/lib/api/category.js +++ b/lib/api/category.js @@ -50,8 +50,9 @@ module.exports = Class.create({ this.loadSession(args, function(err, session, user) { if (err) return self.doError('session', err.message, callback); - if (!self.requireAdmin(session, user, callback)) return; - + if (!self.requireValidUser(session, user, callback)) return; + if (!self.requirePrivilege(user, "create_categories", callback)) return; + args.user = user; args.session = session; @@ -67,23 +68,29 @@ module.exports = Class.create({ else { cat.username = user.username; } + + self.storage.listFind( 'global/categories', { id: cat.id }, function(err, categoryById) { + if (categoryById) { + return self.doError('category', "The category with the given ID already exists: " + cat.id, callback); + } - self.logDebug(6, "Creating new category: " + cat.title, cat); + self.logDebug(6, "Creating new category: " + cat.title, cat); - self.storage.listUnshift( 'global/categories', cat, function(err) { - if (err) { - return self.doError('category', "Failed to create category: " + err, callback); - } - - self.logDebug(6, "Successfully created category: " + cat.title, cat); - self.logTransaction('cat_create', cat.title, self.getClientInfo(args, { cat: cat })); - self.logActivity('cat_create', { cat: cat }, args); - - callback({ code: 0, id: cat.id }); - - // broadcast update to all websocket clients - self.updateClientData( 'categories' ); - } ); // list insert + self.storage.listUnshift( 'global/categories', cat, function(err) { + if (err) { + return self.doError('category', "Failed to create category: " + err, callback); + } + + self.logDebug(6, "Successfully created category: " + cat.title, cat); + self.logTransaction('cat_create', cat.title, self.getClientInfo(args, { cat: cat })); + self.logActivity('cat_create', { cat: cat }, args); + + callback({ code: 0, id: cat.id }); + + // broadcast update to all websocket clients + self.updateClientData( 'categories' ); + } ); // list insert + } ); // find cat } ); // load session }, @@ -99,14 +106,15 @@ module.exports = Class.create({ this.loadSession(args, function(err, session, user) { if (err) return self.doError('session', err.message, callback); - if (!self.requireAdmin(session, user, callback)) return; + if (!self.requireValidUser(session, user, callback)) return; + if (!self.requirePrivilege(user, "update_categories", callback)) return; args.user = user; args.session = session; self.storage.listFind( 'global/categories', { id: params.id }, function(err, cat) { if (err || !cat) { - return self.doError('event', "Failed to locate category: " + params.id, callback); + return self.doError('category', "Failed to locate category: " + params.id, callback); } params.modified = Tools.timeNow(true); @@ -173,7 +181,8 @@ module.exports = Class.create({ this.loadSession(args, function(err, session, user) { if (err) return self.doError('session', err.message, callback); - if (!self.requireAdmin(session, user, callback)) return; + if (!self.requireValidUser(session, user, callback)) return; + if (!self.requirePrivilege(user, "delete_categories", callback)) return; args.user = user; args.session = session; diff --git a/sample_conf/config.json b/sample_conf/config.json index 09312f69..5d852a6e 100644 --- a/sample_conf/config.json +++ b/sample_conf/config.json @@ -55,6 +55,9 @@ { "id": "delete_events", "title": "Delete Events" }, { "id": "run_events", "title": "Run Events" }, { "id": "abort_events", "title": "Abort Events" }, + { "id": "create_categories", "title": "Create Categories" }, + { "id": "update_categories", "title": "Update Categories" }, + { "id": "delete_categories", "title": "Delete Categories" }, { "id": "state_update", "title": "Toggle Scheduler" } ], "new_event_template": {