From 6ca49403dfc86de52864d9bd35240cd32087f7e9 Mon Sep 17 00:00:00 2001 From: kevmodrome Date: Thu, 2 Jul 2020 17:28:33 +0200 Subject: [PATCH 1/9] adds tab to settings modal on front-end to display API Keys --- .../src/components/settings/Modal.svelte | 7 ++- .../src/components/settings/tabs/API.svelte | 55 +++++++++++++++++++ .../src/components/settings/tabs/index.js | 1 + 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 packages/builder/src/components/settings/tabs/API.svelte diff --git a/packages/builder/src/components/settings/Modal.svelte b/packages/builder/src/components/settings/Modal.svelte index 2d832481c3..dc6809f488 100644 --- a/packages/builder/src/components/settings/Modal.svelte +++ b/packages/builder/src/components/settings/Modal.svelte @@ -1,5 +1,5 @@ + +API Keys +
+
+ +
+
+ +
+
+ + diff --git a/packages/builder/src/components/settings/tabs/index.js b/packages/builder/src/components/settings/tabs/index.js index a1d1df38e4..67f79b536c 100644 --- a/packages/builder/src/components/settings/tabs/index.js +++ b/packages/builder/src/components/settings/tabs/index.js @@ -2,4 +2,5 @@ export { default as General } from "./General.svelte" export { default as Integrations } from "./Integrations.svelte" export { default as Permissions } from "./Permissions.svelte" export { default as Users } from "./Users.svelte" +export { default as APIKeys } from "./Api.svelte" export { default as DangerZone } from "./DangerZone.svelte" From d8e6bd0ab9e34cdae5482ad64bafac46f8ff703f Mon Sep 17 00:00:00 2001 From: kevmodrome Date: Thu, 2 Jul 2020 17:53:09 +0200 Subject: [PATCH 2/9] sets up dummy back-end --- .../tabs/{API.svelte => APIKeys.svelte} | 5 ++- .../src/components/settings/tabs/index.js | 2 +- .../server/src/api/controllers/apikeys.js | 45 +++++++++++++++++++ packages/server/src/api/index.js | 4 ++ packages/server/src/api/routes/apikeys.js | 12 +++++ packages/server/src/api/routes/index.js | 2 + 6 files changed, 68 insertions(+), 2 deletions(-) rename packages/builder/src/components/settings/tabs/{API.svelte => APIKeys.svelte} (85%) create mode 100644 packages/server/src/api/controllers/apikeys.js create mode 100644 packages/server/src/api/routes/apikeys.js diff --git a/packages/builder/src/components/settings/tabs/API.svelte b/packages/builder/src/components/settings/tabs/APIKeys.svelte similarity index 85% rename from packages/builder/src/components/settings/tabs/API.svelte rename to packages/builder/src/components/settings/tabs/APIKeys.svelte index fe3fe142ce..03aa2c86fc 100644 --- a/packages/builder/src/components/settings/tabs/API.svelte +++ b/packages/builder/src/components/settings/tabs/APIKeys.svelte @@ -9,12 +9,15 @@ async function updateKey(e) { console.log("Event Key: ", e.detail) // Send to endpoint when it exists - // const response = await api.put(`/api/${$store.appId}`, data) + const response = await api.put(`/api/${$store.appId}`, data) // const res = await response.json() } // Get Keys async function fetchKeys() { + const response = await api.get(`/api/keys/`) + const res = await response.json() + console.log("Res: ", res) // fetch keys from endpoint // return keys } diff --git a/packages/builder/src/components/settings/tabs/index.js b/packages/builder/src/components/settings/tabs/index.js index 67f79b536c..6e34141d09 100644 --- a/packages/builder/src/components/settings/tabs/index.js +++ b/packages/builder/src/components/settings/tabs/index.js @@ -2,5 +2,5 @@ export { default as General } from "./General.svelte" export { default as Integrations } from "./Integrations.svelte" export { default as Permissions } from "./Permissions.svelte" export { default as Users } from "./Users.svelte" -export { default as APIKeys } from "./Api.svelte" +export { default as APIKeys } from "./APIKeys.svelte" export { default as DangerZone } from "./DangerZone.svelte" diff --git a/packages/server/src/api/controllers/apikeys.js b/packages/server/src/api/controllers/apikeys.js new file mode 100644 index 0000000000..11dd0ac787 --- /dev/null +++ b/packages/server/src/api/controllers/apikeys.js @@ -0,0 +1,45 @@ +const CouchDB = require("../../db") +const clientDb = require("../../db/clientDb") +const bcrypt = require("../../utilities/bcrypt") +const getUserId = userName => `user_${userName}` +const { + POWERUSER_LEVEL_ID, + ADMIN_LEVEL_ID, +} = require("../../utilities/accessLevels") + +exports.fetch = async function (ctx) { + // Temporary while "real" infrastructure to store keys is created + ctx.status = 200 + ctx.message = "API Keys" + ctx.body = { + budibase: 'testFromBackEnd', + sendgrid: 'testFromBackEnd' + } +} + +exports.update = async function (ctx) { + ctx.status = 200 + ctx.message = `Updated ${ctx.params.key} succesfully.` + ctx.body = { + [ctx.params.key]: "somethingsomethingsomething" + } + + ctx.status = 200 + ctx.message = `User ${ctx.request.body.username} updated successfully.` + ctx.body = response +} + +const checkAccessLevel = async (db, accessLevelId) => { + if (!accessLevelId) return + if ( + accessLevelId === POWERUSER_LEVEL_ID || + accessLevelId === ADMIN_LEVEL_ID + ) { + return { + _id: accessLevelId, + name: accessLevelId, + permissions: [], + } + } + return await db.get(accessLevelId) +} diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.js index e6143d6725..09849422da 100644 --- a/packages/server/src/api/index.js +++ b/packages/server/src/api/index.js @@ -17,6 +17,7 @@ const { componentRoutes, workflowRoutes, accesslevelRoutes, + apiKeysRoutes } = require("./routes") const router = new Router() @@ -98,6 +99,9 @@ router.use(clientRoutes.allowedMethods()) router.use(accesslevelRoutes.routes()) router.use(accesslevelRoutes.allowedMethods()) +router.use(apiKeysRoutes.routes()) +router.use(apiKeysRoutes.allowedMethods()) + router.use(staticRoutes.routes()) router.use(staticRoutes.allowedMethods()) diff --git a/packages/server/src/api/routes/apikeys.js b/packages/server/src/api/routes/apikeys.js new file mode 100644 index 0000000000..66e1cd2d68 --- /dev/null +++ b/packages/server/src/api/routes/apikeys.js @@ -0,0 +1,12 @@ +const Router = require("@koa/router") +const controller = require("../controllers/apikeys") +const authorized = require("../../middleware/authorized") +const { BUILDER } = require("../../utilities/accessLevels") + +const router = Router() + +router + .get("/api/keys", authorized(BUILDER), controller.fetch) + .put("/api/keys/:key", authorized(BUILDER), controller.update) + +module.exports = router diff --git a/packages/server/src/api/routes/index.js b/packages/server/src/api/routes/index.js index b50fee788a..2e8662242f 100644 --- a/packages/server/src/api/routes/index.js +++ b/packages/server/src/api/routes/index.js @@ -11,6 +11,7 @@ const staticRoutes = require("./static") const componentRoutes = require("./component") const workflowRoutes = require("./workflow") const accesslevelRoutes = require("./accesslevel") +const apiKeysRoutes = require("./apikeys") module.exports = { authRoutes, @@ -26,4 +27,5 @@ module.exports = { componentRoutes, workflowRoutes, accesslevelRoutes, + apiKeysRoutes } From b6f3a1e32de7f21fd315bb82e61a29c78da888d2 Mon Sep 17 00:00:00 2001 From: kevmodrome Date: Thu, 2 Jul 2020 17:55:36 +0200 Subject: [PATCH 3/9] fetches keys correctly on the front-end --- .../builder/src/components/settings/tabs/APIKeys.svelte | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/components/settings/tabs/APIKeys.svelte b/packages/builder/src/components/settings/tabs/APIKeys.svelte index 03aa2c86fc..db07755d56 100644 --- a/packages/builder/src/components/settings/tabs/APIKeys.svelte +++ b/packages/builder/src/components/settings/tabs/APIKeys.svelte @@ -17,12 +17,10 @@ async function fetchKeys() { const response = await api.get(`/api/keys/`) const res = await response.json() - console.log("Res: ", res) - // fetch keys from endpoint - // return keys + keys = res } - let fetchKeysPromise = fetchKeys() + fetchKeys() API Keys From a5d33854da1098d278ad62d9603f35eebbc76c37 Mon Sep 17 00:00:00 2001 From: kevmodrome Date: Thu, 2 Jul 2020 18:38:00 +0200 Subject: [PATCH 4/9] dummy methods wired up to return values to the front-end --- .../src/components/settings/tabs/APIKeys.svelte | 13 ++++++------- packages/server/src/api/controllers/apikeys.js | 11 +++-------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/packages/builder/src/components/settings/tabs/APIKeys.svelte b/packages/builder/src/components/settings/tabs/APIKeys.svelte index db07755d56..4545cf9b85 100644 --- a/packages/builder/src/components/settings/tabs/APIKeys.svelte +++ b/packages/builder/src/components/settings/tabs/APIKeys.svelte @@ -6,11 +6,10 @@ let keys = { budibase: "", sendGrid: "" } - async function updateKey(e) { - console.log("Event Key: ", e.detail) - // Send to endpoint when it exists - const response = await api.put(`/api/${$store.appId}`, data) - // const res = await response.json() + async function updateKey([key, value]) { + const response = await api.put(`/api/keys/${key}`, { value }) + const res = await response.json() + keys = { ...keys, ...res } } // Get Keys @@ -27,7 +26,7 @@
updateKey(['budibase', e.detail])} thin edit value={keys.budibase} @@ -35,7 +34,7 @@
updateKey(['sendgrid', e.detail])} thin edit value={keys.sendgrid} diff --git a/packages/server/src/api/controllers/apikeys.js b/packages/server/src/api/controllers/apikeys.js index 11dd0ac787..88758cb4c3 100644 --- a/packages/server/src/api/controllers/apikeys.js +++ b/packages/server/src/api/controllers/apikeys.js @@ -18,15 +18,10 @@ exports.fetch = async function (ctx) { } exports.update = async function (ctx) { + // Do something with ctx.request.body: <{ value: value }> ctx.status = 200 - ctx.message = `Updated ${ctx.params.key} succesfully.` - ctx.body = { - [ctx.params.key]: "somethingsomethingsomething" - } - - ctx.status = 200 - ctx.message = `User ${ctx.request.body.username} updated successfully.` - ctx.body = response + ctx.message = `Updated ${ctx.params.key} API key succesfully.` + ctx.body = { [ctx.params.key]: ctx.request.body.value } } const checkAccessLevel = async (db, accessLevelId) => { From e3e5caaf9769cb05d14e151fb26a2c4889d38b5c Mon Sep 17 00:00:00 2001 From: kevmodrome Date: Thu, 2 Jul 2020 20:31:26 +0200 Subject: [PATCH 5/9] adds getEnvironmentVariables function --- .../server/src/api/controllers/apikeys.js | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/packages/server/src/api/controllers/apikeys.js b/packages/server/src/api/controllers/apikeys.js index 88758cb4c3..2c31449d0a 100644 --- a/packages/server/src/api/controllers/apikeys.js +++ b/packages/server/src/api/controllers/apikeys.js @@ -1,13 +1,15 @@ -const CouchDB = require("../../db") -const clientDb = require("../../db/clientDb") -const bcrypt = require("../../utilities/bcrypt") -const getUserId = userName => `user_${userName}` -const { - POWERUSER_LEVEL_ID, - ADMIN_LEVEL_ID, -} = require("../../utilities/accessLevels") +const fs = require("fs") + +const ENV_FILE_PATH = ".budibase/.env" exports.fetch = async function (ctx) { + // Check if structure of call makes sense, if not, return error + + + // Read File + const fileContent = await getEnvironmentVariables() + const keys = await extractKeys(fileContent) + // Temporary while "real" infrastructure to store keys is created ctx.status = 200 ctx.message = "API Keys" @@ -24,17 +26,14 @@ exports.update = async function (ctx) { ctx.body = { [ctx.params.key]: ctx.request.body.value } } -const checkAccessLevel = async (db, accessLevelId) => { - if (!accessLevelId) return - if ( - accessLevelId === POWERUSER_LEVEL_ID || - accessLevelId === ADMIN_LEVEL_ID - ) { - return { - _id: accessLevelId, - name: accessLevelId, - permissions: [], - } - } - return await db.get(accessLevelId) +async function getEnvironmentVariables() { + const home = require('os').homedir(); + const filePath = `${home}/${ENV_FILE_PATH}` + + return data = fs.readFileSync(filePath, 'utf8'); } + +async function extractKeys() { + // Extract keys here + return [] +} \ No newline at end of file From 160c3ef7c4fabaa4bff8b8138f628ee525e1ec86 Mon Sep 17 00:00:00 2001 From: kevmodrome Date: Thu, 2 Jul 2020 21:01:34 +0200 Subject: [PATCH 6/9] update process.env with new value --- packages/server/src/api/controllers/apikeys.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/apikeys.js b/packages/server/src/api/controllers/apikeys.js index 2c31449d0a..8fc0260617 100644 --- a/packages/server/src/api/controllers/apikeys.js +++ b/packages/server/src/api/controllers/apikeys.js @@ -20,7 +20,13 @@ exports.fetch = async function (ctx) { } exports.update = async function (ctx) { - // Do something with ctx.request.body: <{ value: value }> + // Set process.env + const envKeyName = `${ctx.params.key.toUpperCase()}_API_KEY` + process.env[envKeyName] = ctx.request.body.value + + // Write to file + // TODO + ctx.status = 200 ctx.message = `Updated ${ctx.params.key} API key succesfully.` ctx.body = { [ctx.params.key]: ctx.request.body.value } @@ -33,7 +39,8 @@ async function getEnvironmentVariables() { return data = fs.readFileSync(filePath, 'utf8'); } -async function extractKeys() { +async function extractKeys(content) { + const lines = content.split(/\r?\n/) // Extract keys here return [] } \ No newline at end of file From 0ae8c425ee8543f0b106a49d90d8acec9d389616 Mon Sep 17 00:00:00 2001 From: kevmodrome Date: Thu, 2 Jul 2020 21:25:25 +0200 Subject: [PATCH 7/9] fetching keys now done correctly --- packages/server/src/api/controllers/apikeys.js | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/server/src/api/controllers/apikeys.js b/packages/server/src/api/controllers/apikeys.js index 8fc0260617..3a993f53ae 100644 --- a/packages/server/src/api/controllers/apikeys.js +++ b/packages/server/src/api/controllers/apikeys.js @@ -1,21 +1,11 @@ const fs = require("fs") - const ENV_FILE_PATH = ".budibase/.env" exports.fetch = async function (ctx) { - // Check if structure of call makes sense, if not, return error - - - // Read File - const fileContent = await getEnvironmentVariables() - const keys = await extractKeys(fileContent) - - // Temporary while "real" infrastructure to store keys is created ctx.status = 200 - ctx.message = "API Keys" ctx.body = { - budibase: 'testFromBackEnd', - sendgrid: 'testFromBackEnd' + budibase: process.env.BUDIBASE_API_KEY, + sendgrid: process.env.SENDGRID_API_KEY } } @@ -41,6 +31,7 @@ async function getEnvironmentVariables() { async function extractKeys(content) { const lines = content.split(/\r?\n/) + console.log(lines) // Extract keys here - return [] + return lines } \ No newline at end of file From 411296894238806268c727bfa6ad216b2b303750 Mon Sep 17 00:00:00 2001 From: kevmodrome Date: Mon, 6 Jul 2020 09:06:59 +0200 Subject: [PATCH 8/9] use utility to get homedir instead of manually doing it --- .../server/src/api/controllers/apikeys.js | 81 +++++++++++-------- 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/packages/server/src/api/controllers/apikeys.js b/packages/server/src/api/controllers/apikeys.js index 3a993f53ae..35fc29e37e 100644 --- a/packages/server/src/api/controllers/apikeys.js +++ b/packages/server/src/api/controllers/apikeys.js @@ -1,37 +1,52 @@ const fs = require("fs") -const ENV_FILE_PATH = ".budibase/.env" +const readline = require("readline") +const { budibaseAppsDir } = require("../../utilities/budibaseDir") +const ENV_FILE_PATH = "/.env" -exports.fetch = async function (ctx) { - ctx.status = 200 - ctx.body = { - budibase: process.env.BUDIBASE_API_KEY, - sendgrid: process.env.SENDGRID_API_KEY +exports.fetch = async function(ctx) { + ctx.status = 200 + ctx.body = { + budibase: process.env.BUDIBASE_API_KEY, + sendgrid: process.env.SENDGRID_API_KEY, + } +} + +exports.update = async function(ctx) { + const key = `${ctx.params.key.toUpperCase()}_API_KEY` + const value = ctx.request.body.value + // Set process.env + process.env[key] = value + + // Write to file + await updateValues([key, value]) + + ctx.status = 200 + ctx.message = `Updated ${ctx.params.key} API key succesfully.` + ctx.body = { [ctx.params.key]: ctx.request.body.value } +} + +async function updateValues([key, value]) { + let newContent = "" + let keyExists = false + const readInterface = readline.createInterface({ + input: fs.createReadStream(`${budibaseAppsDir()}/${ENV_FILE_PATH}`), + output: process.stdout, + console: false, + }) + readInterface.on("line", function(line) { + // Mutate lines and change API Key + if (line.startsWith(key)) { + line = `${key}=${value}` + keyExists = true } + newContent = `${newContent}\n${line}` + }) + readInterface.on("close", function() { + // Write file here + if (!keyExists) { + // Add API Key if it doesn't exist in the file at all + newContent = `${newContent}\n${key}=${value}` + } + fs.writeFileSync(`${budibaseAppsDir()}/${ENV_FILE_PATH}`, newContent) + }) } - -exports.update = async function (ctx) { - // Set process.env - const envKeyName = `${ctx.params.key.toUpperCase()}_API_KEY` - process.env[envKeyName] = ctx.request.body.value - - // Write to file - // TODO - - ctx.status = 200 - ctx.message = `Updated ${ctx.params.key} API key succesfully.` - ctx.body = { [ctx.params.key]: ctx.request.body.value } -} - -async function getEnvironmentVariables() { - const home = require('os').homedir(); - const filePath = `${home}/${ENV_FILE_PATH}` - - return data = fs.readFileSync(filePath, 'utf8'); -} - -async function extractKeys(content) { - const lines = content.split(/\r?\n/) - console.log(lines) - // Extract keys here - return lines -} \ No newline at end of file From c18bcc5499efafae501a565b93fb76ff3cdb1aa5 Mon Sep 17 00:00:00 2001 From: kevmodrome Date: Mon, 6 Jul 2020 09:07:29 +0200 Subject: [PATCH 9/9] lint fixes --- packages/server/src/api/index.js | 2 +- packages/server/src/api/routes/apikeys.js | 4 ++-- packages/server/src/api/routes/index.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.js index 09849422da..911b8f7c66 100644 --- a/packages/server/src/api/index.js +++ b/packages/server/src/api/index.js @@ -17,7 +17,7 @@ const { componentRoutes, workflowRoutes, accesslevelRoutes, - apiKeysRoutes + apiKeysRoutes, } = require("./routes") const router = new Router() diff --git a/packages/server/src/api/routes/apikeys.js b/packages/server/src/api/routes/apikeys.js index 66e1cd2d68..bec9ab677c 100644 --- a/packages/server/src/api/routes/apikeys.js +++ b/packages/server/src/api/routes/apikeys.js @@ -6,7 +6,7 @@ const { BUILDER } = require("../../utilities/accessLevels") const router = Router() router - .get("/api/keys", authorized(BUILDER), controller.fetch) - .put("/api/keys/:key", authorized(BUILDER), controller.update) + .get("/api/keys", authorized(BUILDER), controller.fetch) + .put("/api/keys/:key", authorized(BUILDER), controller.update) module.exports = router diff --git a/packages/server/src/api/routes/index.js b/packages/server/src/api/routes/index.js index 2e8662242f..50a652b39e 100644 --- a/packages/server/src/api/routes/index.js +++ b/packages/server/src/api/routes/index.js @@ -27,5 +27,5 @@ module.exports = { componentRoutes, workflowRoutes, accesslevelRoutes, - apiKeysRoutes + apiKeysRoutes, }