diff --git a/packages/server/middleware/routeHandlers/aggregatesPost.js b/packages/server/middleware/routeHandlers/aggregatesPost.js new file mode 100644 index 0000000000..8b2af0c84f --- /dev/null +++ b/packages/server/middleware/routeHandlers/aggregatesPost.js @@ -0,0 +1,12 @@ +const StatusCodes = require("../../utilities/statusCodes") +const { getRecordKey } = require("./helpers") + +module.exports = async ctx => { + const indexkey = getRecordKey(ctx.params.appname, ctx.request.path) + ctx.body = await ctx.instance.indexApi.aggregates(indexkey, { + rangeStartParams: ctx.request.body.rangeStartParams, + rangeEndParams: ctx.request.body.rangeEndParams, + searchPhrase: ctx.request.body.searchPhrase, + }) + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/appDefault.js b/packages/server/middleware/routeHandlers/appDefault.js new file mode 100644 index 0000000000..61dd9520de --- /dev/null +++ b/packages/server/middleware/routeHandlers/appDefault.js @@ -0,0 +1,19 @@ +const send = require("koa-send") + +module.exports = async (ctx, next) => { + const path = ctx.path.replace(`/${ctx.params.appname}`, "") + + if (path.startsWith("/api/")) { + await next() + } else if (path.startsWith("/_shared/")) { + await send(ctx, path.replace(`/_shared/`, ""), { root: ctx.sharedPath }) + } else if ( + path.endsWith(".js") || + path.endsWith(".map") || + path.endsWith(".css") + ) { + await send(ctx, path, { root: ctx.publicPath }) + } else { + await send(ctx, "/index.html", { root: ctx.publicPath }) + } +} diff --git a/packages/server/middleware/routeHandlers/authenticate.js b/packages/server/middleware/routeHandlers/authenticate.js new file mode 100644 index 0000000000..263c545907 --- /dev/null +++ b/packages/server/middleware/routeHandlers/authenticate.js @@ -0,0 +1,15 @@ +const StatusCodes = require("../../utilities/statusCodes") + +module.exports = async ctx => { + const user = await ctx.master.authenticate( + ctx.sessionId, + ctx.params.appname, + ctx.request.body.username, + ctx.request.body.password + ) + if (!user) { + ctx.throw(StatusCodes.UNAUTHORIZED, "invalid username or password") + } + ctx.body = user.user_json + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/changeMyPassword.js b/packages/server/middleware/routeHandlers/changeMyPassword.js new file mode 100644 index 0000000000..5f7d63979c --- /dev/null +++ b/packages/server/middleware/routeHandlers/changeMyPassword.js @@ -0,0 +1,9 @@ +const StatusCodes = require("../../utilities/statusCodes") + +module.exports = async ctx => { + await ctx.instance.authApi.changeMyPassword( + ctx.request.body.currentPassword, + ctx.request.body.newPassword + ) + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/createTemporaryAccess.js b/packages/server/middleware/routeHandlers/createTemporaryAccess.js new file mode 100644 index 0000000000..8189e3027a --- /dev/null +++ b/packages/server/middleware/routeHandlers/createTemporaryAccess.js @@ -0,0 +1,17 @@ +const StatusCodes = require("../../utilities/statusCodes") + +module.exports = async ctx => { + const instanceApi = await ctx.master.getFullAccessInstanceApiForUsername( + ctx.params.appname, + ctx.request.body.username + ) + + if (!instanceApi) { + ctx.request.status = StatusCodes.OK + return + } + + await instanceApi.authApi.createTemporaryAccess(ctx.request.body.username) + + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/createUser.js b/packages/server/middleware/routeHandlers/createUser.js new file mode 100644 index 0000000000..9e630ff6d4 --- /dev/null +++ b/packages/server/middleware/routeHandlers/createUser.js @@ -0,0 +1,10 @@ +const StatusCodes = require("../../utilities/statusCodes") + +module.exports = async ctx => { + await ctx.instance.authApi.createUser( + ctx.request.body.user, + ctx.request.body.password + ) + + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/deleteRecord.js b/packages/server/middleware/routeHandlers/deleteRecord.js new file mode 100644 index 0000000000..fb08046096 --- /dev/null +++ b/packages/server/middleware/routeHandlers/deleteRecord.js @@ -0,0 +1,9 @@ +const StatusCodes = require("../../utilities/statusCodes") +const { getRecordKey } = require("./helpers") + +module.exports = async ctx => { + await ctx.instance.recordApi.delete( + getRecordKey(ctx.params.appname, ctx.request.path) + ) + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/disableUser.js b/packages/server/middleware/routeHandlers/disableUser.js new file mode 100644 index 0000000000..b1be4d587e --- /dev/null +++ b/packages/server/middleware/routeHandlers/disableUser.js @@ -0,0 +1,11 @@ +const StatusCodes = require("../../utilities/statusCodes") + +module.exports = async ctx => { + await ctx.instance.authApi.disableUser(ctx.request.body.username) + + await ctx.master.removeSessionsForUser( + ctx.params.appname, + ctx.request.body.username + ) + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/enableUser.js b/packages/server/middleware/routeHandlers/enableUser.js new file mode 100644 index 0000000000..fd86c24c4b --- /dev/null +++ b/packages/server/middleware/routeHandlers/enableUser.js @@ -0,0 +1,6 @@ +const StatusCodes = require("../../utilities/statusCodes") + +module.exports = async ctx => { + await ctx.instance.authApi.enableUser(ctx.request.body.username) + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/executeAction.js b/packages/server/middleware/routeHandlers/executeAction.js new file mode 100644 index 0000000000..a6b3b3a310 --- /dev/null +++ b/packages/server/middleware/routeHandlers/executeAction.js @@ -0,0 +1,9 @@ +const StatusCodes = require("../../utilities/statusCodes") + +module.exports = async ctx => { + ctx.body = await ctx.instance.actionApi.execute( + ctx.request.body.actionname, + ctx.request.body.parameters + ) + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/getAccessLevels.js b/packages/server/middleware/routeHandlers/getAccessLevels.js new file mode 100644 index 0000000000..75dd865982 --- /dev/null +++ b/packages/server/middleware/routeHandlers/getAccessLevels.js @@ -0,0 +1,6 @@ +const StatusCodes = require("../../utilities/statusCodes") + +module.exports = async ctx => { + ctx.body = await ctx.instance.authApi.getAccessLevels() + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/getRecord.js b/packages/server/middleware/routeHandlers/getRecord.js new file mode 100644 index 0000000000..177e56ffee --- /dev/null +++ b/packages/server/middleware/routeHandlers/getRecord.js @@ -0,0 +1,15 @@ +const StatusCodes = require("../../utilities/statusCodes") +const { getRecordKey } = require("./helpers") + +module.exports = async ctx => { + try { + ctx.body = await ctx.instance.recordApi.load( + getRecordKey(ctx.params.appname, ctx.request.path) + ) + ctx.response.status = StatusCodes.OK + } catch (e) { + // need to be catching for 404s here + ctx.response.status = StatusCodes.INTERAL_ERROR + ctx.response.body = e.message + } +} diff --git a/packages/server/middleware/routeHandlers/getUsers.js b/packages/server/middleware/routeHandlers/getUsers.js new file mode 100644 index 0000000000..cd661b95df --- /dev/null +++ b/packages/server/middleware/routeHandlers/getUsers.js @@ -0,0 +1,6 @@ +const StatusCodes = require("../../utilities/statusCodes") + +module.exports = async ctx => { + ctx.body = await ctx.instance.authApi.getUsers() + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/helpers.js b/packages/server/middleware/routeHandlers/helpers.js new file mode 100644 index 0000000000..22a29d3d84 --- /dev/null +++ b/packages/server/middleware/routeHandlers/helpers.js @@ -0,0 +1,7 @@ +exports.getRecordKey = (appname, wholePath) => + wholePath + .replace(`/${appname}/api/files/`, "") + .replace(`/${appname}/api/lookup_field/`, "") + .replace(`/${appname}/api/record/`, "") + .replace(`/${appname}/api/listRecords/`, "") + .replace(`/${appname}/api/aggregates/`, "") diff --git a/packages/server/middleware/routeHandlers/index.js b/packages/server/middleware/routeHandlers/index.js new file mode 100644 index 0000000000..af4ff60605 --- /dev/null +++ b/packages/server/middleware/routeHandlers/index.js @@ -0,0 +1,43 @@ +const authenticate = require("./authenticate") +const setPasswordFromTemporaryCode = require("./setPasswordFromTemporaryCode") +const createTemporaryAccess = require("./createTemporaryAccess") +const appDefault = require("./appDefault") +const changeMyPassword = require("./changeMyPassword") +const executeAction = require("./executeAction") +const createUser = require("./createUser") +const enableUser = require("./enableUser") +const disableUser = require("./disableUser") +const getUsers = require("./getUsers") +const getAccessLevels = require("./getAccessLevels") +const listRecordsGet = require("./listRecordsGet") +const listRecordsPost = require("./listRecordsPost") +const aggregatesPost = require("./aggregatesPost") +const postFiles = require("./postFiles") +const saveRecord = require("./saveRecord") +const lookupField = require("./lookupField") +const getRecord = require("./getRecord") +const deleteRecord = require("./deleteRecord") +const saveAppHierarchy = require("./saveAppHierarchy") + +module.exports = { + authenticate, + setPasswordFromTemporaryCode, + createTemporaryAccess, + appDefault, + changeMyPassword, + executeAction, + createUser, + enableUser, + disableUser, + getUsers, + getAccessLevels, + listRecordsGet, + listRecordsPost, + aggregatesPost, + postFiles, + saveRecord, + lookupField, + getRecord, + deleteRecord, + saveAppHierarchy, +} diff --git a/packages/server/middleware/routeHandlers/listRecordsGet.js b/packages/server/middleware/routeHandlers/listRecordsGet.js new file mode 100644 index 0000000000..b456016451 --- /dev/null +++ b/packages/server/middleware/routeHandlers/listRecordsGet.js @@ -0,0 +1,8 @@ +const StatusCodes = require("../../utilities/statusCodes") +const { getRecordKey } = require("./helpers") + +module.exports = async ctx => { + const indexkey = getRecordKey(ctx.params.appname, ctx.request.path) + ctx.body = await ctx.instance.indexApi.listItems(indexkey) + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/listRecordsPost.js b/packages/server/middleware/routeHandlers/listRecordsPost.js new file mode 100644 index 0000000000..c1856ba925 --- /dev/null +++ b/packages/server/middleware/routeHandlers/listRecordsPost.js @@ -0,0 +1,12 @@ +const StatusCodes = require("../../utilities/statusCodes") +const { getRecordKey } = require("./helpers") + +module.exports = async ctx => { + const indexkey = getRecordKey(ctx.params.appname, ctx.request.path) + ctx.body = await ctx.instance.indexApi.listItems(indexkey, { + rangeStartParams: ctx.request.body.rangeStartParams, + rangeEndParams: ctx.request.body.rangeEndParams, + searchPhrase: ctx.request.body.searchPhrase, + }) + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/lookupField.js b/packages/server/middleware/routeHandlers/lookupField.js new file mode 100644 index 0000000000..fec2d4e39e --- /dev/null +++ b/packages/server/middleware/routeHandlers/lookupField.js @@ -0,0 +1,14 @@ +const StatusCodes = require("../../utilities/statusCodes") +const { getRecordKey } = require("./helpers") + +module.exports = async ctx => { + const recordKey = getRecordKey(ctx.params.appname, ctx.request.path) + const fields = ctx.query.fields.split(",") + const recordContext = await ctx.instance.recordApi.getContext(recordKey) + const allContext = [] + for (let field of fields) { + allContext.push(await recordContext.referenceOptions(field)) + } + ctx.body = allContext + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/postFiles.js b/packages/server/middleware/routeHandlers/postFiles.js new file mode 100644 index 0000000000..7b63e8eccd --- /dev/null +++ b/packages/server/middleware/routeHandlers/postFiles.js @@ -0,0 +1,13 @@ +const StatusCodes = require("../../utilities/statusCodes") +const { getRecordKey } = require("./helpers") +const fs = require("fs") + +module.exports = async ctx => { + const file = ctx.request.files.file + ctx.body = await ctx.instance.recordApi.uploadFile( + getRecordKey(ctx.params.appname, ctx.request.path), + fs.createReadStream(file.path), + file.name + ) + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/saveAppHierarchy.js b/packages/server/middleware/routeHandlers/saveAppHierarchy.js new file mode 100644 index 0000000000..3537fa35f6 --- /dev/null +++ b/packages/server/middleware/routeHandlers/saveAppHierarchy.js @@ -0,0 +1,6 @@ +const StatusCodes = require("../../utilities/statusCodes") + +module.exports = async ctx => { + ctx.body = await ctx.instance.templateApi.saveApplicationHierarchy(ctx.body) + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/saveRecord.js b/packages/server/middleware/routeHandlers/saveRecord.js new file mode 100644 index 0000000000..53ec9d94ff --- /dev/null +++ b/packages/server/middleware/routeHandlers/saveRecord.js @@ -0,0 +1,6 @@ +const StatusCodes = require("../../utilities/statusCodes") + +module.exports = async ctx => { + ctx.body = await ctx.instance.recordApi.save(ctx.request.body) + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routeHandlers/setPasswordFromTemporaryCode.js b/packages/server/middleware/routeHandlers/setPasswordFromTemporaryCode.js new file mode 100644 index 0000000000..69d0652450 --- /dev/null +++ b/packages/server/middleware/routeHandlers/setPasswordFromTemporaryCode.js @@ -0,0 +1,20 @@ +const StatusCodes = require("../../utilities/statusCodes") + +module.exports = async ctx => { + const instanceApi = await ctx.master.getFullAccessInstanceApiForUsername( + ctx.params.appname, + ctx.request.body.username + ) + + if (!instanceApi) { + ctx.request.status = StatusCodes.OK + return + } + + await instanceApi.authApi.setPasswordFromTemporaryCode( + ctx.request.body.tempCode, + ctx.request.body.newPassword + ) + + ctx.response.status = StatusCodes.OK +} diff --git a/packages/server/middleware/routers.js b/packages/server/middleware/routers.js index 3d07ac29d9..2933ca0a55 100644 --- a/packages/server/middleware/routers.js +++ b/packages/server/middleware/routers.js @@ -1,9 +1,10 @@ const Router = require("@koa/router") const session = require("./session") const StatusCodes = require("../utilities/statusCodes") -const fs = require("fs") const { resolve } = require("path") const send = require("koa-send") +const routeHandlers = require("./routeHandlers") + const { getPackageForBuilder, getComponentDefinitions, @@ -38,6 +39,25 @@ module.exports = (config, app) => { ctx.set("x-bbappname", appname) if (appname === "_builder") { + if (!config.dev) { + ctx.response.status = StatusCodes.FORBIDDEN + ctx.body = "run in dev mode to access builder" + return + } + + if (ctx.path.startsWith("/_builder/instance/_master")) { + ctx.instance = ctx.master.getFullAccessApiForMaster() + ctx.isAuthenticated = !!ctx.instance + } else if (ctx.path.startsWith("/_builder/instance")) { + const builderAppName = pathParts[3] + const instanceId = pathParts[4] + ctx.instance = ctx.master.getFullAccessApiForInstanceId( + builderAppName, + instanceId + ).bbInstance + ctx.isAuthenticated = !!ctx.instance + } + await next() } else { const instance = await ctx.master.getInstanceApiForSession( @@ -54,12 +74,6 @@ module.exports = (config, app) => { } }) .get("/_builder", async ctx => { - if (!config.dev) { - ctx.response.status = StatusCodes.FORBIDDEN - ctx.body = "run in dev mode to access builder" - return - } - await send(ctx, "/index.html", { root: builderPath }) }) .get("/_builder/:appname/componentlibrary", async ctx => { @@ -71,12 +85,6 @@ module.exports = (config, app) => { await send(ctx, info.components._lib || "index.js", { root: info.libDir }) }) .get("/_builder/*", async (ctx, next) => { - if (!config.dev) { - ctx.response.status = StatusCodes.FORBIDDEN - ctx.body = "run in dev mode to access builder" - return - } - const path = ctx.path.replace("/_builder", "") if (path.startsWith("/api/")) { @@ -85,58 +93,36 @@ module.exports = (config, app) => { await send(ctx, path, { root: builderPath }) } }) - .post("/:appname/api/authenticate", async ctx => { - const user = await ctx.master.authenticate( - ctx.sessionId, - ctx.params.appname, - ctx.request.body.username, - ctx.request.body.password - ) - if (!user) { - ctx.throw(StatusCodes.UNAUTHORIZED, "invalid username or password") - } - ctx.body = user.user_json - ctx.response.status = StatusCodes.OK - }) - .post("/:appname/api/setPasswordFromTemporaryCode", async ctx => { - const instanceApi = await ctx.master.getFullAccessInstanceApiForUsername( - ctx.params.appname, - ctx.request.body.username - ) - - if (!instanceApi) { - ctx.request.status = StatusCodes.OK - return - } - - await instanceApi.authApi.setPasswordFromTemporaryCode( - ctx.request.body.tempCode, - ctx.request.body.newPassword - ) - - ctx.response.status = StatusCodes.OK - }) - .post("/:appname/api/createTemporaryAccess", async ctx => { - const instanceApi = await ctx.master.getFullAccessInstanceApiForUsername( - ctx.params.appname, - ctx.request.body.username - ) - - if (!instanceApi) { - ctx.request.status = StatusCodes.OK - return - } - - await instanceApi.authApi.createTemporaryAccess(ctx.request.body.username) - - ctx.response.status = StatusCodes.OK - }) + .post("/:appname/api/authenticate", routeHandlers.authenticate) + .post( + "/_builder/instance/:appname/:instanceid/api/authenticate", + routeHandlers.authenticate + ) + .post( + "/:appname/api/setPasswordFromTemporaryCode", + routeHandlers.setPasswordFromTemporaryCode + ) + .post( + "/_builder/instance/:appname/:instanceid/api/setPasswordFromTemporaryCode", + routeHandlers.setPasswordFromTemporaryCode + ) + .post( + "/:appname/api/createTemporaryAccess", + routeHandlers.createTemporaryAccess + ) + .post( + "/_builder/instance/:appname/:instanceid/api/createTemporaryAccess", + routeHandlers.createTemporaryAccess + ) .get("/_builder/api/apps", async ctx => { ctx.body = await getApps(config, ctx.master) ctx.response.status = StatusCodes.OK }) .get("/_builder/api/:appname/appPackage", async ctx => { - ctx.body = await getPackageForBuilder(config, ctx.params.appname) + const application = await ctx.master.getApplicationWithInstances( + ctx.params.appname + ) + ctx.body = await getPackageForBuilder(config, application) ctx.response.status = StatusCodes.OK }) .get("/_builder/api/:appname/components", async ctx => { @@ -228,23 +214,9 @@ module.exports = (config, app) => { .get("/:appname", async ctx => { await send(ctx, "/index.html", { root: ctx.publicPath }) }) - .get("/:appname/*", async (ctx, next) => { - const path = ctx.path.replace(`/${ctx.params.appname}`, "") - - if (path.startsWith("/api/")) { - await next() - } else if (path.startsWith("/_shared/")) { - await send(ctx, path.replace(`/_shared/`, ""), { root: ctx.sharedPath }) - } else if ( - path.endsWith(".js") || - path.endsWith(".map") || - path.endsWith(".css") - ) { - await send(ctx, path, { root: ctx.publicPath }) - } else { - await send(ctx, "/index.html", { root: ctx.publicPath }) - } - }) + .get("/:appname/*", routeHandlers.appDefault) + .get("/_builder/instance/:appname/:instanceid/*", routeHandlers.appDefault) + // EVERYTHING BELOW HERE REQUIRES AUTHENTICATION .use(async (ctx, next) => { if (ctx.isAuthenticated) { await next() @@ -252,147 +224,85 @@ module.exports = (config, app) => { ctx.response.status = StatusCodes.UNAUTHORIZED } }) - .post("/:appname/api/changeMyPassword", async ctx => { - await ctx.instance.authApi.changeMyPassword( - ctx.request.body.currentPassword, - ctx.request.body.newPassword - ) - ctx.response.status = StatusCodes.OK - }) - .post("/:appname/api/changeMyPassword", async ctx => { - await ctx.instance.authApi.changeMyPassword( - ctx.request.body.currentPassword, - ctx.request.body.newPassword - ) - ctx.response.status = StatusCodes.OK - }) - .post("/:appname/api/executeAction/:actionname", async ctx => { - ctx.body = await ctx.instance.actionApi.execute( - ctx.request.body.actionname, - ctx.request.body.parameters - ) - ctx.response.status = StatusCodes.OK - }) - .post("/:appname/api/createUser", async ctx => { - await ctx.instance.authApi.createUser( - ctx.request.body.user, - ctx.request.body.password - ) - - ctx.response.status = StatusCodes.OK - }) - .post("/:appname/api/enableUser", async ctx => { - await ctx.instance.authApi.enableUser(ctx.request.body.username) - ctx.response.status = StatusCodes.OK - }) - .post("/:appname/api/disableUser", async ctx => { - await ctx.instance.authApi.disableUser(ctx.request.body.username) - - await ctx.master.removeSessionsForUser( - ctx.params.appname, - ctx.request.body.username - ) - ctx.response.status = StatusCodes.OK - }) - .get("/:appname/api/users", async ctx => { - ctx.body = await ctx.instance.authApi.getUsers() - ctx.response.status = StatusCodes.OK - }) - .get("/:appname/api/accessLevels", async ctx => { - ctx.body = await ctx.instance.authApi.getAccessLevels() - ctx.response.status = StatusCodes.OK - }) - .get("/:appname/api/listRecords/*", async ctx => { - const indexkey = getRecordKey(ctx.params.appname, ctx.request.path) - ctx.body = await ctx.instance.indexApi.listItems(indexkey) - ctx.response.status = StatusCodes.OK - }) - .post("/:appname/api/listRecords/*", async ctx => { - const indexkey = getRecordKey(ctx.params.appname, ctx.request.path) - ctx.body = await ctx.instance.indexApi.listItems(indexkey, { - rangeStartParams: ctx.request.body.rangeStartParams, - rangeEndParams: ctx.request.body.rangeEndParams, - searchPhrase: ctx.request.body.searchPhrase, - }) - ctx.response.status = StatusCodes.OK - }) - .post("/:appname/api/aggregates/*", async ctx => { - const indexkey = getRecordKey(ctx.params.appname, ctx.request.path) - ctx.body = await ctx.instance.indexApi.aggregates(indexkey, { - rangeStartParams: ctx.request.body.rangeStartParams, - rangeEndParams: ctx.request.body.rangeEndParams, - searchPhrase: ctx.request.body.searchPhrase, - }) - ctx.response.status = StatusCodes.OK - }) - .post("/:appname/api/files/*", async ctx => { - const file = ctx.request.files.file - ctx.body = await ctx.instance.recordApi.uploadFile( - getRecordKey(ctx.params.appname, ctx.request.path), - fs.createReadStream(file.path), - file.name - ) - ctx.response.status = StatusCodes.OK - }) - .post("/:appname/api/record/*", async ctx => { - ctx.body = await ctx.instance.recordApi.save(ctx.request.body) - ctx.response.status = StatusCodes.OK - }) - .get("/:appname/api/lookup_field/*", async ctx => { - const recordKey = getRecordKey(ctx.params.appname, ctx.request.path) - const fields = ctx.query.fields.split(",") - const recordContext = await ctx.instance.recordApi.getContext(recordKey) - const allContext = [] - for (let field of fields) { - allContext.push(await recordContext.referenceOptions(field)) - } - ctx.body = allContext - ctx.response.status = StatusCodes.OK - }) - .get("/:appname/api/record/*", async ctx => { - try { - ctx.body = await ctx.instance.recordApi.load( - getRecordKey(ctx.params.appname, ctx.request.path) - ) - ctx.response.status = StatusCodes.OK - } catch (e) { - // need to be catching for 404s here - ctx.response.status = StatusCodes.INTERAL_ERROR - ctx.response.body = e.message - } - }) - .del("/:appname/api/record/*", async ctx => { - await ctx.instance.recordApi.delete( - getRecordKey(ctx.params.appname, ctx.request.path) - ) - ctx.response.status = StatusCodes.OK - }) - .post("/:appname/api/apphierarchy", async ctx => { - ctx.body = await ctx.instance.templateApi.saveApplicationHierarchy( - ctx.body - ) - ctx.response.status = StatusCodes.OK - }) - /*.post("/:appname/api/actionsAndTriggers", async (ctx) => { - ctx.body = await ctx.instance.templateApi.saveApplicationHierarchy( - ctx.body - ); - ctx.response.status = StatusCodes.OK; - }) - .get("/:appname/api/appDefinition", async (ctx) => { - ctx.body = await ctx.instance.templateApi.saveActionsAndTriggers( - ctx.body - ); - ctx.response.status = StatusCodes.OK; - })*/ - - const getRecordKey = (appname, wholePath) => - wholePath - .replace(`/${appname}/api/files/`, "") - .replace(`/${appname}/api/lookup_field/`, "") - .replace(`/${appname}/api/record/`, "") - .replace(`/${appname}/api/listRecords/`, "") - .replace(`/${appname}/api/aggregates/`, "") + .post("/:appname/api/changeMyPassword", routeHandlers.changeMyPassword) + .post( + "/_builder/instance/:appname/:instanceid/api/changeMyPassword", + routeHandlers.changeMyPassword + ) + .post( + "/:appname/api/executeAction/:actionname", + routeHandlers.executeAction + ) + .post( + "/_builder/instance/:appname/:instanceid/api/executeAction/:actionname", + routeHandlers.executeAction + ) + .post("/:appname/api/createUser", routeHandlers.createUser) + .post( + "/_builder/instance/:appname/:instanceid/api/createUser", + routeHandlers.createUser + ) + .post("/:appname/api/enableUser", routeHandlers.enableUser) + .post( + "/_builder/instance/:appname/:instanceid/api/enableUser", + routeHandlers.enableUser + ) + .post("/:appname/api/disableUser", routeHandlers.disableUser) + .post( + "/_builder/instance/:appname/:instanceid/api/disableUser", + routeHandlers.disableUser + ) + .get("/:appname/api/users", routeHandlers.getUsers) + .get( + "/_builder/instance/:appname/:instanceid/api/users", + routeHandlers.getUsers + ) + .get("/:appname/api/accessLevels", routeHandlers.getAccessLevels) + .get( + "/_builder/instance/:appname/:instanceid/api/accessLevels", + routeHandlers.getAccessLevels + ) + .get("/:appname/api/listRecords/*", routeHandlers.listRecordsGet) + .get( + "/_builder/instance/:appname/:instanceid/api/listRecords/*", + routeHandlers.listRecordsGet + ) + .post("/:appname/api/listRecords/*", routeHandlers.listRecordsPost) + .post( + "/_builder/instance/:appname/:instanceid/api/listRecords/*", + routeHandlers.listRecordsPost + ) + .post("/:appname/api/aggregates/*", routeHandlers.aggregatesPost) + .post( + "/_builder/instance/:appname/:instanceid/api/aggregates/*", + routeHandlers.aggregatesPost + ) + .post("/:appname/api/files/*", routeHandlers.postFiles) + .post( + "/_builder/instance/:appname/:instanceid/api/files/*", + routeHandlers.postFiles + ) + .post("/:appname/api/record/*", routeHandlers.saveRecord) + .post( + "/_builder/instance/:appname/:instanceid/api/record/*", + routeHandlers.saveRecord + ) + .get("/:appname/api/lookup_field/*", routeHandlers.lookupField) + .get( + "/_builder/instance/:appname/:instanceid/api/lookup_field/*", + routeHandlers.lookupField + ) + .get("/:appname/api/record/*", routeHandlers.getRecord) + .get( + "/_builder/instance/:appname/:instanceid/api/record/*", + routeHandlers.getRecord + ) + .del("/:appname/api/record/*", routeHandlers.deleteRecord) + .del( + "/_builder/instance/:appname/:instanceid/api/record/*", + routeHandlers.deleteRecord + ) + .post("/:appname/api/apphierarchy", routeHandlers.saveAppHierarchy) return router } diff --git a/packages/server/utilities/builder/index.js b/packages/server/utilities/builder/index.js index 24215083bd..4dcdefa32b 100644 --- a/packages/server/utilities/builder/index.js +++ b/packages/server/utilities/builder/index.js @@ -27,8 +27,8 @@ module.exports.saveBackend = saveBackend const getAppDefinition = async appPath => await readJSON(`${appPath}/appDefinition.json`) -module.exports.getPackageForBuilder = async (config, appname) => { - const appPath = appPackageFolder(config, appname) +module.exports.getPackageForBuilder = async (config, application) => { + const appPath = appPackageFolder(config, application.name) const pages = await getPages(appPath) @@ -40,6 +40,8 @@ module.exports.getPackageForBuilder = async (config, appname) => { pages, components: await getComponentDefinitions(appPath, pages), + + application, } } diff --git a/packages/server/utilities/masterAppInternal.js b/packages/server/utilities/masterAppInternal.js index 8da8da609f..462e628d59 100644 --- a/packages/server/utilities/masterAppInternal.js +++ b/packages/server/utilities/masterAppInternal.js @@ -6,7 +6,7 @@ const { const getDatastore = require("./datastore") const getDatabaseManager = require("./databaseManager") const { $ } = require("@budibase/core").common -const { keyBy, values } = require("lodash/fp") +const { keyBy, values, cloneDeep } = require("lodash/fp") const { masterAppPackage, applictionVersionPackage, @@ -128,20 +128,10 @@ module.exports = async context => { const userInMaster = await getUser(app.id, username) if (!userInMaster) return null - const instance = await bb.recordApi.load(userInMaster.instance.key) - - const versionId = determineVersionId(instance.version) - - const dsConfig = JSON.parse(instance.datastoreconfig) - const appPackage = await applictionVersionPackage( - context, + const { instance, bbInstance } = await getFullAccessApiForInstanceId( appname, - versionId, - instance.key - ) - const bbInstance = await getApisWithFullAccess( - datastoreModule.getDatastore(dsConfig), - appPackage + userInMaster.instance.id, + app.id ) const authUser = await bbInstance.authApi.authenticate(username, password) @@ -164,6 +154,34 @@ module.exports = async context => { return session } + const getFullAccessApiForInstanceId = async (appname, instanceId, appId) => { + if (!appId) { + appId = (await getApplication(appname)).id + } + const instanceKey = `/applications/${appId}/instances/${instanceId}` + const instance = await bb.recordApi.load(instanceKey) + + const versionId = determineVersionId(instance.version) + + const dsConfig = JSON.parse(instance.datastoreconfig) + const appPackage = await applictionVersionPackage( + context, + appname, + versionId, + instance.key + ) + return { + bbInstance: await getApisWithFullAccess( + datastoreModule.getDatastore(dsConfig), + appPackage + ), + instance, + } + } + + const getFullAccessApiForMaster = async () => + await getApisWithFullAccess(masterDatastore, masterAppPackage(context)) + const getInstanceApiForSession = async (appname, sessionId) => { if (isMaster(appname)) { const customId = bb.recordApi.customId("mastersession", sessionId) @@ -295,6 +313,14 @@ module.exports = async context => { } } + const getApplicationWithInstances = async appname => { + const app = cloneDeep(await getApplication(appname)) + app.instances = await bb.indexApi.listItems( + `/applications/${app.id}/allinstances` + ) + return app + } + const disableUser = async (app, username) => { await removeSessionsForUser(app.name, username) const userInMaster = await getUser(app.id, username) @@ -324,5 +350,8 @@ module.exports = async context => { createAppUser, bbMaster: bb, listApplications, + getFullAccessApiForInstanceId, + getFullAccessApiForMaster, + getApplicationWithInstances, } }