Merge branch 'plugins-dev-experience' of github.com:Budibase/budibase into plugins-dev-experience-websocket
This commit is contained in:
commit
1f1f482f9d
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "1.2.58-alpha.6",
|
||||
"version": "1.2.59-alpha.0",
|
||||
"npmClient": "yarn",
|
||||
"packages": [
|
||||
"packages/*"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/backend-core",
|
||||
"version": "1.2.58-alpha.6",
|
||||
"version": "1.2.59-alpha.0",
|
||||
"description": "Budibase backend core libraries used in server and worker",
|
||||
"main": "dist/src/index.js",
|
||||
"types": "dist/src/index.d.ts",
|
||||
|
@ -20,7 +20,7 @@
|
|||
"test:watch": "jest --watchAll"
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/types": "1.2.58-alpha.6",
|
||||
"@budibase/types": "1.2.59-alpha.0",
|
||||
"@techpass/passport-openidconnect": "0.3.2",
|
||||
"aws-sdk": "2.1030.0",
|
||||
"bcrypt": "5.0.1",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/bbui",
|
||||
"description": "A UI solution used in the different Budibase projects.",
|
||||
"version": "1.2.58-alpha.6",
|
||||
"version": "1.2.59-alpha.0",
|
||||
"license": "MPL-2.0",
|
||||
"svelte": "src/index.js",
|
||||
"module": "dist/bbui.es.js",
|
||||
|
@ -38,7 +38,7 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
|
||||
"@budibase/string-templates": "1.2.58-alpha.6",
|
||||
"@budibase/string-templates": "1.2.59-alpha.0",
|
||||
"@spectrum-css/actionbutton": "^1.0.1",
|
||||
"@spectrum-css/actiongroup": "^1.0.1",
|
||||
"@spectrum-css/avatar": "^3.0.2",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/builder",
|
||||
"version": "1.2.58-alpha.6",
|
||||
"version": "1.2.59-alpha.0",
|
||||
"license": "GPL-3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -69,10 +69,10 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "1.2.58-alpha.6",
|
||||
"@budibase/client": "1.2.58-alpha.6",
|
||||
"@budibase/frontend-core": "1.2.58-alpha.6",
|
||||
"@budibase/string-templates": "1.2.58-alpha.6",
|
||||
"@budibase/bbui": "1.2.59-alpha.0",
|
||||
"@budibase/client": "1.2.59-alpha.0",
|
||||
"@budibase/frontend-core": "1.2.59-alpha.0",
|
||||
"@budibase/string-templates": "1.2.59-alpha.0",
|
||||
"@sentry/browser": "5.19.1",
|
||||
"@spectrum-css/page": "^3.0.1",
|
||||
"@spectrum-css/vars": "^3.0.1",
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
export let scrollable = false
|
||||
export let highlighted = false
|
||||
export let rightAlignIcon = false
|
||||
export let id
|
||||
|
||||
const scrollApi = getContext("scroll")
|
||||
const dispatch = createEventDispatcher()
|
||||
|
@ -58,6 +59,7 @@
|
|||
on:click={onClick}
|
||||
ondragover="return false"
|
||||
ondragenter="return false"
|
||||
{id}
|
||||
>
|
||||
<div class="nav-item-content" bind:this={contentRef}>
|
||||
{#if withArrow}
|
||||
|
|
|
@ -7,14 +7,15 @@
|
|||
$: noPaste = !$store.componentToPaste
|
||||
|
||||
const keyboardEvent = (key, ctrlKey = false) => {
|
||||
// Ensure this component is selected first
|
||||
if (component._id !== $store.selectedComponentId) {
|
||||
store.update(state => {
|
||||
state.selectedComponentId = component._id
|
||||
return state
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("component-menu", {
|
||||
detail: {
|
||||
key,
|
||||
ctrlKey,
|
||||
id: component?._id,
|
||||
},
|
||||
})
|
||||
}
|
||||
document.dispatchEvent(new KeyboardEvent("keydown", { key, ctrlKey }))
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
<script>
|
||||
import { onMount } from "svelte"
|
||||
import { selectedComponent, selectedScreen, store } from "builderStore"
|
||||
import { findComponent } from "builderStore/componentUtils"
|
||||
import { goto, isActive } from "@roxi/routify"
|
||||
import { notifications } from "@budibase/bbui"
|
||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||
|
||||
let confirmDeleteDialog
|
||||
let componentToDelete
|
||||
|
||||
const keyHandlers = {
|
||||
["^ArrowUp"]: async component => {
|
||||
await store.actions.components.moveUp(component)
|
||||
},
|
||||
["^ArrowDown"]: async component => {
|
||||
await store.actions.components.moveDown(component)
|
||||
},
|
||||
["^c"]: component => {
|
||||
store.actions.components.copy(component, false)
|
||||
},
|
||||
["^x"]: component => {
|
||||
store.actions.components.copy(component, true)
|
||||
},
|
||||
["^v"]: async component => {
|
||||
await store.actions.components.paste(component, "inside")
|
||||
},
|
||||
["^d"]: async component => {
|
||||
store.actions.components.copy(component)
|
||||
await store.actions.components.paste(component, "below")
|
||||
},
|
||||
["^Enter"]: () => {
|
||||
$goto("./new")
|
||||
},
|
||||
["Delete"]: component => {
|
||||
// Don't show confirmation for the screen itself
|
||||
if (component?._id === $selectedScreen.props._id) {
|
||||
return false
|
||||
}
|
||||
componentToDelete = component
|
||||
confirmDeleteDialog.show()
|
||||
},
|
||||
["ArrowUp"]: () => {
|
||||
store.actions.components.selectPrevious()
|
||||
},
|
||||
["ArrowDown"]: () => {
|
||||
store.actions.components.selectNext()
|
||||
},
|
||||
["Escape"]: () => {
|
||||
if (!$isActive("/new")) {
|
||||
return false
|
||||
}
|
||||
$goto("./")
|
||||
},
|
||||
}
|
||||
|
||||
const handleKeyAction = async (component, key, ctrlKey = false) => {
|
||||
if (!component || !key) {
|
||||
return false
|
||||
}
|
||||
try {
|
||||
// Delete and backspace are the same
|
||||
if (key === "Backspace") {
|
||||
key = "Delete"
|
||||
}
|
||||
// Prefix key with a caret for ctrl modifier
|
||||
if (ctrlKey) {
|
||||
key = "^" + key
|
||||
}
|
||||
const handler = keyHandlers[key]
|
||||
if (!handler) {
|
||||
return false
|
||||
}
|
||||
return handler(component)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
notifications.error("Error handling key press")
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyPress = async e => {
|
||||
// Ignore repeating events
|
||||
if (e.repeat) {
|
||||
return
|
||||
}
|
||||
// Ignore events when typing
|
||||
const activeTag = document.activeElement?.tagName.toLowerCase()
|
||||
if (["input", "textarea"].indexOf(activeTag) !== -1 && e.key !== "Escape") {
|
||||
return
|
||||
}
|
||||
// Key events are always for the selected component
|
||||
return handleKeyAction($selectedComponent, e.key, e.ctrlKey || e.metaKey)
|
||||
}
|
||||
|
||||
const handleComponentMenu = async e => {
|
||||
// Menu events can be for any component
|
||||
const { id, key, ctrlKey } = e.detail
|
||||
const component = findComponent($selectedScreen.props, id)
|
||||
return await handleKeyAction(component, key, ctrlKey)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
document.addEventListener("keydown", handleKeyPress)
|
||||
document.addEventListener("component-menu", handleComponentMenu)
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyPress)
|
||||
document.removeEventListener("component-menu", handleComponentMenu)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<ConfirmDialog
|
||||
bind:this={confirmDeleteDialog}
|
||||
title="Confirm Deletion"
|
||||
body={`Are you sure you want to delete "${componentToDelete?._instanceName}"?`}
|
||||
okText="Delete Component"
|
||||
onOk={() => store.actions.components.delete(componentToDelete)}
|
||||
/>
|
|
@ -2,62 +2,15 @@
|
|||
import Panel from "components/design/Panel.svelte"
|
||||
import ComponentTree from "./ComponentTree.svelte"
|
||||
import { dndStore } from "./dndStore.js"
|
||||
import { goto, isActive } from "@roxi/routify"
|
||||
import { store, selectedScreen, selectedComponent } from "builderStore"
|
||||
import { goto } from "@roxi/routify"
|
||||
import { store, selectedScreen } from "builderStore"
|
||||
import NavItem from "components/common/NavItem.svelte"
|
||||
import ScreenslotDropdownMenu from "./ScreenslotDropdownMenu.svelte"
|
||||
import { setContext, onMount } from "svelte"
|
||||
import { get } from "svelte/store"
|
||||
import DNDPositionIndicator from "./DNDPositionIndicator.svelte"
|
||||
import { DropPosition } from "./dndStore"
|
||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||
import { notifications, Button } from "@budibase/bbui"
|
||||
|
||||
let scrollRef
|
||||
let confirmDeleteDialog
|
||||
|
||||
const scrollTo = bounds => {
|
||||
if (!bounds) {
|
||||
return
|
||||
}
|
||||
const sidebarWidth = 259
|
||||
const navItemHeight = 32
|
||||
const { scrollLeft, scrollTop, offsetHeight } = scrollRef
|
||||
let scrollBounds = scrollRef.getBoundingClientRect()
|
||||
let newOffsets = {}
|
||||
|
||||
// Calculate left offset
|
||||
const offsetX = bounds.left + bounds.width + scrollLeft - 36
|
||||
if (offsetX > sidebarWidth) {
|
||||
newOffsets.left = offsetX - sidebarWidth
|
||||
} else {
|
||||
newOffsets.left = 0
|
||||
}
|
||||
if (newOffsets.left === scrollLeft) {
|
||||
delete newOffsets.left
|
||||
}
|
||||
|
||||
// Calculate top offset
|
||||
const offsetY = bounds.top - scrollBounds?.top + scrollTop
|
||||
if (offsetY > scrollTop + offsetHeight - 2 * navItemHeight) {
|
||||
newOffsets.top = offsetY - offsetHeight + 2 * navItemHeight
|
||||
} else if (offsetY < scrollTop + navItemHeight) {
|
||||
newOffsets.top = offsetY - navItemHeight
|
||||
} else {
|
||||
delete newOffsets.top
|
||||
}
|
||||
|
||||
// Skip if offset is unchanged
|
||||
if (newOffsets.left == null && newOffsets.top == null) {
|
||||
return
|
||||
}
|
||||
|
||||
// Smoothly scroll to the offset
|
||||
scrollRef.scroll({
|
||||
...newOffsets,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
import ComponentKeyHandler from "./ComponentKeyHandler.svelte"
|
||||
import ComponentScrollWrapper from "./ComponentScrollWrapper.svelte"
|
||||
|
||||
const onDrop = async () => {
|
||||
try {
|
||||
|
@ -67,95 +20,15 @@
|
|||
notifications.error("Error saving component")
|
||||
}
|
||||
}
|
||||
|
||||
// Set scroll context so components can invoke scrolling when selected
|
||||
setContext("scroll", {
|
||||
scrollTo,
|
||||
})
|
||||
|
||||
const deleteComponent = async () => {
|
||||
await store.actions.components.delete(get(selectedComponent))
|
||||
}
|
||||
|
||||
const handleKeyPress = async e => {
|
||||
// Ignore repeating events
|
||||
if (e.repeat) {
|
||||
return
|
||||
}
|
||||
// Ignore events when typing
|
||||
const activeTag = document.activeElement?.tagName.toLowerCase()
|
||||
if (["input", "textarea"].indexOf(activeTag) !== -1 && e.key !== "Escape") {
|
||||
return
|
||||
}
|
||||
const component = get(selectedComponent)
|
||||
try {
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
if (e.key === "ArrowUp") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.moveUp(component)
|
||||
} else if (e.key === "ArrowDown") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.moveDown(component)
|
||||
} else if (e.key === "c") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.copy(component, false)
|
||||
} else if (e.key === "x") {
|
||||
e.preventDefault()
|
||||
store.actions.components.copy(component, true)
|
||||
} else if (e.key === "v") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.paste(component, "inside")
|
||||
} else if (e.key === "d") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.copy(component)
|
||||
await store.actions.components.paste(component, "below")
|
||||
} else if (e.key === "Enter") {
|
||||
e.preventDefault()
|
||||
$goto("./new")
|
||||
}
|
||||
} else if (e.key === "Backspace" || e.key === "Delete") {
|
||||
// Don't show confirmation for the screen itself
|
||||
if (component._id === get(selectedScreen).props._id) {
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
confirmDeleteDialog.show()
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.selectPrevious()
|
||||
} else if (e.key === "ArrowDown") {
|
||||
e.preventDefault()
|
||||
await store.actions.components.selectNext()
|
||||
} else if (e.key === "Escape" && $isActive("./new")) {
|
||||
e.preventDefault()
|
||||
$goto("./")
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
notifications.error("Error handling key press")
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
document.addEventListener("keydown", handleKeyPress)
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyPress)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<Panel title="Components" showExpandIcon borderRight>
|
||||
<div class="add-component">
|
||||
<Button on:click={() => $goto("./new")} cta>Add component</Button>
|
||||
</div>
|
||||
<div class="nav-items-container" bind:this={scrollRef}>
|
||||
<ComponentScrollWrapper>
|
||||
<ul>
|
||||
<li
|
||||
on:click={() => {
|
||||
$store.selectedComponentId = $selectedScreen?.props._id
|
||||
}}
|
||||
id={`component-${$selectedScreen?.props._id}`}
|
||||
>
|
||||
<li>
|
||||
<NavItem
|
||||
text="Screen"
|
||||
indentLevel={0}
|
||||
|
@ -164,6 +37,10 @@
|
|||
scrollable
|
||||
icon="WebPage"
|
||||
on:drop={onDrop}
|
||||
on:click={() => {
|
||||
$store.selectedComponentId = $selectedScreen?.props._id
|
||||
}}
|
||||
id={`component-${$selectedScreen?.props._id}`}
|
||||
>
|
||||
<ScreenslotDropdownMenu component={$selectedScreen?.props} />
|
||||
</NavItem>
|
||||
|
@ -187,15 +64,9 @@
|
|||
{/if}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</ComponentScrollWrapper>
|
||||
</Panel>
|
||||
<ConfirmDialog
|
||||
bind:this={confirmDeleteDialog}
|
||||
title="Confirm Deletion"
|
||||
body={`Are you sure you want to delete "${$selectedComponent?._instanceName}"?`}
|
||||
okText="Delete Component"
|
||||
onOk={deleteComponent}
|
||||
/>
|
||||
<ComponentKeyHandler />
|
||||
|
||||
<style>
|
||||
.add-component {
|
||||
|
@ -205,12 +76,6 @@
|
|||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
.nav-items-container {
|
||||
padding: var(--spacing-xl) 0;
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
height: 0;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
<script>
|
||||
import { setContext } from "svelte"
|
||||
import { dndStore } from "./dndStore"
|
||||
import { notifications } from "@budibase/bbui"
|
||||
|
||||
let scrollRef
|
||||
|
||||
const scrollTo = bounds => {
|
||||
if (!bounds) {
|
||||
return
|
||||
}
|
||||
const sidebarWidth = 259
|
||||
const navItemHeight = 32
|
||||
const { scrollLeft, scrollTop, offsetHeight } = scrollRef
|
||||
let scrollBounds = scrollRef.getBoundingClientRect()
|
||||
let newOffsets = {}
|
||||
|
||||
// Calculate left offset
|
||||
const offsetX = bounds.left + bounds.width + scrollLeft - 36
|
||||
if (offsetX > sidebarWidth) {
|
||||
newOffsets.left = offsetX - sidebarWidth
|
||||
} else {
|
||||
newOffsets.left = 0
|
||||
}
|
||||
if (newOffsets.left === scrollLeft) {
|
||||
delete newOffsets.left
|
||||
}
|
||||
|
||||
// Calculate top offset
|
||||
const offsetY = bounds.top - scrollBounds?.top + scrollTop
|
||||
if (offsetY > scrollTop + offsetHeight - 2 * navItemHeight) {
|
||||
newOffsets.top = offsetY - offsetHeight + 2 * navItemHeight
|
||||
} else if (offsetY < scrollTop + navItemHeight) {
|
||||
newOffsets.top = offsetY - navItemHeight
|
||||
} else {
|
||||
delete newOffsets.top
|
||||
}
|
||||
|
||||
// Skip if offset is unchanged
|
||||
if (newOffsets.left == null && newOffsets.top == null) {
|
||||
return
|
||||
}
|
||||
|
||||
// Smoothly scroll to the offset
|
||||
scrollRef.scroll({
|
||||
...newOffsets,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
|
||||
// Set scroll context so components can invoke scrolling when selected
|
||||
setContext("scroll", {
|
||||
scrollTo,
|
||||
})
|
||||
|
||||
const onDrop = async () => {
|
||||
try {
|
||||
await dndStore.actions.drop()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
notifications.error("Error saving component")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={scrollRef}
|
||||
on:drop={onDrop}
|
||||
ondragover="return false"
|
||||
ondragenter="return false"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
padding: var(--spacing-xl) 0;
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
height: 0;
|
||||
}
|
||||
</style>
|
|
@ -68,7 +68,8 @@
|
|||
closedNodes = closedNodes
|
||||
}
|
||||
|
||||
const onDrop = async () => {
|
||||
const onDrop = async e => {
|
||||
e.stopPropagation()
|
||||
try {
|
||||
await dndStore.actions.drop()
|
||||
} catch (error) {
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
{#if $selectedComponent}
|
||||
{#key $selectedComponent._id}
|
||||
<Panel {title} icon={componentDefinition.icon} borderLeft>
|
||||
<Panel {title} icon={componentDefinition?.icon} borderLeft>
|
||||
<ComponentSettingsSection
|
||||
{componentInstance}
|
||||
{componentDefinition}
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
const onConfirm = () => {
|
||||
let valid = true
|
||||
userData.forEach((input, index) => {
|
||||
valid = validateInput(input.email, index) && valid
|
||||
valid = validateInput(input, index) && valid
|
||||
})
|
||||
if (!valid) {
|
||||
return false
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/cli",
|
||||
"version": "1.2.58-alpha.6",
|
||||
"version": "1.2.59-alpha.0",
|
||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
|
@ -26,9 +26,9 @@
|
|||
"outputPath": "build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/backend-core": "1.2.58-alpha.6",
|
||||
"@budibase/string-templates": "1.2.58-alpha.6",
|
||||
"@budibase/types": "1.2.58-alpha.6",
|
||||
"@budibase/backend-core": "1.2.58-alpha.7",
|
||||
"@budibase/string-templates": "1.2.58-alpha.7",
|
||||
"@budibase/types": "1.2.58-alpha.7",
|
||||
"axios": "0.21.2",
|
||||
"chalk": "4.1.0",
|
||||
"cli-progress": "3.11.2",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/client",
|
||||
"version": "1.2.58-alpha.6",
|
||||
"version": "1.2.59-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": "1.2.58-alpha.6",
|
||||
"@budibase/frontend-core": "1.2.58-alpha.6",
|
||||
"@budibase/string-templates": "1.2.58-alpha.6",
|
||||
"@budibase/bbui": "1.2.59-alpha.0",
|
||||
"@budibase/frontend-core": "1.2.59-alpha.0",
|
||||
"@budibase/string-templates": "1.2.59-alpha.0",
|
||||
"@spectrum-css/button": "^3.0.3",
|
||||
"@spectrum-css/card": "^3.0.3",
|
||||
"@spectrum-css/divider": "^1.0.3",
|
||||
|
|
|
@ -144,7 +144,7 @@ const createComponentStore = () => {
|
|||
if (!Component || !schema?.schema?.name || !version) {
|
||||
return
|
||||
}
|
||||
const component = `plugin/${schema.schema.name}/${version}`
|
||||
const component = `plugin/${schema.schema.name}`
|
||||
store.update(state => {
|
||||
state.customComponentManifest[component] = {
|
||||
Component,
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@budibase/frontend-core",
|
||||
"version": "1.2.58-alpha.6",
|
||||
"version": "1.2.59-alpha.0",
|
||||
"description": "Budibase frontend core libraries used in builder and client",
|
||||
"author": "Budibase",
|
||||
"license": "MPL-2.0",
|
||||
"svelte": "src/index.js",
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "1.2.58-alpha.6",
|
||||
"@budibase/bbui": "1.2.59-alpha.0",
|
||||
"lodash": "^4.17.21",
|
||||
"svelte": "^3.46.2"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/server",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "1.2.58-alpha.6",
|
||||
"version": "1.2.59-alpha.0",
|
||||
"description": "Budibase Web Server",
|
||||
"main": "src/index.ts",
|
||||
"repository": {
|
||||
|
@ -77,11 +77,11 @@
|
|||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@apidevtools/swagger-parser": "10.0.3",
|
||||
"@budibase/backend-core": "1.2.58-alpha.6",
|
||||
"@budibase/client": "1.2.58-alpha.6",
|
||||
"@budibase/pro": "1.2.58-alpha.6",
|
||||
"@budibase/string-templates": "1.2.58-alpha.6",
|
||||
"@budibase/types": "1.2.58-alpha.6",
|
||||
"@budibase/backend-core": "1.2.59-alpha.0",
|
||||
"@budibase/client": "1.2.59-alpha.0",
|
||||
"@budibase/pro": "1.2.59-alpha.0",
|
||||
"@budibase/string-templates": "1.2.59-alpha.0",
|
||||
"@budibase/types": "1.2.59-alpha.0",
|
||||
"@bull-board/api": "3.7.0",
|
||||
"@bull-board/koa": "3.9.4",
|
||||
"@elastic/elasticsearch": "7.10.0",
|
||||
|
|
|
@ -2,6 +2,7 @@ const { DocumentType, getPluginParams } = require("../../db/utils")
|
|||
const { getComponentLibraryManifest } = require("../../utilities/fileSystem")
|
||||
const { getAppDB } = require("@budibase/backend-core/context")
|
||||
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
|
||||
const env = require("../../environment")
|
||||
|
||||
exports.fetchAppComponentDefinitions = async function (ctx) {
|
||||
try {
|
||||
|
@ -32,23 +33,26 @@ exports.fetchAppComponentDefinitions = async function (ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
// Add custom components
|
||||
const globalDB = getGlobalDB()
|
||||
const response = await globalDB.allDocs(
|
||||
getPluginParams(null, {
|
||||
include_docs: true,
|
||||
})
|
||||
)
|
||||
response.rows
|
||||
.map(row => row.doc)
|
||||
.filter(plugin => plugin.schema.type === "component")
|
||||
.forEach(plugin => {
|
||||
const fullComponentName = `plugin/${plugin.name}/${plugin.version}`
|
||||
definitions[fullComponentName] = {
|
||||
component: fullComponentName,
|
||||
...plugin.schema.schema,
|
||||
}
|
||||
})
|
||||
// for now custom components only supported in self-host
|
||||
if (env.SELF_HOSTED) {
|
||||
// Add custom components
|
||||
const globalDB = getGlobalDB()
|
||||
const response = await globalDB.allDocs(
|
||||
getPluginParams(null, {
|
||||
include_docs: true,
|
||||
})
|
||||
)
|
||||
response.rows
|
||||
.map(row => row.doc)
|
||||
.filter(plugin => plugin.schema.type === "component")
|
||||
.forEach(plugin => {
|
||||
const fullComponentName = `plugin/${plugin.name}`
|
||||
definitions[fullComponentName] = {
|
||||
component: fullComponentName,
|
||||
...plugin.schema.schema,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
ctx.body = definitions
|
||||
} catch (err) {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { ObjectStoreBuckets } from "../../constants"
|
||||
import { extractPluginTarball } from "../../utilities/fileSystem"
|
||||
import { extractPluginTarball, loadJSFile } from "../../utilities/fileSystem"
|
||||
import { getGlobalDB } from "@budibase/backend-core/tenancy"
|
||||
import { generatePluginID, getPluginParams } from "../../db/utils"
|
||||
import { uploadDirectory } from "@budibase/backend-core/objectStore"
|
||||
import { PluginType, FileType } from "@budibase/types"
|
||||
import { ClientAppSocket } from "../../app"
|
||||
import env from "../../environment"
|
||||
|
||||
export async function getPlugins(type?: PluginType) {
|
||||
const db = getGlobalDB()
|
||||
|
@ -50,6 +51,9 @@ export async function fetch(ctx: any) {
|
|||
export async function destroy(ctx: any) {}
|
||||
|
||||
export async function processPlugin(plugin: FileType) {
|
||||
if (!env.SELF_HOSTED) {
|
||||
throw new Error("Plugins not supported outside of self-host.")
|
||||
}
|
||||
const db = getGlobalDB()
|
||||
const { metadata, directory } = await extractPluginTarball(plugin)
|
||||
const version = metadata.package.version,
|
||||
|
@ -58,7 +62,7 @@ export async function processPlugin(plugin: FileType) {
|
|||
hash = metadata.schema.hash
|
||||
|
||||
// first open the tarball into tmp directory
|
||||
const bucketPath = `${name}/${version}/`
|
||||
const bucketPath = `${name}/`
|
||||
const files = await uploadDirectory(
|
||||
ObjectStoreBuckets.PLUGINS,
|
||||
directory,
|
||||
|
@ -68,8 +72,20 @@ export async function processPlugin(plugin: FileType) {
|
|||
if (!jsFile) {
|
||||
throw new Error(`Plugin missing .js file.`)
|
||||
}
|
||||
// validate the JS for a datasource
|
||||
if (metadata.schema.type === PluginType.DATASOURCE) {
|
||||
const js = loadJSFile(directory, jsFile.name)
|
||||
// TODO: this isn't safe - but we need full node environment
|
||||
// in future we should do this in a thread for safety
|
||||
try {
|
||||
eval(js)
|
||||
} catch (err: any) {
|
||||
const message = err?.message ? err.message : JSON.stringify(err)
|
||||
throw new Error(`JS invalid: ${message}`)
|
||||
}
|
||||
}
|
||||
const jsFileName = jsFile.name
|
||||
const pluginId = generatePluginID(name, version)
|
||||
const pluginId = generatePluginID(name)
|
||||
|
||||
// overwrite existing docs entirely if they exist
|
||||
let rev
|
||||
|
@ -82,11 +98,11 @@ export async function processPlugin(plugin: FileType) {
|
|||
const doc = {
|
||||
_id: pluginId,
|
||||
_rev: rev,
|
||||
...metadata,
|
||||
name,
|
||||
version,
|
||||
hash,
|
||||
description,
|
||||
...metadata,
|
||||
jsUrl: `${bucketPath}${jsFileName}`,
|
||||
}
|
||||
const response = await db.put(doc)
|
||||
|
|
|
@ -371,8 +371,8 @@ exports.getMemoryViewParams = (otherProps = {}) => {
|
|||
return getDocParams(DocumentType.MEM_VIEW, null, otherProps)
|
||||
}
|
||||
|
||||
exports.generatePluginID = (name, version) => {
|
||||
return `${DocumentType.PLUGIN}${SEPARATOR}${name}${SEPARATOR}${version}`
|
||||
exports.generatePluginID = name => {
|
||||
return `${DocumentType.PLUGIN}${SEPARATOR}${name}`
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -66,18 +66,18 @@ if (environment.SELF_HOSTED) {
|
|||
DEFINITIONS[SourceName.GOOGLE_SHEETS] = googlesheets.schema
|
||||
}
|
||||
|
||||
function isIntegrationAvailable(integration: string) {}
|
||||
|
||||
module.exports = {
|
||||
getDefinitions: async () => {
|
||||
const plugins = await getPlugins(PluginType.DATASOURCE)
|
||||
// extract the actual schema from each custom
|
||||
const pluginSchemas: { [key: string]: Integration } = {}
|
||||
for (let plugin of plugins) {
|
||||
const sourceId = plugin.name
|
||||
pluginSchemas[sourceId] = {
|
||||
...plugin.schema["schema"],
|
||||
custom: true,
|
||||
if (environment.SELF_HOSTED) {
|
||||
const plugins = await getPlugins(PluginType.DATASOURCE)
|
||||
// extract the actual schema from each custom
|
||||
for (let plugin of plugins) {
|
||||
const sourceId = plugin.name
|
||||
pluginSchemas[sourceId] = {
|
||||
...plugin.schema["schema"],
|
||||
custom: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
|
@ -89,16 +89,19 @@ module.exports = {
|
|||
if (INTEGRATIONS[integration]) {
|
||||
return INTEGRATIONS[integration]
|
||||
}
|
||||
const plugins = await getPlugins(PluginType.DATASOURCE)
|
||||
for (let plugin of plugins) {
|
||||
if (plugin.name === integration) {
|
||||
// need to use commonJS require due to its dynamic runtime nature
|
||||
return getDatasourcePlugin(
|
||||
plugin.name,
|
||||
plugin.jsUrl,
|
||||
plugin.schema?.hash
|
||||
)
|
||||
if (environment.SELF_HOSTED) {
|
||||
const plugins = await getPlugins(PluginType.DATASOURCE)
|
||||
for (let plugin of plugins) {
|
||||
if (plugin.name === integration) {
|
||||
// need to use commonJS require due to its dynamic runtime nature
|
||||
return getDatasourcePlugin(
|
||||
plugin.name,
|
||||
plugin.jsUrl,
|
||||
plugin.schema?.hash
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error("No datasource implementation found.")
|
||||
},
|
||||
}
|
||||
|
|
|
@ -103,6 +103,13 @@ exports.loadHandlebarsFile = path => {
|
|||
return fs.readFileSync(path, "utf8")
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as above just with a different name.
|
||||
*/
|
||||
exports.loadJSFile = (directory, name) => {
|
||||
return fs.readFileSync(join(directory, name), "utf8")
|
||||
}
|
||||
|
||||
/**
|
||||
* When return a file from the API need to write the file to the system temporarily so we
|
||||
* can create a read stream to send.
|
||||
|
|
|
@ -29,8 +29,9 @@ export function watch() {
|
|||
const name = split[split.length - 1]
|
||||
console.log("Importing plugin:", path)
|
||||
await processPlugin({ name, path })
|
||||
} catch (err) {
|
||||
console.log("Failed to import plugin:", err)
|
||||
} catch (err: any) {
|
||||
const message = err?.message ? err?.message : err
|
||||
console.error("Failed to import plugin:", message)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1094,12 +1094,12 @@
|
|||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@budibase/backend-core@1.2.58-alpha.6":
|
||||
version "1.2.58-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.2.58-alpha.6.tgz#5c8d56af57247ce240d4dc1ba5ca8d5dc172903b"
|
||||
integrity sha512-QMtLjvF4aKOSzlr4G3/ZREhSgD3dzm4v7l9KnYTmoeV0KkoxW1ytf1Q7fP2QWiMwWr4mhG3OJiY2x/0vsVaGiw==
|
||||
"@budibase/backend-core@1.2.59-alpha.0":
|
||||
version "1.2.59-alpha.0"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.2.59-alpha.0.tgz#34330175c26e31ec4838891e8ed561d5f75f1158"
|
||||
integrity sha512-dXVX+IKeNMUIQUAH52E/9D73s+PSkXeU5m7dGjGeiWZQ2m3CALX0XhdL64NVEmpwmKwJVEvagi26lQSYwVrSyQ==
|
||||
dependencies:
|
||||
"@budibase/types" "1.2.58-alpha.6"
|
||||
"@budibase/types" "1.2.59-alpha.0"
|
||||
"@techpass/passport-openidconnect" "0.3.2"
|
||||
aws-sdk "2.1030.0"
|
||||
bcrypt "5.0.1"
|
||||
|
@ -1178,13 +1178,13 @@
|
|||
svelte-flatpickr "^3.2.3"
|
||||
svelte-portal "^1.0.0"
|
||||
|
||||
"@budibase/pro@1.2.58-alpha.6":
|
||||
version "1.2.58-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.2.58-alpha.6.tgz#00d5c9d90109718019bc5891d3a57e6e184e137f"
|
||||
integrity sha512-EzjUVXKOP5QbtIC1+e0w5eE75Meq8ZmtzYAQ9uU7vSy3EvErxCG5+eEr0UwzHHxQNhWxqkrNQnuae7QfNy1Ytg==
|
||||
"@budibase/pro@1.2.59-alpha.0":
|
||||
version "1.2.59-alpha.0"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.2.59-alpha.0.tgz#191865bf170832b17566336b047d3461f6036009"
|
||||
integrity sha512-nP8LL0eZdWpLZ4NViou31dE3uY1dQPJDzvFE9r71OsSLG6KGHS7fkkMA+4hq3gfrsPzAIp729m6RroksqnnKGw==
|
||||
dependencies:
|
||||
"@budibase/backend-core" "1.2.58-alpha.6"
|
||||
"@budibase/types" "1.2.58-alpha.6"
|
||||
"@budibase/backend-core" "1.2.59-alpha.0"
|
||||
"@budibase/types" "1.2.59-alpha.0"
|
||||
"@koa/router" "8.0.8"
|
||||
joi "17.6.0"
|
||||
node-fetch "^2.6.1"
|
||||
|
@ -1207,10 +1207,10 @@
|
|||
svelte-apexcharts "^1.0.2"
|
||||
svelte-flatpickr "^3.1.0"
|
||||
|
||||
"@budibase/types@1.2.58-alpha.6":
|
||||
version "1.2.58-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.2.58-alpha.6.tgz#7465783174993749852f6c6be0afa67f709571f3"
|
||||
integrity sha512-+z7WZw/gunmGIQeyPnLWApiqUTqeq+Wq8m/1Utz58ImHkoM0iumKrHtevSNRxAl0YHy5s11aetI24XuoxGHaTQ==
|
||||
"@budibase/types@1.2.59-alpha.0":
|
||||
version "1.2.59-alpha.0"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.2.59-alpha.0.tgz#7d77598607865a0f5b9a7c9063e0f7a774ebd2a1"
|
||||
integrity sha512-ddoqBxRKYl3biVmg3l3jFE/eB8DvL1VqMA/sIaVYU16Js913pU0jauT9Z/y2uqiGescfoZReCabBjD7qhr6L7w==
|
||||
|
||||
"@bull-board/api@3.7.0":
|
||||
version "3.7.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/string-templates",
|
||||
"version": "1.2.58-alpha.6",
|
||||
"version": "1.2.59-alpha.0",
|
||||
"description": "Handlebars wrapper for Budibase templating.",
|
||||
"main": "src/index.cjs",
|
||||
"module": "dist/bundle.mjs",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/types",
|
||||
"version": "1.2.58-alpha.6",
|
||||
"version": "1.2.59-alpha.0",
|
||||
"description": "Budibase types",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/worker",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "1.2.58-alpha.6",
|
||||
"version": "1.2.59-alpha.0",
|
||||
"description": "Budibase background service",
|
||||
"main": "src/index.ts",
|
||||
"repository": {
|
||||
|
@ -35,10 +35,10 @@
|
|||
"author": "Budibase",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@budibase/backend-core": "1.2.58-alpha.6",
|
||||
"@budibase/pro": "1.2.58-alpha.6",
|
||||
"@budibase/string-templates": "1.2.58-alpha.6",
|
||||
"@budibase/types": "1.2.58-alpha.6",
|
||||
"@budibase/backend-core": "1.2.59-alpha.0",
|
||||
"@budibase/pro": "1.2.59-alpha.0",
|
||||
"@budibase/string-templates": "1.2.59-alpha.0",
|
||||
"@budibase/types": "1.2.59-alpha.0",
|
||||
"@koa/router": "8.0.8",
|
||||
"@sentry/node": "6.17.7",
|
||||
"@techpass/passport-openidconnect": "0.3.2",
|
||||
|
|
|
@ -291,12 +291,12 @@
|
|||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@budibase/backend-core@1.2.58-alpha.6":
|
||||
version "1.2.58-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.2.58-alpha.6.tgz#5c8d56af57247ce240d4dc1ba5ca8d5dc172903b"
|
||||
integrity sha512-QMtLjvF4aKOSzlr4G3/ZREhSgD3dzm4v7l9KnYTmoeV0KkoxW1ytf1Q7fP2QWiMwWr4mhG3OJiY2x/0vsVaGiw==
|
||||
"@budibase/backend-core@1.2.59-alpha.0":
|
||||
version "1.2.59-alpha.0"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.2.59-alpha.0.tgz#34330175c26e31ec4838891e8ed561d5f75f1158"
|
||||
integrity sha512-dXVX+IKeNMUIQUAH52E/9D73s+PSkXeU5m7dGjGeiWZQ2m3CALX0XhdL64NVEmpwmKwJVEvagi26lQSYwVrSyQ==
|
||||
dependencies:
|
||||
"@budibase/types" "1.2.58-alpha.6"
|
||||
"@budibase/types" "1.2.59-alpha.0"
|
||||
"@techpass/passport-openidconnect" "0.3.2"
|
||||
aws-sdk "2.1030.0"
|
||||
bcrypt "5.0.1"
|
||||
|
@ -325,21 +325,21 @@
|
|||
uuid "8.3.2"
|
||||
zlib "1.0.5"
|
||||
|
||||
"@budibase/pro@1.2.58-alpha.6":
|
||||
version "1.2.58-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.2.58-alpha.6.tgz#00d5c9d90109718019bc5891d3a57e6e184e137f"
|
||||
integrity sha512-EzjUVXKOP5QbtIC1+e0w5eE75Meq8ZmtzYAQ9uU7vSy3EvErxCG5+eEr0UwzHHxQNhWxqkrNQnuae7QfNy1Ytg==
|
||||
"@budibase/pro@1.2.59-alpha.0":
|
||||
version "1.2.59-alpha.0"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.2.59-alpha.0.tgz#191865bf170832b17566336b047d3461f6036009"
|
||||
integrity sha512-nP8LL0eZdWpLZ4NViou31dE3uY1dQPJDzvFE9r71OsSLG6KGHS7fkkMA+4hq3gfrsPzAIp729m6RroksqnnKGw==
|
||||
dependencies:
|
||||
"@budibase/backend-core" "1.2.58-alpha.6"
|
||||
"@budibase/types" "1.2.58-alpha.6"
|
||||
"@budibase/backend-core" "1.2.59-alpha.0"
|
||||
"@budibase/types" "1.2.59-alpha.0"
|
||||
"@koa/router" "8.0.8"
|
||||
joi "17.6.0"
|
||||
node-fetch "^2.6.1"
|
||||
|
||||
"@budibase/types@1.2.58-alpha.6":
|
||||
version "1.2.58-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.2.58-alpha.6.tgz#7465783174993749852f6c6be0afa67f709571f3"
|
||||
integrity sha512-+z7WZw/gunmGIQeyPnLWApiqUTqeq+Wq8m/1Utz58ImHkoM0iumKrHtevSNRxAl0YHy5s11aetI24XuoxGHaTQ==
|
||||
"@budibase/types@1.2.59-alpha.0":
|
||||
version "1.2.59-alpha.0"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.2.59-alpha.0.tgz#7d77598607865a0f5b9a7c9063e0f7a774ebd2a1"
|
||||
integrity sha512-ddoqBxRKYl3biVmg3l3jFE/eB8DvL1VqMA/sIaVYU16Js913pU0jauT9Z/y2uqiGescfoZReCabBjD7qhr6L7w==
|
||||
|
||||
"@cspotcode/source-map-consumer@0.8.0":
|
||||
version "0.8.0"
|
||||
|
|
Loading…
Reference in New Issue