diff --git a/package-lock.json b/package-lock.json index f12bc5a..fea9eda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4689,6 +4689,39 @@ "safer-buffer": "^2.1.0" } }, + "edit-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/edit-json-file/-/edit-json-file-1.1.0.tgz", + "integrity": "sha512-ucjeRZNqwXZhvgoDTUHUiaPybCMbmSSvrduIE9NHq88n2O7/HMKw+RLfdI00nbz49XBvNOUPELrYZWa4LxqxdQ==", + "requires": { + "find-value": "^1.0.3", + "iterate-object": "^1.3.2", + "r-json": "^1.2.5", + "set-value": "^0.4.0", + "w-json": "^1.3.5" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, "editorconfig": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.0.tgz", @@ -5364,6 +5397,23 @@ "tiny-lru": "^1.6.1" } }, + "fastify-formbody": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fastify-formbody/-/fastify-formbody-2.0.1.tgz", + "integrity": "sha512-Gk591NV3FuIhSL9hGQncqnTVngPQlu3pgT2ccBuoI4fhS5JaCcDoQ1ck6k8wwWrG62TGLYru4Oj8pMtXXcflUQ==", + "requires": { + "fastify-plugin": "^1.0.0", + "qs": "^6.5.1" + } + }, + "fastify-plugin": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-1.2.1.tgz", + "integrity": "sha512-TLmXpp3PEsD3MFSV3FBiH70hkalvgZ8Qmg3hr1FcJpZFaPwxoWUDoFdXZrguY+2gBVQgkFtrM47aWL5lNHqg+Q==", + "requires": { + "semver": "^5.5.0" + } + }, "fastq": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz", @@ -5632,6 +5682,11 @@ "pinkie-promise": "^2.0.0" } }, + "find-value": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/find-value/-/find-value-1.0.10.tgz", + "integrity": "sha512-s6faDIG4RgKMaDvY1BW+UtNHA6/HIq9xeK7UoRPkNTJq/HsHh6wPmmxzgfycvorwg+LocJjzEaDaMXYnZ1og2A==" + }, "find-versions": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-1.2.1.tgz", @@ -8210,6 +8265,11 @@ "integrity": "sha512-flaQ/45dMqCYSMzBQI/h3bcto6T70uN7kjNnI8n3gQU6no5p+QcnMWBNXkraED0YvbUymxKaqdvgPa09RZQM5A==", "dev": true }, + "iterate-object": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/iterate-object/-/iterate-object-1.3.2.tgz", + "integrity": "sha1-JOwVr/pdADnog5aVohwsrh9Ftms=" + }, "iterm2-version": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/iterm2-version/-/iterm2-version-2.3.0.tgz", @@ -11424,8 +11484,7 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "query-string": { "version": "4.3.4", @@ -11477,6 +11536,11 @@ "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", "dev": true }, + "r-json": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/r-json/-/r-json-1.2.8.tgz", + "integrity": "sha1-dEBWDMHt8AudjZT6MLytfd6U6uI=" + }, "randomatic": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz", @@ -15035,6 +15099,11 @@ "indexof": "0.0.1" } }, + "w-json": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/w-json/-/w-json-1.3.8.tgz", + "integrity": "sha1-NUNjJkAB7neOWwQE+xC0x/HGS0E=" + }, "w3c-hr-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", diff --git a/package.json b/package.json index a8d9637..5e80a2b 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,9 @@ "chalk": "^2.4.1", "chalkline": "0.0.5", "cli-progress": "^2.1.0", + "edit-json-file": "^1.1.0", "fastify": "^1.12.1", + "fastify-formbody": "^2.0.1", "find": "^0.2.9", "fluent-ffmpeg": "^2.1.2", "fs-extra": "^7.0.0", diff --git a/src/api/config.js b/src/api/config.js new file mode 100644 index 0000000..8ad3fba --- /dev/null +++ b/src/api/config.js @@ -0,0 +1,80 @@ +const authService = require('./auth'); +const upath = require('upath'); +const fs = require('fs-extra'); +const editJsonFile = require('edit-json-file'); + +const getFullConfig = async (path) => { + // Get current config + let config = editJsonFile(upath.join(path, 'config.json')); + + // Return Config + return [200, config.toObject()]; +}; + +const getConfigByKey = async (path, key) => { + // Get current config + let config = editJsonFile(upath.join(path, 'config.json')); + + // Return Config by a specific key + let configValue = config.get(key); + + // Return 200 if it has a value and 404 if it does not have a value + if (configValue) { + return [200, configValue]; + } else { + return [404, null]; + } +}; + +const changeConfig = async (path, config, key, newValue) => { + // Make string safe + newValue = toSafeString(newValue); + let currentValue = key.split('.').reduce(index, config); + + // Change config + let configFile = editJsonFile(upath.join(path, 'config.json')); + + configFile.set(key, JSON.parse(newValue)); + configFile.save(); + + return [200, { key: key, oldValue: currentValue, newValue: JSON.parse(newValue) }]; +}; + +// Helper function to make a string safe for ffmpeg & json +const toSafeString = function(string) { + return string; +}; + +module.exports = (fastify, path, stream, config) => { + fastify.get( + '/config', + authService.secureRouteHandler(config, async (request, reply) => { + // Returns full config is "key" is not set, otherwise only return the requested key + let response; + if (request.query.key) { + response = await getConfigByKey(path, request.query.key); + } else { + response = await getFullConfig(path); + } + + reply.type('application/json').code(response[0]); + return { + key: request.query.key, + value: response[1] + }; + }) + ); + + // Change a setting + fastify.post( + '/config', + authService.secureRouteHandler(config, async (request, reply) => { + let response = await changeConfig(path, config, request.body.key, request.body.value); + + reply.type('application/json').code(response[0]); + return { + response: response[1] + }; + }) + ); +}; diff --git a/src/api/endpoints.mdx b/src/api/endpoints.mdx index dc89b2d..1d728fd 100644 --- a/src/api/endpoints.mdx +++ b/src/api/endpoints.mdx @@ -195,3 +195,60 @@ Only showing one item from the generated example content. And used the `include_ } ``` +## GET /config + +### Description + +Returns a param of the config.json. If no key is set, all config params will be returned + +### Query Params + +* `key` - The config key in dot-notation (example: 'api.host', 'interlude.overlay.enabled') + +### Example Response: +**HTTP 200** - Successful +``` +{ + "key": "interlude.enabled", + "value": true +} +``` +**HTTP 404** - Key not existant +``` +{ + "key": "interlude.awesomeness_level", + "value": null +} +``` + +## POST /config + +### Description + +Changes a config param in the config.json. If they key has no value, it will be added + +### Body Params + +* `key` - The config key in dot-notation (example: 'api.host', 'interlude.overlay.enabled') +* `value` - The new value for the config in json (example: (string) "test", (number) 5, (boolean) true) + +### Example Response: +**HTTP 200** - Successful +``` +{ + "response": { + "key": "interlude.frequency", + "oldValue": "0.5", + "newValue": "0.2" + } +} +``` +**HTTP 200** - Key not existant, new key will be saved +``` +{ + "response": { + "key": "interlude.awesomeness_level", + "newValue": "over 9000" + } +} +``` \ No newline at end of file diff --git a/src/api/index.js b/src/api/index.js index 3398a91..65eabea 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,8 +1,12 @@ const chalk = require('chalk'); const fastify = require('fastify')({}); +// www-form-urlencoded parser for fastify +fastify.register(require('fastify-formbody')); + // Get our routes const addStreamRoutes = require('./stream.js'); +const addConfigRoutes = require('./config.js'); const addLibraryRoutes = require('./library.js'); let currentStream; @@ -23,6 +27,7 @@ module.exports = { // Implement our other routes addStreamRoutes(fastify, path, currentStream, currentConfig); + addConfigRoutes(fastify, path, currentStream, currentConfig); addLibraryRoutes(fastify, path, currentStream, currentConfig); await new Promise((resolve, reject) => { diff --git a/src/api/stream.js b/src/api/stream.js index 77959b1..9c0087b 100644 --- a/src/api/stream.js +++ b/src/api/stream.js @@ -101,4 +101,41 @@ module.exports = (fastify, path, stream, config) => { }; }) ); + + // Returns 405 + fastify.post( + '/stream', + authService.secureRouteHandler(config, async (request, reply) => { + reply.type('application/json').code(405); + return {}; + }) + ); + fastify.get( + '/stream/start', + authService.secureRouteHandler(config, async (request, reply) => { + reply.type('application/json').code(405); + return {}; + }) + ); + fastify.get( + '/stream/stop', + authService.secureRouteHandler(config, async (request, reply) => { + reply.type('application/json').code(405); + return {}; + }) + ); + fastify.get( + '/stream/restart', + authService.secureRouteHandler(config, async (request, reply) => { + reply.type('application/json').code(405); + return {}; + }) + ); + fastify.post( + '/stream/history', + authService.secureRouteHandler(config, async (request, reply) => { + reply.type('application/json').code(405); + return {}; + }) + ); };