Merge branch 'develop' of github.com:Budibase/budibase into fix/user-metadata
This commit is contained in:
commit
99c47ab264
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "0.9.176-alpha.0",
|
||||
"version": "0.9.180-alpha.0",
|
||||
"npmClient": "yarn",
|
||||
"packages": [
|
||||
"packages/*"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/auth",
|
||||
"version": "0.9.176-alpha.0",
|
||||
"version": "0.9.180-alpha.0",
|
||||
"description": "Authentication middlewares for budibase builder and apps",
|
||||
"main": "src/index.js",
|
||||
"author": "Budibase",
|
||||
|
|
|
@ -6,6 +6,7 @@ exports.UserStatus = {
|
|||
exports.Cookies = {
|
||||
CurrentApp: "budibase:currentapp",
|
||||
Auth: "budibase:auth",
|
||||
Init: "budibase:init",
|
||||
OIDC_CONFIG: "budibase:oidc:config",
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/bbui",
|
||||
"description": "A UI solution used in the different Budibase projects.",
|
||||
"version": "0.9.176-alpha.0",
|
||||
"version": "0.9.180-alpha.0",
|
||||
"license": "AGPL-3.0",
|
||||
"svelte": "src/index.js",
|
||||
"module": "dist/bbui.es.js",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/builder",
|
||||
"version": "0.9.176-alpha.0",
|
||||
"version": "0.9.180-alpha.0",
|
||||
"license": "AGPL-3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -65,10 +65,10 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^0.9.176-alpha.0",
|
||||
"@budibase/client": "^0.9.176-alpha.0",
|
||||
"@budibase/bbui": "^0.9.180-alpha.0",
|
||||
"@budibase/client": "^0.9.180-alpha.0",
|
||||
"@budibase/colorpicker": "1.1.2",
|
||||
"@budibase/string-templates": "^0.9.176-alpha.0",
|
||||
"@budibase/string-templates": "^0.9.180-alpha.0",
|
||||
"@sentry/browser": "5.19.1",
|
||||
"@spectrum-css/page": "^3.0.1",
|
||||
"@spectrum-css/vars": "^3.0.1",
|
||||
|
|
|
@ -8,10 +8,13 @@
|
|||
|
||||
let name
|
||||
let selectedTrigger
|
||||
let nameTouched = false
|
||||
let triggerVal
|
||||
export let webhookModal
|
||||
|
||||
$: instanceId = $database._id
|
||||
$: nameError =
|
||||
nameTouched && !name ? "Please specify a name for the automation." : null
|
||||
|
||||
async function createAutomation() {
|
||||
await automationStore.actions.create({
|
||||
|
@ -51,13 +54,18 @@
|
|||
confirmText="Save"
|
||||
size="M"
|
||||
onConfirm={createAutomation}
|
||||
disabled={!selectedTrigger}
|
||||
disabled={!selectedTrigger || !name}
|
||||
>
|
||||
<Body size="XS"
|
||||
>Please name your automation, then select a trigger. Every automation must
|
||||
start with a trigger.
|
||||
</Body>
|
||||
<Input bind:value={name} label="Name" />
|
||||
<Input
|
||||
bind:value={name}
|
||||
on:change={() => (nameTouched = true)}
|
||||
bind:error={nameError}
|
||||
label="Name"
|
||||
/>
|
||||
|
||||
<Layout noPadding>
|
||||
<Body size="S">Triggers</Body>
|
||||
|
|
|
@ -33,6 +33,16 @@
|
|||
.instanceName("Content Placeholder")
|
||||
.json()
|
||||
|
||||
// Messages that can be sent from the iframe preview to the builder
|
||||
// Budibase events are and initalisation events
|
||||
const MessageTypes = {
|
||||
IFRAME_LOADED: "iframe-loaded",
|
||||
READY: "ready",
|
||||
ERROR: "error",
|
||||
BUDIBASE: "type",
|
||||
KEYDOWN: "keydown"
|
||||
}
|
||||
|
||||
// Construct iframe template
|
||||
$: template = iframeTemplate.replace(
|
||||
/\{\{ CLIENT_LIB_PATH }}/,
|
||||
|
@ -80,46 +90,44 @@
|
|||
// Refresh the preview when required
|
||||
$: refreshContent(strippedJson)
|
||||
|
||||
onMount(() => {
|
||||
// Initialise the app when mounted
|
||||
iframe.contentWindow.addEventListener(
|
||||
"ready",
|
||||
() => {
|
||||
function receiveMessage(message) {
|
||||
const handlers = {
|
||||
[MessageTypes.READY]: () => {
|
||||
// Initialise the app when mounted
|
||||
// Display preview immediately if the intelligent loading feature
|
||||
// is not supported
|
||||
if (!loading) return
|
||||
|
||||
if (!$store.clientFeatures.intelligentLoading) {
|
||||
loading = false
|
||||
}
|
||||
refreshContent(strippedJson)
|
||||
},
|
||||
{ once: true }
|
||||
)
|
||||
|
||||
// Catch any app errors
|
||||
iframe.contentWindow.addEventListener(
|
||||
"error",
|
||||
event => {
|
||||
[MessageTypes.ERROR]: event => {
|
||||
// Catch any app errors
|
||||
loading = false
|
||||
error = event.detail || "An unknown error occurred"
|
||||
error = event.error || "An unknown error occurred"
|
||||
},
|
||||
{ once: true }
|
||||
)
|
||||
[MessageTypes.KEYDOWN]: handleKeydownEvent
|
||||
}
|
||||
|
||||
// Add listener for events sent by client library in preview
|
||||
iframe.contentWindow.addEventListener("bb-event", handleBudibaseEvent)
|
||||
iframe.contentWindow.addEventListener("keydown", handleKeydownEvent)
|
||||
const messageHandler = handlers[message.data.type] || handleBudibaseEvent
|
||||
messageHandler(message)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
window.addEventListener("message", receiveMessage)
|
||||
})
|
||||
|
||||
// Remove all iframe event listeners on component destroy
|
||||
onDestroy(() => {
|
||||
if (iframe.contentWindow) {
|
||||
iframe.contentWindow.removeEventListener("bb-event", handleBudibaseEvent)
|
||||
iframe.contentWindow.removeEventListener("keydown", handleKeydownEvent)
|
||||
window.removeEventListener("message", receiveMessage) //
|
||||
}
|
||||
})
|
||||
|
||||
const handleBudibaseEvent = event => {
|
||||
const { type, data } = event.detail
|
||||
const { type, data } = event.data
|
||||
if (type === "select-component" && data.id) {
|
||||
store.actions.components.select({ _id: data.id })
|
||||
} else if (type === "update-prop") {
|
||||
|
@ -151,13 +159,14 @@
|
|||
store.actions.components.paste(destination, data.mode)
|
||||
}
|
||||
} else {
|
||||
console.warning(`Client sent unknown event type: ${type}`)
|
||||
console.warn(`Client sent unknown event type: ${type}`)
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeydownEvent = event => {
|
||||
const { key } = event.data
|
||||
if (
|
||||
(event.key === "Delete" || event.key === "Backspace") &&
|
||||
(key === "Delete" || key === "Backspace") &&
|
||||
selectedComponentId &&
|
||||
["input", "textarea"].indexOf(
|
||||
iframe.contentWindow.document.activeElement?.tagName.toLowerCase()
|
||||
|
|
|
@ -84,17 +84,20 @@ export default `
|
|||
if (window.loadBudibase) {
|
||||
window.loadBudibase()
|
||||
document.documentElement.classList.add("loaded")
|
||||
window.dispatchEvent(new Event("iframe-loaded"))
|
||||
window.parent.postMessage({ type: "iframe-loaded" })
|
||||
} else {
|
||||
throw "The client library couldn't be loaded"
|
||||
}
|
||||
} catch (error) {
|
||||
window.dispatchEvent(new CustomEvent("error", { detail: error }))
|
||||
window.parent.postMessage({ type: "error", error })
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", receiveMessage)
|
||||
window.dispatchEvent(new Event("ready"))
|
||||
window.addEventListener("keydown", evt => {
|
||||
window.parent.postMessage({ type: "keydown", key: event.key })
|
||||
})
|
||||
window.parent.postMessage({ type: "ready" })
|
||||
</script>
|
||||
</head>
|
||||
<body/>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
Checkbox,
|
||||
} from "@budibase/bbui"
|
||||
import { store, automationStore, hostingStore } from "builderStore"
|
||||
import { admin } from "stores/portal"
|
||||
import { admin, auth } from "stores/portal"
|
||||
import { string, mixed, object } from "yup"
|
||||
import api, { get, post } from "builderStore/api"
|
||||
import analytics, { Events } from "analytics"
|
||||
|
@ -139,6 +139,7 @@
|
|||
}
|
||||
const userResp = await api.post(`/api/users/metadata/self`, user)
|
||||
await userResp.json()
|
||||
await auth.setInitInfo({})
|
||||
$goto(`/builder/app/${appJson.instance._id}`)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
@ -146,6 +147,16 @@
|
|||
submitting = false
|
||||
}
|
||||
}
|
||||
|
||||
function getModalTitle() {
|
||||
let title = "Create App"
|
||||
if (template.fromFile) {
|
||||
title = "Import App"
|
||||
} else if (template.key) {
|
||||
title = "Create app from template"
|
||||
}
|
||||
return title
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if showTemplateSelection}
|
||||
|
@ -172,7 +183,7 @@
|
|||
</ModalContent>
|
||||
{:else}
|
||||
<ModalContent
|
||||
title={template?.fromFile ? "Import app" : "Create app"}
|
||||
title={getModalTitle()}
|
||||
confirmText={template?.fromFile ? "Import app" : "Create app"}
|
||||
onConfirm={createNewApp}
|
||||
onCancel={inline ? () => (template = null) : null}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { isActive, redirect } from "@roxi/routify"
|
||||
import { isActive, redirect, params } from "@roxi/routify"
|
||||
import { admin, auth } from "stores/portal"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
|
@ -47,6 +47,10 @@
|
|||
}
|
||||
|
||||
onMount(async () => {
|
||||
if ($params["?template"]) {
|
||||
await auth.setInitInfo({ init_template: $params["?template"] })
|
||||
}
|
||||
|
||||
await auth.checkAuth()
|
||||
await admin.init()
|
||||
|
||||
|
|
|
@ -184,9 +184,27 @@
|
|||
}
|
||||
}
|
||||
|
||||
function createAppFromTemplateUrl(templateKey) {
|
||||
// validate the template key just to make sure
|
||||
const templateParts = templateKey.split("/")
|
||||
if (templateParts.length === 2 && templateParts[0] === "app") {
|
||||
template = {
|
||||
key: templateKey,
|
||||
}
|
||||
initiateAppCreation()
|
||||
} else {
|
||||
notifications.error("Your Template URL is invalid. Please try another.")
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
await apps.load()
|
||||
loaded = true
|
||||
// if the portal is loaded from an external URL with a template param
|
||||
const initInfo = await auth.getInitInfo()
|
||||
if (initInfo.init_template) {
|
||||
createAppFromTemplateUrl(initInfo.init_template)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
@ -83,6 +83,13 @@ export function createAuthStore() {
|
|||
return {
|
||||
subscribe: store.subscribe,
|
||||
setOrganisation: setOrganisation,
|
||||
getInitInfo: async () => {
|
||||
const response = await api.get(`/api/global/auth/init`)
|
||||
return await response.json()
|
||||
},
|
||||
setInitInfo: async info => {
|
||||
await api.post(`/api/global/auth/init`, info)
|
||||
},
|
||||
checkQueryString: async () => {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
if (urlParams.has("tenantId")) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/cli",
|
||||
"version": "0.9.176-alpha.0",
|
||||
"version": "0.9.180-alpha.0",
|
||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
|
|
|
@ -19,7 +19,7 @@ The object key is the name of the component, as exported by `index.js`.
|
|||
- **bindable** - whether the components provides a bindable value or not
|
||||
- **settings** - array of settings displayed in the builder
|
||||
|
||||
###Settings Definitions
|
||||
### Settings Definitions
|
||||
|
||||
The `type` field in each setting is used by the builder to know which component to use to display
|
||||
the setting, so it's important that this field is correct. The valid options are:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/client",
|
||||
"version": "0.9.176-alpha.0",
|
||||
"version": "0.9.180-alpha.0",
|
||||
"license": "MPL-2.0",
|
||||
"module": "dist/budibase-client.js",
|
||||
"main": "dist/budibase-client.js",
|
||||
|
@ -19,9 +19,9 @@
|
|||
"dev:builder": "rollup -cw"
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^0.9.176-alpha.0",
|
||||
"@budibase/bbui": "^0.9.180-alpha.0",
|
||||
"@budibase/standard-components": "^0.9.139",
|
||||
"@budibase/string-templates": "^0.9.176-alpha.0",
|
||||
"@budibase/string-templates": "^0.9.180-alpha.0",
|
||||
"regexparam": "^1.3.0",
|
||||
"shortid": "^2.2.15",
|
||||
"svelte-spa-router": "^3.0.5"
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
import { Modal, ModalContent, ActionButton } from "@budibase/bbui"
|
||||
import { onDestroy } from "svelte"
|
||||
|
||||
const MessageTypes = {
|
||||
NOTIFICATION: "notification",
|
||||
CLOSE_SCREEN_MODAL: "close-screen-modal",
|
||||
INVALIDATE_DATASOURCE: "invalidate-datasource",
|
||||
}
|
||||
|
||||
let iframe
|
||||
let listenersAttached = false
|
||||
|
||||
|
@ -21,32 +27,33 @@
|
|||
notificationStore.actions.send(message, type, icon)
|
||||
}
|
||||
|
||||
function receiveMessage(message) {
|
||||
const handlers = {
|
||||
[MessageTypes.NOTIFICATION]: () => {
|
||||
proxyNotification(message.data)
|
||||
},
|
||||
[MessageTypes.CLOSE_SCREEN_MODAL]: peekStore.actions.hidePeek,
|
||||
[MessageTypes.INVALIDATE_DATASOURCE]: () => {
|
||||
invalidateDataSource(message.data)
|
||||
},
|
||||
}
|
||||
|
||||
const messageHandler = handlers[message.data.type]
|
||||
if (messageHandler) {
|
||||
messageHandler(message)
|
||||
} else {
|
||||
console.warning("Unknown event type", message?.data?.type)
|
||||
}
|
||||
}
|
||||
|
||||
const attachListeners = () => {
|
||||
// Mirror datasource invalidation to keep the parent window up to date
|
||||
iframe.contentWindow.addEventListener(
|
||||
"invalidate-datasource",
|
||||
invalidateDataSource
|
||||
)
|
||||
// Listen for a close event to close the screen peek
|
||||
iframe.contentWindow.addEventListener(
|
||||
"close-screen-modal",
|
||||
peekStore.actions.hidePeek
|
||||
)
|
||||
// Proxy notifications back to the parent window instead of iframe
|
||||
iframe.contentWindow.addEventListener("notification", proxyNotification)
|
||||
window.addEventListener("message", receiveMessage)
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
peekStore.actions.hidePeek()
|
||||
iframe.contentWindow.removeEventListener(
|
||||
"invalidate-datasource",
|
||||
invalidateDataSource
|
||||
)
|
||||
iframe.contentWindow.removeEventListener(
|
||||
"close-screen-modal",
|
||||
peekStore.actions.hidePeek
|
||||
)
|
||||
iframe.contentWindow.removeEventListener("notification", proxyNotification)
|
||||
window.removeEventListener("message", receiveMessage)
|
||||
}
|
||||
|
||||
const handleFullscreen = () => {
|
||||
|
|
|
@ -4,11 +4,7 @@ import { findComponentById, findComponentPathById } from "../utils/components"
|
|||
import { pingEndUser } from "../api"
|
||||
|
||||
const dispatchEvent = (type, data = {}) => {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("bb-event", {
|
||||
detail: { type, data },
|
||||
})
|
||||
)
|
||||
window.parent.postMessage({ type, data })
|
||||
}
|
||||
|
||||
const createBuilderStore = () => {
|
||||
|
|
|
@ -59,11 +59,10 @@ export const createDataSourceStore = () => {
|
|||
|
||||
// Emit this as a window event, so parent screens which are iframing us in
|
||||
// can also invalidate the same datasource
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("invalidate-datasource", {
|
||||
detail: { dataSourceId },
|
||||
})
|
||||
)
|
||||
window.parent.postMessage({
|
||||
type: "close-screen-modal",
|
||||
detail: { dataSourceId },
|
||||
})
|
||||
|
||||
let invalidations = [dataSourceId]
|
||||
|
||||
|
|
|
@ -26,11 +26,14 @@ const createNotificationStore = () => {
|
|||
|
||||
// If peeking, pass notifications back to parent window
|
||||
if (get(routeStore).queryParams?.peek) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("notification", {
|
||||
detail: { message, type, icon },
|
||||
})
|
||||
)
|
||||
window.parent.postMessage({
|
||||
type: "notification",
|
||||
detail: {
|
||||
message,
|
||||
type,
|
||||
icon,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ const changeFormStepHandler = async (action, context) => {
|
|||
const closeScreenModalHandler = () => {
|
||||
// Emit this as a window event, so parent screens which are iframing us in
|
||||
// can close the modal
|
||||
window.dispatchEvent(new Event("close-screen-modal"))
|
||||
window.parent.postMessage({ type: "close-screen-modal" })
|
||||
}
|
||||
|
||||
const updateStateHandler = action => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/server",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "0.9.176-alpha.0",
|
||||
"version": "0.9.180-alpha.0",
|
||||
"description": "Budibase Web Server",
|
||||
"main": "src/index.js",
|
||||
"repository": {
|
||||
|
@ -68,9 +68,9 @@
|
|||
"author": "Budibase",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@budibase/auth": "^0.9.176-alpha.0",
|
||||
"@budibase/client": "^0.9.176-alpha.0",
|
||||
"@budibase/string-templates": "^0.9.176-alpha.0",
|
||||
"@budibase/auth": "^0.9.180-alpha.0",
|
||||
"@budibase/client": "^0.9.180-alpha.0",
|
||||
"@budibase/string-templates": "^0.9.180-alpha.0",
|
||||
"@elastic/elasticsearch": "7.10.0",
|
||||
"@koa/router": "8.0.0",
|
||||
"@sendgrid/mail": "7.1.1",
|
||||
|
|
|
@ -23,9 +23,6 @@ function formatResponse(resp) {
|
|||
try {
|
||||
resp = JSON.parse(resp)
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"Error parsing JSON response. Returning string in array instead."
|
||||
)
|
||||
resp = { response: resp }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ const { generateWebhookID, getWebhookParams } = require("../../db/utils")
|
|||
const toJsonSchema = require("to-json-schema")
|
||||
const validate = require("jsonschema").validate
|
||||
const triggers = require("../../automations/triggers")
|
||||
const { getDeployedAppID } = require("@budibase/auth/db")
|
||||
|
||||
const AUTOMATION_DESCRIPTION = "Generated from Webhook Schema"
|
||||
|
||||
|
@ -76,24 +77,34 @@ exports.buildSchema = async ctx => {
|
|||
}
|
||||
|
||||
exports.trigger = async ctx => {
|
||||
const db = new CouchDB(ctx.params.instance)
|
||||
const webhook = await db.get(ctx.params.id)
|
||||
// validate against the schema
|
||||
if (webhook.bodySchema) {
|
||||
validate(ctx.request.body, webhook.bodySchema)
|
||||
}
|
||||
const target = await db.get(webhook.action.target)
|
||||
if (webhook.action.type === exports.WebhookType.AUTOMATION) {
|
||||
// trigger with both the pure request and then expand it
|
||||
// incase the user has produced a schema to bind to
|
||||
await triggers.externalTrigger(target, {
|
||||
body: ctx.request.body,
|
||||
...ctx.request.body,
|
||||
appId: ctx.params.instance,
|
||||
})
|
||||
}
|
||||
ctx.status = 200
|
||||
ctx.body = {
|
||||
message: "Webhook trigger fired successfully",
|
||||
const prodAppId = getDeployedAppID(ctx.params.instance)
|
||||
try {
|
||||
const db = new CouchDB(prodAppId)
|
||||
const webhook = await db.get(ctx.params.id)
|
||||
// validate against the schema
|
||||
if (webhook.bodySchema) {
|
||||
validate(ctx.request.body, webhook.bodySchema)
|
||||
}
|
||||
const target = await db.get(webhook.action.target)
|
||||
if (webhook.action.type === exports.WebhookType.AUTOMATION) {
|
||||
// trigger with both the pure request and then expand it
|
||||
// incase the user has produced a schema to bind to
|
||||
await triggers.externalTrigger(target, {
|
||||
body: ctx.request.body,
|
||||
...ctx.request.body,
|
||||
appId: prodAppId,
|
||||
})
|
||||
}
|
||||
ctx.status = 200
|
||||
ctx.body = {
|
||||
message: "Webhook trigger fired successfully",
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.status === 404) {
|
||||
ctx.status = 200
|
||||
ctx.body = {
|
||||
message: "Application not deployed yet.",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,6 +85,18 @@ exports.run = async function ({ inputs }) {
|
|||
const request = {
|
||||
method: requestMethod,
|
||||
}
|
||||
if (headers) {
|
||||
try {
|
||||
const customHeaders =
|
||||
typeof headers === "string" ? JSON.parse(headers) : headers
|
||||
request.headers = { ...request.headers, ...customHeaders }
|
||||
} catch (err) {
|
||||
return {
|
||||
success: false,
|
||||
response: "Unable to process headers, must be a JSON object.",
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
requestBody &&
|
||||
requestBody.length !== 0 &&
|
||||
|
@ -95,21 +107,9 @@ exports.run = async function ({ inputs }) {
|
|||
? requestBody
|
||||
: JSON.stringify(requestBody)
|
||||
request.headers = {
|
||||
...request.headers,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
if (headers) {
|
||||
try {
|
||||
const customHeaders =
|
||||
typeof headers === "string" ? JSON.parse(headers) : headers
|
||||
request.headers = { ...request.headers, ...customHeaders }
|
||||
} catch (err) {
|
||||
return {
|
||||
success: false,
|
||||
response: "Unable to process headers, must be a JSON object.",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -122,7 +122,7 @@ exports.run = async function ({ inputs }) {
|
|||
return {
|
||||
httpStatus: status,
|
||||
response: message,
|
||||
success: status === 200,
|
||||
success: status >= 200 && status <= 206,
|
||||
}
|
||||
} catch (err) {
|
||||
/* istanbul ignore next */
|
||||
|
|
|
@ -6,6 +6,7 @@ const { queue } = require("./bullboard")
|
|||
const newid = require("../db/newid")
|
||||
const { updateEntityMetadata } = require("../utilities")
|
||||
const { MetadataTypes } = require("../constants")
|
||||
const { getDeployedAppID } = require("@budibase/auth/db")
|
||||
|
||||
const WH_STEP_ID = definitions.WEBHOOK.stepId
|
||||
const CRON_STEP_ID = definitions.CRON.stepId
|
||||
|
@ -150,9 +151,13 @@ exports.checkForWebhooks = async ({ appId, oldAuto, newAuto }) => {
|
|||
await webhooks.save(ctx)
|
||||
const id = ctx.body.webhook._id
|
||||
newTrigger.webhookId = id
|
||||
// the app ID has to be development for this endpoint
|
||||
// it can only be used when building the app
|
||||
// but the trigger endpoint will always be used in production
|
||||
const prodAppId = getDeployedAppID(appId)
|
||||
newTrigger.inputs = {
|
||||
schemaUrl: `api/webhooks/schema/${appId}/${id}`,
|
||||
triggerUrl: `api/webhooks/trigger/${appId}/${id}`,
|
||||
triggerUrl: `api/webhooks/trigger/${prodAppId}/${id}`,
|
||||
}
|
||||
}
|
||||
return newAuto
|
||||
|
|
|
@ -142,13 +142,11 @@ module RestModule {
|
|||
}
|
||||
|
||||
async parseResponse(response: any) {
|
||||
switch (this.headers.Accept) {
|
||||
case "application/json":
|
||||
return await response.json()
|
||||
case "text/html":
|
||||
return await response.text()
|
||||
default:
|
||||
return await response.json()
|
||||
const contentType = response.headers.get("content-type")
|
||||
if (contentType && contentType.indexOf("application/json") !== -1) {
|
||||
return await response.json()
|
||||
} else {
|
||||
return await response.text()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,7 +189,7 @@ module RestModule {
|
|||
}
|
||||
|
||||
const response = await fetch(this.getUrl(path, queryString), {
|
||||
method: "POST",
|
||||
method: "PUT",
|
||||
headers: this.headers,
|
||||
body: JSON.stringify(json),
|
||||
})
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
jest.mock("node-fetch", () =>
|
||||
jest.fn(() => ({ json: jest.fn(), text: jest.fn() }))
|
||||
jest.fn(() => ({
|
||||
headers: {
|
||||
get: () => ["application/json"]
|
||||
},
|
||||
json: jest.fn(),
|
||||
text: jest.fn()
|
||||
}))
|
||||
)
|
||||
const fetch = require("node-fetch")
|
||||
const RestIntegration = require("../rest")
|
||||
|
|
|
@ -124,7 +124,10 @@ function copyExistingPropsOver(
|
|||
return table
|
||||
}
|
||||
|
||||
export function finaliseExternalTables(tables: { [key: string]: any }, entities: { [key: string]: any }) {
|
||||
export function finaliseExternalTables(
|
||||
tables: { [key: string]: any },
|
||||
entities: { [key: string]: any }
|
||||
) {
|
||||
const finalTables: { [key: string]: any } = {}
|
||||
const errors: { [key: string]: string } = {}
|
||||
for (let [name, table] of Object.entries(tables)) {
|
||||
|
|
|
@ -5,21 +5,18 @@ const {
|
|||
doesHaveBasePermission,
|
||||
} = require("@budibase/auth/permissions")
|
||||
const builderMiddleware = require("./builder")
|
||||
const { isWebhookEndpoint } = require("./utils")
|
||||
|
||||
function hasResource(ctx) {
|
||||
return ctx.resourceId != null
|
||||
}
|
||||
|
||||
const WEBHOOK_ENDPOINTS = new RegExp(
|
||||
["webhooks/trigger", "webhooks/schema"].join("|")
|
||||
)
|
||||
|
||||
module.exports =
|
||||
(permType, permLevel = null) =>
|
||||
async (ctx, next) => {
|
||||
// webhooks don't need authentication, each webhook unique
|
||||
// also internal requests (between services) don't need authorized
|
||||
if (WEBHOOK_ENDPOINTS.test(ctx.request.url) || ctx.internal) {
|
||||
if (isWebhookEndpoint(ctx) || ctx.internal) {
|
||||
return next()
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ const { isUserInAppTenant } = require("@budibase/auth/tenancy")
|
|||
const { getCachedSelf } = require("../utilities/global")
|
||||
const CouchDB = require("../db")
|
||||
const env = require("../environment")
|
||||
const { isWebhookEndpoint } = require("./utils")
|
||||
|
||||
module.exports = async (ctx, next) => {
|
||||
// try to get the appID from the request
|
||||
|
@ -38,6 +39,7 @@ module.exports = async (ctx, next) => {
|
|||
// deny access to application preview
|
||||
if (
|
||||
isDevAppID(requestAppId) &&
|
||||
!isWebhookEndpoint(ctx) &&
|
||||
(!ctx.user || !ctx.user.builder || !ctx.user.builder.global)
|
||||
) {
|
||||
clearCookie(ctx, Cookies.CurrentApp)
|
||||
|
|
|
@ -2,6 +2,10 @@ const CouchDB = require("../db")
|
|||
const usageQuota = require("../utilities/usageQuota")
|
||||
const env = require("../environment")
|
||||
const { getTenantId } = require("@budibase/auth/tenancy")
|
||||
const {
|
||||
isExternalTable,
|
||||
isRowId: isExternalRowId,
|
||||
} = require("../integrations/utils")
|
||||
|
||||
// tenants without limits
|
||||
const EXCLUDED_TENANTS = ["bb", "default", "bbtest", "bbstaging"]
|
||||
|
@ -46,14 +50,24 @@ module.exports = async (ctx, next) => {
|
|||
}
|
||||
// post request could be a save of a pre-existing entry
|
||||
if (ctx.request.body && ctx.request.body._id && ctx.request.body._rev) {
|
||||
const usageId = ctx.request.body._id
|
||||
try {
|
||||
if (ctx.appId) {
|
||||
const db = new CouchDB(ctx.appId)
|
||||
await db.get(ctx.request.body._id)
|
||||
await db.get(usageId)
|
||||
}
|
||||
return next()
|
||||
} catch (err) {
|
||||
ctx.throw(404, `${ctx.request.body._id} does not exist`)
|
||||
if (
|
||||
isExternalTable(usageId) ||
|
||||
(ctx.request.body.tableId &&
|
||||
isExternalTable(ctx.request.body.tableId)) ||
|
||||
isExternalRowId(usageId)
|
||||
) {
|
||||
return next()
|
||||
} else {
|
||||
ctx.throw(404, `${usageId} does not exist`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
const WEBHOOK_ENDPOINTS = new RegExp(
|
||||
["webhooks/trigger", "webhooks/schema"].join("|")
|
||||
)
|
||||
|
||||
exports.isWebhookEndpoint = ctx => {
|
||||
return WEBHOOK_ENDPOINTS.test(ctx.request.url)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/string-templates",
|
||||
"version": "0.9.176-alpha.0",
|
||||
"version": "0.9.180-alpha.0",
|
||||
"description": "Handlebars wrapper for Budibase templating.",
|
||||
"main": "src/index.cjs",
|
||||
"module": "dist/bundle.mjs",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/worker",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "0.9.176-alpha.0",
|
||||
"version": "0.9.180-alpha.0",
|
||||
"description": "Budibase background service",
|
||||
"main": "src/index.js",
|
||||
"repository": {
|
||||
|
@ -29,8 +29,8 @@
|
|||
"author": "Budibase",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@budibase/auth": "^0.9.176-alpha.0",
|
||||
"@budibase/string-templates": "^0.9.176-alpha.0",
|
||||
"@budibase/auth": "^0.9.180-alpha.0",
|
||||
"@budibase/string-templates": "^0.9.180-alpha.0",
|
||||
"@koa/router": "^8.0.0",
|
||||
"@sentry/node": "^6.0.0",
|
||||
"@techpass/passport-openidconnect": "^0.3.0",
|
||||
|
|
|
@ -77,6 +77,17 @@ exports.authenticate = async (ctx, next) => {
|
|||
})(ctx, next)
|
||||
}
|
||||
|
||||
exports.setInitInfo = ctx => {
|
||||
const initInfo = ctx.request.body
|
||||
setCookie(ctx, initInfo, Cookies.Init)
|
||||
ctx.status = 200
|
||||
}
|
||||
|
||||
exports.getInitInfo = ctx => {
|
||||
const initInfo = getCookie(ctx, Cookies.Init)
|
||||
ctx.body = initInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the user password, used as part of a forgotten password flow.
|
||||
*/
|
||||
|
|
|
@ -56,6 +56,8 @@ router
|
|||
authController.resetUpdate
|
||||
)
|
||||
.post("/api/global/auth/logout", authController.logout)
|
||||
.post("/api/global/auth/init", authController.setInitInfo)
|
||||
.get("/api/global/auth/init", authController.getInitInfo)
|
||||
.get(
|
||||
"/api/global/auth/:tenantId/google",
|
||||
updateTenant,
|
||||
|
|
Loading…
Reference in New Issue