budibase/packages/server/middleware/routers.js

404 lines
13 KiB
JavaScript
Raw Normal View History

2019-06-28 23:59:27 +02:00
const Router = require("@koa/router");
2019-06-14 11:05:46 +02:00
const session = require("./session");
const StatusCodes = require("../utilities/statusCodes");
2019-06-28 23:59:27 +02:00
const fs = require("fs");
2019-07-15 07:59:46 +02:00
const { resolve } = require("path");
const send = require('koa-send');
2019-07-26 18:08:59 +02:00
const {
getPackageForBuilder,
getRootComponents,
savePackage,
getApps,
saveDerivedComponent,
2019-07-27 08:43:03 +02:00
renameDerivedComponent,
2019-08-19 22:18:23 +02:00
deleteDerivedComponent,
2019-09-06 14:04:23 +02:00
componentLibraryInfo
2019-07-26 18:08:59 +02:00
} = require("../utilities/builder");
2019-06-28 23:59:27 +02:00
2019-07-15 07:59:46 +02:00
const builderPath = resolve(__dirname, "../builder");
2019-06-14 11:05:46 +02:00
module.exports = (config, app) => {
2019-06-14 18:01:01 +02:00
const router = new Router();
2019-06-14 11:05:46 +02:00
2019-07-15 07:59:46 +02:00
const prependSlash = path =>
path.startsWith("/")
? path
: `/${path}`;
2019-06-14 11:05:46 +02:00
router
2019-06-14 18:01:01 +02:00
.use(session(config, app))
.use(async (ctx, next) => {
ctx.sessionId = ctx.session._sessCtx.externalKey;
ctx.session.accessed = true;
2019-07-16 23:14:57 +02:00
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);
2019-07-16 23:14:57 +02:00
if(appname === "_builder") {
await next();
} else {
const instance = await ctx.master.getInstanceApiForSession(
appname,
ctx.sessionId);
ctx.instance = instance.instance;
ctx.publicPath = instance.publicPath;
2019-09-12 16:55:36 +02:00
ctx.sharedPath = instance.sharedPath;
ctx.isAuthenticated = !!instance.instance;
await next();
}
2019-06-14 18:01:01 +02:00
})
2019-07-13 11:35:57 +02:00
.get("/_builder", async (ctx) => {
2019-07-15 07:59:46 +02:00
if(!config.dev) {
ctx.response.status = StatusCodes.FORBIDDEN;
ctx.body = "run in dev mode to access builder";
2019-07-15 07:59:46 +02:00
return;
}
await send(ctx, "/index.html", { root: builderPath });
})
2019-09-09 06:24:14 +02:00
.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});
})
2019-10-07 07:03:41 +02:00
.get("/_builder/:appname/componentLibraryGenerators", async (ctx) => {
const info = await componentLibraryInfo(
config,
ctx.params.appname,
ctx.query.lib);
await send(ctx, info.generators._lib || "generators.js", { root: info.libDir});
})
2019-07-15 07:59:46 +02:00
.get("/_builder/*", async (ctx, next) => {
if(!config.dev) {
ctx.response.status = StatusCodes.FORBIDDEN;
ctx.body = "run in dev mode to access builder";
2019-07-15 07:59:46 +02:00
return;
}
const path = ctx.path.replace("/_builder", "");
if(path.startsWith("/api/")) {
await next();
2019-07-16 23:14:57 +02:00
} else {
await send(ctx, path, { root: builderPath });
2019-07-15 07:59:46 +02:00
}
2019-07-13 11:35:57 +02:00
})
2019-06-14 18:01:01 +02:00
.post("/:appname/api/authenticate", async (ctx, next) => {
2019-06-14 11:05:46 +02:00
const user = await ctx.master.authenticate(
2019-06-14 18:01:01 +02:00
ctx.sessionId,
2019-06-14 11:05:46 +02:00
ctx.params.appname,
ctx.request.body.username,
ctx.request.body.password
);
if(!user) {
ctx.throw(StatusCodes.UNAUTHORIZED, "invalid username or password");
}
2019-10-18 18:32:03 +02:00
ctx.body = user.user_json;
2019-06-14 18:01:01 +02:00
ctx.response.status = StatusCodes.OK;
2019-06-14 11:05:46 +02:00
})
2019-06-14 18:01:01 +02:00
.post("/:appname/api/setPasswordFromTemporaryCode", async (ctx) => {
const instanceApi = await ctx.master.getFullAccessInstanceApiForUsername(
ctx.params.appname,
ctx.request.body.username
);
2019-07-07 10:03:37 +02:00
if(!instanceApi) {
ctx.request.status = StatusCodes.OK;
return;
}
await instanceApi.authApi.setPasswordFromTemporaryCode(
ctx.request.body.tempCode,
2019-06-26 23:29:28 +02:00
ctx.request.body.newPassword);
ctx.response.status = StatusCodes.OK;
2019-06-16 00:55:32 +02:00
})
.post("/:appname/api/createTemporaryAccess", async (ctx) => {
const instanceApi = await ctx.master.getFullAccessInstanceApiForUsername(
ctx.params.appname,
ctx.request.body.username
);
2019-07-07 10:03:37 +02:00
if(!instanceApi) {
ctx.request.status = StatusCodes.OK;
return;
}
await instanceApi.authApi.createTemporaryAccess(
ctx.request.body.username);
2019-06-16 00:55:32 +02:00
ctx.response.status = StatusCodes.OK;
2019-06-14 11:05:46 +02:00
})
.get("/_builder/api/apps", async (ctx) => {
2019-10-11 18:14:23 +02:00
ctx.body = await getApps(config, ctx.master);
ctx.response.status = StatusCodes.OK;
})
2019-07-13 11:35:57 +02:00
.get("/_builder/api/:appname/appPackage", async (ctx) => {
ctx.body = await getPackageForBuilder(
config,
ctx.params.appname);
ctx.response.status = StatusCodes.OK;
})
.post("/_builder/api/:appname/appPackage", async (ctx) => {
2019-07-26 18:08:59 +02:00
2019-07-13 11:35:57 +02:00
ctx.body = await savePackage(
config,
ctx.params.appname,
ctx.request.body);
ctx.response.status = StatusCodes.OK;
2019-07-13 11:35:57 +02:00
})
2019-07-26 18:08:59 +02:00
.get("/_builder/api/:appname/rootcomponents", async (ctx) => {
try {
2019-07-26 18:08:59 +02:00
ctx.body = getRootComponents(
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;
}
}
})
2019-09-09 06:24:14 +02:00
.get("/_builder/api/:appname/componentlibrary", async (ctx) => {
2019-09-06 14:04:23 +02:00
const info = await componentLibraryInfo(
2019-08-19 22:18:23 +02:00
config,
ctx.params.appname,
2019-09-09 06:24:14 +02:00
ctx.query.lib ? decodeURI(ctx.query.lib) : "");
ctx.body = info.components;
ctx.response.status = StatusCodes.OK;
2019-08-19 22:18:23 +02:00
})
2019-10-07 07:03:41 +02:00
.get("/_builder/api/:appname/generators", async (ctx) => {
const info = await componentLibraryInfo(
config,
ctx.params.appname,
ctx.query.lib ? decodeURI(ctx.query.lib) : "");
ctx.body = info.generators;
ctx.response.status = StatusCodes.OK;
})
2019-07-26 18:08:59 +02:00
.post("/_builder/api/:appname/derivedcomponent", async (ctx) => {
await saveDerivedComponent(
config,
ctx.params.appname,
ctx.request.body);
ctx.response.status = StatusCodes.OK;
})
.patch("/_builder/api/:appname/derivedcomponent", async (ctx) => {
await renameDerivedComponent(
config,
ctx.params.appname,
ctx.request.body.oldname,
ctx.request.body.newname);
ctx.response.status = StatusCodes.OK;
})
2019-07-27 08:43:03 +02:00
.delete("/_builder/api/:appname/derivedcomponent/*", async (ctx) => {
const name = ctx.request.path.replace(
`/_builder/api/${ctx.params.appname}/derivedcomponent/`, "");
2019-07-26 18:08:59 +02:00
await deleteDerivedComponent(
config,
ctx.params.appname,
2019-09-09 06:24:14 +02:00
decodeURI(name));
2019-07-26 18:08:59 +02:00
ctx.response.status = StatusCodes.OK;
})
2019-07-16 23:14:57 +02:00
.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}`, "");
2019-06-14 18:01:01 +02:00
2019-07-16 23:14:57 +02:00
if(path.startsWith("/api/")) {
await next();
2019-09-12 16:55:36 +02:00
} else if(path.startsWith("/_shared/")) {
await send(
ctx,
path.replace(`/_shared/`, ""),
{ root: ctx.sharedPath });
}
else {
2019-07-16 23:14:57 +02:00
await send(ctx, path, { root: ctx.publicPath });
}
})
.use(async (ctx, next) => {
if(ctx.isAuthenticated) {
2019-06-21 15:00:24 +02:00
await next();
2019-07-16 23:14:57 +02:00
} else {
ctx.response.status = StatusCodes.UNAUTHORIZED;
2019-06-21 15:00:24 +02:00
}
2019-06-14 11:05:46 +02:00
})
2019-06-16 00:55:32 +02:00
.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;
})
2019-06-14 18:01:01 +02:00
.post("/:appname/api/executeAction/:actionname", async (ctx) => {
2019-06-16 00:55:32 +02:00
ctx.body = await ctx.instance.actionApi.execute(
ctx.request.body.actionname,
ctx.request.body.parameters);
ctx.response.status = StatusCodes.OK;
2019-06-14 11:05:46 +02:00
})
2019-06-14 18:01:01 +02:00
.post("/:appname/api/createUser", async (ctx) => {
await ctx.instance.authApi.createUser(
ctx.request.body.user,
ctx.request.body.password
);
2019-06-14 11:05:46 +02:00
2019-06-14 18:01:01 +02:00
ctx.response.status = StatusCodes.OK;
2019-06-14 11:05:46 +02:00
})
2019-06-14 18:01:01 +02:00
.post("/:appname/api/enableUser", async (ctx) => {
2019-06-16 00:55:32 +02:00
await ctx.instance.authApi.enableUser(
ctx.request.body.username);
ctx.response.status = StatusCodes.OK;
2019-06-14 11:05:46 +02:00
})
2019-06-14 18:01:01 +02:00
.post("/:appname/api/disableUser", async (ctx) => {
2019-06-16 00:55:32 +02:00
await ctx.instance.authApi.disableUser(
ctx.request.body.username);
2019-06-19 23:05:53 +02:00
await ctx.master.removeSessionsForUser(
ctx.params.appname,
ctx.request.body.username
);
2019-06-16 00:55:32 +02:00
ctx.response.status = StatusCodes.OK;
2019-06-14 11:05:46 +02:00
})
2019-06-14 18:01:01 +02:00
.get("/:appname/api/users", async (ctx) => {
2019-06-15 00:03:01 +02:00
ctx.body = await ctx.instance.authApi.getUsers();
ctx.response.status = StatusCodes.OK;
2019-06-14 11:05:46 +02:00
})
2019-06-14 18:01:01 +02:00
.get("/:appname/api/accessLevels", async (ctx) => {
2019-06-16 00:55:32 +02:00
ctx.body = await ctx.instance.authApi.getAccessLevels();
ctx.response.status = StatusCodes.OK;
2019-06-14 11:05:46 +02:00
})
2019-10-03 07:12:13 +02:00
.get("/:appname/api/listRecords/:indexkey", async (ctx) => {
ctx.body = await ctx.instance.indexApi.listItems(
ctx.params.indexkey);
ctx.response.status = StatusCodes.OK;
})
2019-06-14 18:01:01 +02:00
.post("/:appname/api/listRecords/:indexkey", async (ctx) => {
2019-06-16 00:55:32 +02:00
ctx.body = await ctx.instance.indexApi.listItems(
ctx.request.body.indexKey,
{
rangeStartParams:ctx.request.body.rangeStartParams,
rangeEndParams:ctx.request.body.rangeEndParams,
searchPhrase:ctx.request.body.searchPhrase
}
);
ctx.response.status = StatusCodes.OK;
2019-06-14 11:05:46 +02:00
})
2019-06-16 00:55:32 +02:00
.post("/:appname/api/aggregates/:indexkey", async (ctx) => {
ctx.body = await ctx.instance.indexApi.aggregates(
ctx.request.body.indexKey,
{
rangeStartParams:ctx.request.body.rangeStartParams,
rangeEndParams:ctx.request.body.rangeEndParams,
searchPhrase:ctx.request.body.searchPhrase
}
);
ctx.response.status = StatusCodes.OK;
2019-06-14 11:05:46 +02:00
})
2019-06-28 23:59:27 +02:00
.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) => {
2019-06-16 00:55:32 +02:00
ctx.body = await ctx.instance.recordApi.save(
ctx.request.body
);
ctx.response.status = StatusCodes.OK;
2019-06-14 11:05:46 +02:00
})
.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;
})
2019-06-28 23:59:27 +02:00
.get("/:appname/api/record/*", async (ctx) => {
2019-07-11 10:43:47 +02:00
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;
}
2019-06-14 11:05:46 +02:00
})
2019-06-28 23:59:27 +02:00
.del("/:appname/api/record/*", async (ctx) => {
2019-06-16 00:55:32 +02:00
await ctx.instance.recordApi.delete(
2019-06-28 23:59:27 +02:00
getRecordKey(ctx.params.appname, ctx.request.path)
2019-06-16 00:55:32 +02:00
);
ctx.response.status = StatusCodes.OK;
2019-06-14 11:05:46 +02:00
})
2019-06-25 23:48:22 +02:00
.post("/:appname/api/apphierarchy", async (ctx) => {
ctx.body = await ctx.instance.templateApi.saveApplicationHierarchy(
2019-06-16 00:55:32 +02:00
ctx.body
);
ctx.response.status = StatusCodes.OK;
2019-09-12 16:55:36 +02:00
});
/*.post("/:appname/api/actionsAndTriggers", async (ctx) => {
2019-06-25 23:48:22 +02:00
ctx.body = await ctx.instance.templateApi.saveApplicationHierarchy(
2019-06-16 00:55:32 +02:00
ctx.body
);
ctx.response.status = StatusCodes.OK;
2019-06-14 18:01:01 +02:00
})
2019-06-16 00:55:32 +02:00
.get("/:appname/api/appDefinition", async (ctx) => {
ctx.body = await ctx.instance.templateApi.saveActionsAndTriggers(
ctx.body
);
ctx.response.status = StatusCodes.OK;
2019-09-12 16:55:36 +02:00
})*/
2019-06-14 11:05:46 +02:00
2019-06-28 23:59:27 +02:00
const getRecordKey = (appname, wholePath) =>
wholePath
.replace(`/${appname}/api/files/`, "")
.replace(`/${appname}/api/lookup_field/`, "")
.replace(`/${appname}/api/record/`, "");
2019-06-28 23:59:27 +02:00
2019-06-14 11:05:46 +02:00
return router;
}
/*
front end get authenticateTemporaryAccess {}
*/