const Router = require("@koa/router") const session = require("./session") const StatusCodes = require("../utilities/statusCodes") const { resolve } = require("path") const send = require("koa-send") const routeHandlers = require("./routeHandlers") const { getPackageForBuilder, getComponentDefinitions, getApps, saveScreen, renameScreen, deleteScreen, buildPage, componentLibraryInfo, listScreens, saveBackend, } = require("../utilities/builder") const builderPath = resolve(__dirname, "../builder") module.exports = (config, app) => { const router = new Router() router .use(session(config, app)) .use(async (ctx, next) => { ctx.sessionId = ctx.session._sessCtx.externalKey ctx.session.accessed = true const pathParts = ctx.path.split("/") if (pathParts.length < 2) { ctx.throw(StatusCodes.NOT_FOUND, "App Name not declared") } const appname = pathParts[1] 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")) { const { instance, publicPath, sharedPath, } = await ctx.master.getFullAccessApiForMaster() ctx.instance = instance ctx.publicPath = publicPath ctx.sharedPath = sharedPath ctx.isAuthenticated = !!ctx.instance } else if (ctx.path.startsWith("/_builder/instance")) { const builderAppName = pathParts[3] const instanceId = pathParts[4] const { bbInstance, publicPath, sharedPath, } = await ctx.master.getFullAccessApiForInstanceId( builderAppName, instanceId ) ctx.instance = bbInstance ctx.publicPath = publicPath ctx.sharedPath = sharedPath ctx.isAuthenticated = !!ctx.instance } await next() } else { const instance = await ctx.master.getInstanceApiForSession( appname, ctx.sessionId ) ctx.instance = instance.instance ctx.publicPath = instance.publicPath ctx.sharedPath = instance.sharedPath ctx.isAuthenticated = !!instance.instance await next() } }) .get("/_builder", async ctx => { await send(ctx, "/index.html", { root: builderPath }) }) .get("/_builder/:appname/componentlibrary", async ctx => { const info = await componentLibraryInfo( config, ctx.params.appname, ctx.query.lib ) await send(ctx, info.components._lib || "index.js", { root: info.libDir }) }) .get("/_builder/*", async (ctx, next) => { const path = ctx.path.replace("/_builder", "") const isFile = new RegExp(/(.+\..{1,5})/g).test(path) if (path.startsWith("/api/") || path.startsWith("/instance/")) { await next() } else if (isFile) { await send(ctx, path, { root: builderPath }) } else { await send(ctx, "/index.html", { root: builderPath }) } }) .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 => { 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 => { try { ctx.body = getComponentDefinitions( config, ctx.params.appname, ctx.query.lib ) ctx.response.status = StatusCodes.OK } catch (e) { if (e.status) { ctx.response.status = e.status } else { throw e } } }) .get("/_builder/api/:appname/componentlibrary", async ctx => { const info = await componentLibraryInfo( config, ctx.params.appname, ctx.query.lib ? decodeURI(ctx.query.lib) : "" ) ctx.body = info.components ctx.response.status = StatusCodes.OK }) .post("/_builder/api/:appname/backend", async ctx => { await saveBackend( config, ctx.params.appname, ctx.request.body.appDefinition, ctx.request.body.accessLevels ) ctx.master.deleteLatestPackageFromCache(ctx.params.appname) ctx.response.status = StatusCodes.OK }) .post("/_builder/api/:appname/pages/:pageName", async ctx => { await buildPage( config, ctx.params.appname, ctx.params.pageName, ctx.request.body ) ctx.response.status = StatusCodes.OK }) .get("/_builder/api/:appname/pages/:pagename/screens", async ctx => { ctx.body = await listScreens( config, ctx.params.appname, ctx.params.pagename ) ctx.response.status = StatusCodes.OK }) .post("/_builder/api/:appname/pages/:pagename/screen", async ctx => { ctx.body = await saveScreen( config, ctx.params.appname, ctx.params.pagename, ctx.request.body ) ctx.response.status = StatusCodes.OK }) .patch("/_builder/api/:appname/pages/:pagename/screen", async ctx => { await renameScreen( config, ctx.params.appname, ctx.params.pagename, ctx.request.body.oldname, ctx.request.body.newname ) ctx.response.status = StatusCodes.OK }) .delete("/_builder/api/:appname/pages/:pagename/screen/*", async ctx => { const name = ctx.request.path.replace( `/_builder/api/${ctx.params.appname}/pages/${ctx.params.pagename}/screen/`, "" ) await deleteScreen( config, ctx.params.appname, ctx.params.pagename, decodeURI(name) ) ctx.response.status = StatusCodes.OK }) .get("/:appname", async ctx => { 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() } else { ctx.response.status = StatusCodes.UNAUTHORIZED } }) .post( "/_builder/instance/:appname/:instanceid/api/upgradeData", routeHandlers.upgradeData ) .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 }