budibase/packages/server/src/middleware/builder.js

103 lines
3.1 KiB
JavaScript
Raw Normal View History

const { APP_DEV_PREFIX } = require("../db/utils")
const {
doesUserHaveLock,
updateLock,
checkDebounce,
setDebounce,
} = require("../utilities/redis")
const { doWithDB } = require("@budibase/backend-core/db")
2022-05-05 13:52:17 +02:00
const { DocumentTypes, getGlobalIDFromUserMetadataID } = require("../db/utils")
const { PermissionTypes } = require("@budibase/backend-core/permissions")
const { app: appCache } = require("@budibase/backend-core/cache")
const DEBOUNCE_TIME_SEC = 30
/************************************************** *
* This middleware has been broken out of the *
* "authorized" middleware as it had nothing to do *
* with authorization, but requires the perms *
* imparted by it. This middleware shouldn't *
* be called directly, it should always be called *
* through the authorized middleware *
****************************************************/
async function checkDevAppLocks(ctx) {
const appId = ctx.appId
// if any public usage, don't proceed
if (!ctx.user._id && !ctx.user.userId) {
return
}
// not a development app, don't need to do anything
if (!appId || !appId.startsWith(APP_DEV_PREFIX)) {
return
}
if (!(await doesUserHaveLock(appId, ctx.user))) {
ctx.throw(400, "User does not hold app lock.")
}
// they do have lock, update it
await updateLock(appId, ctx.user)
}
async function updateAppUpdatedAt(ctx) {
const appId = ctx.appId
// if debouncing skip this update
// get methods also aren't updating
2021-05-21 16:03:28 +02:00
if (ctx.method === "GET" || (await checkDebounce(appId))) {
return
}
await doWithDB(appId, async db => {
const metadata = await db.get(DocumentTypes.APP_METADATA)
metadata.updatedAt = new Date().toISOString()
2022-05-05 13:52:17 +02:00
const getInitials = user => {
let initials = ""
initials += user.firstName ? user.firstName[0] : ""
initials += user.lastName ? user.lastName[0] : ""
return initials == "" ? undefined : initials
}
metadata.updatedBy = {
email: ctx.user.email,
firstName: ctx.user.firstName,
lastName: ctx.user.lastName,
initials: getInitials(ctx.user),
_id: getGlobalIDFromUserMetadataID(ctx.user.userId),
}
const response = await db.put(metadata)
metadata._rev = response.rev
await appCache.invalidateAppMetadata(appId, metadata)
// set a new debounce record with a short TTL
await setDebounce(appId, DEBOUNCE_TIME_SEC)
})
}
module.exports = async (ctx, permType) => {
const appId = ctx.appId
// this only functions within an app context
if (!appId) {
return
}
const isBuilderApi = permType === PermissionTypes.BUILDER
const referer = ctx.headers["referer"]
2022-05-05 13:52:17 +02:00
const overviewPath = "/builder/portal/overview/"
const overviewContext = !referer ? false : referer.includes(overviewPath)
if (overviewContext) {
return
}
const hasAppId = !referer ? false : referer.includes(appId)
const editingApp = referer ? hasAppId : false
// check this is a builder call and editing
if (!isBuilderApi || !editingApp) {
return
}
// check locks
await checkDevAppLocks(ctx)
// set updated at time on app
await updateAppUpdatedAt(ctx)
2021-05-21 15:49:59 +02:00
}