diff --git a/packages/builder/src/components/common/Inputs/InputGroup.svelte b/packages/builder/src/components/common/Inputs/InputGroup.svelte
index e5fed68103..385623ca19 100644
--- a/packages/builder/src/components/common/Inputs/InputGroup.svelte
+++ b/packages/builder/src/components/common/Inputs/InputGroup.svelte
@@ -19,9 +19,10 @@
onChange(_value)
}
- $: displayValues = value && suffix
- ? value.map(v => v.replace(new RegExp(`${suffix}$`), ""))
- : value || []
+ $: displayValues =
+ value && suffix
+ ? value.map(v => v.replace(new RegExp(`${suffix}$`), ""))
+ : value || []
diff --git a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte
index 53450c2200..00d7e6c171 100644
--- a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte
+++ b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte
@@ -21,7 +21,7 @@
return componentName || "element"
}
- const screenPlaceholder = {
+ const screenPlaceholder = {
name: "Screen Placeholder",
route: "*",
props: {
@@ -60,9 +60,8 @@
},
}
-
$: hasComponent = !!$store.currentPreviewItem
-
+
$: {
styles = ""
// Apply the CSS from the currently selected page and its screens
@@ -88,9 +87,9 @@
libraries: $store.libraries,
page: $store.pages[$store.currentPageName],
screens: [
- $store.currentFrontEndType === "page"
- ? screenPlaceholder
- : $store.currentPreviewItem,
+ $store.currentFrontEndType === "page"
+ ? screenPlaceholder
+ : $store.currentPreviewItem,
],
appRootPath: "",
}
@@ -102,20 +101,25 @@
: ""
const refreshContent = () => {
- iframe.contentWindow.postMessage(JSON.stringify({
- styles,
- stylesheetLinks,
- selectedComponentType,
- selectedComponentId,
- frontendDefinition,
- }))
+ iframe.contentWindow.postMessage(
+ JSON.stringify({
+ styles,
+ stylesheetLinks,
+ selectedComponentType,
+ selectedComponentId,
+ frontendDefinition,
+ })
+ )
}
- $: if(iframe) iframe.contentWindow.addEventListener("bb-ready", refreshContent, { once: true })
+ $: if (iframe)
+ iframe.contentWindow.addEventListener("bb-ready", refreshContent, {
+ once: true,
+ })
- $: if(iframe && frontendDefinition) {
- refreshContent()
- }
+ $: if (iframe && frontendDefinition) {
+ refreshContent()
+ }
diff --git a/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte b/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte
index 60279aa9a1..7d33b9c963 100644
--- a/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte
+++ b/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte
@@ -38,15 +38,30 @@
c => c._component === componentInstance._component
) || {}
- $: panelDefinition = componentPropDefinition.properties
- ? componentPropDefinition.properties[selectedCategory.value]
- : {}
+ let panelDefinition = {}
- // SCREEN PROPS =============================================
- $: screen_props =
- $store.currentFrontEndType === "page"
- ? getProps($store.currentPreviewItem, ["name", "favicon"])
- : getProps($store.currentPreviewItem, ["name", "description", "route"])
+ $: {
+ if (componentPropDefinition.properties) {
+ if (selectedCategory.value === "design") {
+ panelDefinition = componentPropDefinition.properties["design"]
+ } else {
+ let panelDef = componentPropDefinition.properties["settings"]
+ if (
+ $store.currentFrontEndType === "page" &&
+ $store.currentView !== "component"
+ ) {
+ panelDefinition = [...page, ...panelDef]
+ } else if (
+ $store.currentFrontEndType === "screen" &&
+ $store.currentView !== "component"
+ ) {
+ panelDefinition = [...screen, ...panelDef]
+ } else {
+ panelDefinition = panelDef
+ }
+ }
+ }
+ }
const onStyleChanged = store.setComponentStyle
const onPropChanged = store.setComponentProp
@@ -106,12 +121,10 @@
height: 100%;
display: flex;
flex-direction: column;
- /* Merge Check */
overflow-x: hidden;
overflow-y: hidden;
padding: 20px;
box-sizing: border-box;
- /* Merge Check */
}
.title > div:nth-child(1) {
diff --git a/packages/builder/src/components/userInterface/propertyCategories.js b/packages/builder/src/components/userInterface/propertyCategories.js
index 38ef0558e4..c0485304ce 100644
--- a/packages/builder/src/components/userInterface/propertyCategories.js
+++ b/packages/builder/src/components/userInterface/propertyCategories.js
@@ -12,8 +12,9 @@ export const layout = [
label: "Display",
key: "display",
control: OptionSelect,
- initialValue: "Flex",
+ initialValue: "",
options: [
+ { label: "", value: "" },
{ label: "Flex", value: "flex" },
{ label: "Inline Flex", value: "inline-flex" },
],
@@ -39,6 +40,7 @@ export const layout = [
control: OptionSelect,
initialValue: "Flex Start",
options: [
+ { label: "", value: "" },
{ label: "Flex Start", value: "flex-start" },
{ label: "Flex End", value: "flex-end" },
{ label: "Center", value: "center" },
@@ -317,19 +319,31 @@ export const border = [
{
label: "Radius",
key: "border-radius",
- control: Input,
- width: "48px",
- placeholder: "px",
- textAlign: "center",
+ control: OptionSelect,
+ defaultValue: "None",
+ options: [
+ { label: "None", value: "0" },
+ { label: "small", value: "0.125rem" },
+ { label: "Medium", value: "0.25rem;" },
+ { label: "Large", value: "0.375rem" },
+ { label: "Extra large", value: "0.5rem" },
+ { label: "Full", value: "5678px" },
+ ],
},
{
label: "Width",
key: "border-width",
- control: Input,
- width: "48px",
- placeholder: "px",
- textAlign: "center",
- }, //custom
+ control: OptionSelect,
+ defaultValue: "None",
+ options: [
+ { label: "None", value: "0" },
+ { label: "Extra small", value: "0.5px" },
+ { label: "Small", value: "1px" },
+ { label: "Medium", value: "2px" },
+ { label: "Large", value: "4px" },
+ { label: "Extra large", value: "8px" },
+ ],
+ },
{
label: "Color",
key: "border-color",
@@ -339,6 +353,7 @@ export const border = [
label: "Style",
key: "border-style",
control: OptionSelect,
+ defaultValue: "None",
options: [
"none",
"hidden",
@@ -365,17 +380,50 @@ export const effects = [
},
{
label: "Rotate",
- key: "transform",
- control: Input,
- width: "48px",
- textAlign: "center",
- placeholder: "deg",
+ key: "transform-rotate",
+ control: OptionSelect,
+ defaultValue: "0",
+ options: [
+ "0",
+ "45deg",
+ "90deg",
+ "90deg",
+ "135deg",
+ "180deg",
+ "225deg",
+ "270deg",
+ "315dev",
+ ],
}, //needs special control
{
label: "Shadow",
key: "box-shadow",
- control: InputGroup,
- meta: [{ placeholder: "X" }, { placeholder: "Y" }, { placeholder: "B" }],
+ control: OptionSelect,
+ defaultValue: "None",
+ options: [
+ { label: "None", value: "none" },
+ { label: "Extra small", value: "0 1px 2px 0 rgba(0, 0, 0, 0.05)" },
+ {
+ label: "Small",
+ value:
+ "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
+ },
+ {
+ label: "Medium",
+ value:
+ "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
+ },
+ {
+ label: "Large",
+ value:
+ "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
+ },
+ {
+ label: "Extra large",
+ value:
+ "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
+ },
+ ],
},
]
diff --git a/packages/builder/src/components/userInterface/temporaryPanelStructure.js b/packages/builder/src/components/userInterface/temporaryPanelStructure.js
index 603e348e18..afd3a06694 100644
--- a/packages/builder/src/components/userInterface/temporaryPanelStructure.js
+++ b/packages/builder/src/components/userInterface/temporaryPanelStructure.js
@@ -187,6 +187,17 @@ export default {
],
},
},
+ {
+ _component: "@budibase/standard-components/image",
+ name: "Image",
+ description: "A basic component for displaying images",
+ icon: "ri-image-fill",
+ children: [],
+ properties: {
+ design: { ...all },
+ settings: [{ label: "URL", key: "url", control: Input }],
+ },
+ },
{
_component: "@budibase/standard-components/icon",
name: "Icon",
diff --git a/packages/builder/src/pages/[application]/_reset.svelte b/packages/builder/src/pages/[application]/_reset.svelte
index 2eec749c8f..4d2a708f1e 100644
--- a/packages/builder/src/pages/[application]/_reset.svelte
+++ b/packages/builder/src/pages/[application]/_reset.svelte
@@ -62,7 +62,7 @@
(location = `/${application}`)}>
+ on:click={() => window.open(`/${application}`)}>
diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js
index 9bd015c86a..0f604be0e7 100644
--- a/packages/server/src/api/controllers/application.js
+++ b/packages/server/src/api/controllers/application.js
@@ -11,7 +11,7 @@ const { exec } = require("child_process")
const sqrl = require("squirrelly")
exports.fetch = async function(ctx) {
- const db = new CouchDB(ClientDb.name(env.CLIENT_ID))
+ const db = new CouchDB(ClientDb.name(getClientId(ctx)))
const body = await db.query("client/by_type", {
include_docs: true,
key: ["app"],
@@ -21,16 +21,30 @@ exports.fetch = async function(ctx) {
}
exports.fetchAppPackage = async function(ctx) {
- const db = new CouchDB(ClientDb.name(env.CLIENT_ID))
+ const clientId = await lookupClientId(ctx.params.applicationId)
+ const db = new CouchDB(ClientDb.name(clientId))
const application = await db.get(ctx.params.applicationId)
ctx.body = await getPackageForBuilder(ctx.config, application)
}
exports.create = async function(ctx) {
- const db = new CouchDB(ClientDb.name(env.CLIENT_ID))
+ const clientId =
+ (ctx.request.body && ctx.request.body.clientId) || env.CLIENT_ID
+
+ if (!clientId) {
+ ctx.throw(400, "ClientId not suplied")
+ }
+ const appId = newid()
+ // insert an appId -> clientId lookup
+ const masterDb = new CouchDB("clientAppLookup")
+ await masterDb.put({
+ _id: appId,
+ clientId,
+ })
+ const db = new CouchDB(ClientDb.name(clientId))
const newApplication = {
- _id: newid(),
+ _id: appId,
type: "app",
instances: [],
userInstanceMap: {},
@@ -47,11 +61,10 @@ exports.create = async function(ctx) {
const createInstCtx = {
params: {
- clientId: env.CLIENT_ID,
applicationId: newApplication._id,
},
request: {
- body: { name: `dev-${env.CLIENT_ID}` },
+ body: { name: `dev-${clientId}` },
},
}
await instanceController.create(createInstCtx)
@@ -61,6 +74,7 @@ exports.create = async function(ctx) {
await runNpmInstall(newAppFolder)
}
+ ctx.status = 200
ctx.body = newApplication
ctx.message = `Application ${ctx.request.body.name} created successfully`
}
@@ -79,7 +93,6 @@ const createEmptyAppPackage = async (ctx, app) => {
if (await exists(newAppFolder)) {
ctx.throw(400, "App folder already exists for this application")
- return
}
await copy(templateFolder, newAppFolder)
@@ -99,6 +112,24 @@ const createEmptyAppPackage = async (ctx, app) => {
return newAppFolder
}
+const lookupClientId = async appId => {
+ const masterDb = new CouchDB("clientAppLookup")
+ const { clientId } = await masterDb.get(appId)
+ return clientId
+}
+
+const getClientId = ctx => {
+ const clientId =
+ (ctx.request.body && ctx.request.body.clientId) ||
+ (ctx.query && ctx.query.clientId) ||
+ env.CLIENT_ID
+
+ if (!clientId) {
+ ctx.throw(400, "ClientId not suplied")
+ }
+ return clientId
+}
+
const updateJsonFile = async (filePath, app) => {
const json = await readFile(filePath, "utf8")
const newJson = sqrl.Render(json, app)
diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js
index de88c29643..5ec3aea617 100644
--- a/packages/server/src/api/controllers/auth.js
+++ b/packages/server/src/api/controllers/auth.js
@@ -2,7 +2,6 @@ const jwt = require("jsonwebtoken")
const CouchDB = require("../../db")
const ClientDb = require("../../db/clientDb")
const bcrypt = require("../../utilities/bcrypt")
-const env = require("../../environment")
exports.authenticate = async ctx => {
const { username, password } = ctx.request.body
@@ -10,8 +9,14 @@ exports.authenticate = async ctx => {
if (!username) ctx.throw(400, "Username Required.")
if (!password) ctx.throw(400, "Password Required")
+ const masterDb = new CouchDB("clientAppLookup")
+ const { clientId } = await masterDb.get(ctx.params.appId)
+
+ if (!clientId) {
+ ctx.throw(400, "ClientId not suplied")
+ }
// find the instance that the user is associated with
- const db = new CouchDB(ClientDb.name(env.CLIENT_ID))
+ const db = new CouchDB(ClientDb.name(clientId))
const appId = ctx.params.appId
const app = await db.get(appId)
const instanceId = app.userInstanceMap[username]
diff --git a/packages/server/src/api/controllers/client.js b/packages/server/src/api/controllers/client.js
index 433bf58c3b..400ccde358 100644
--- a/packages/server/src/api/controllers/client.js
+++ b/packages/server/src/api/controllers/client.js
@@ -6,13 +6,26 @@ exports.getClientId = async function(ctx) {
}
exports.create = async function(ctx) {
- await create(env.CLIENT_ID)
+ const clientId = getClientId(ctx)
+ await create(clientId)
ctx.status = 200
- ctx.message = `Client Database ${env.CLIENT_ID} successfully provisioned.`
+ ctx.message = `Client Database ${clientId} successfully provisioned.`
}
exports.destroy = async function(ctx) {
- await destroy(env.CLIENT_ID)
+ const clientId = getClientId(ctx)
+ await destroy(clientId)
ctx.status = 200
- ctx.message = `Client Database ${env.CLIENT_ID} successfully deleted.`
+ ctx.message = `Client Database ${clientId} successfully deleted.`
+}
+
+const getClientId = ctx => {
+ const clientId =
+ (ctx.query && ctx.query.clientId) ||
+ (ctx.body && ctx.body.clientId) ||
+ env.CLIENT_ID
+ if (!clientId) {
+ ctx.throw(400, "ClientId not suplied")
+ }
+ return clientId
}
diff --git a/packages/server/src/api/controllers/component.js b/packages/server/src/api/controllers/component.js
index 0b07826555..a3b4d9c2bb 100644
--- a/packages/server/src/api/controllers/component.js
+++ b/packages/server/src/api/controllers/component.js
@@ -5,10 +5,11 @@ const {
budibaseTempDir,
budibaseAppsDir,
} = require("../../utilities/budibaseDir")
-const env = require("../../environment")
exports.fetchAppComponentDefinitions = async function(ctx) {
- const db = new CouchDB(ClientDb.name(env.CLIENT_ID))
+ const masterDb = new CouchDB("clientAppLookup")
+ const { clientId } = await masterDb.get(ctx.params.appId)
+ const db = new CouchDB(ClientDb.name(clientId))
const app = await db.get(ctx.params.appId)
const componentDefinitions = app.componentLibraries.reduce(
diff --git a/packages/server/src/api/controllers/instance.js b/packages/server/src/api/controllers/instance.js
index fe40cf9fb4..155437371f 100644
--- a/packages/server/src/api/controllers/instance.js
+++ b/packages/server/src/api/controllers/instance.js
@@ -1,14 +1,16 @@
const CouchDB = require("../../db")
const client = require("../../db/clientDb")
const newid = require("../../db/newid")
-const env = require("../../environment")
exports.create = async function(ctx) {
const instanceName = ctx.request.body.name
const appShortId = ctx.params.applicationId.substring(0, 7)
const instanceId = `inst_${appShortId}_${newid()}`
const { applicationId } = ctx.params
- const clientId = env.CLIENT_ID
+
+ const masterDb = new CouchDB("clientAppLookup")
+ const { clientId } = await masterDb.get(applicationId)
+
const db = new CouchDB(instanceId)
await db.put({
_id: "_design/database",
diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js
index 5810872989..1b66cf1cca 100644
--- a/packages/server/src/api/controllers/user.js
+++ b/packages/server/src/api/controllers/user.js
@@ -1,7 +1,6 @@
const CouchDB = require("../../db")
const clientDb = require("../../db/clientDb")
const bcrypt = require("../../utilities/bcrypt")
-const env = require("../../environment")
const getUserId = userName => `user_${userName}`
const {
POWERUSER_LEVEL_ID,
@@ -42,8 +41,11 @@ exports.create = async function(ctx) {
const response = await database.post(user)
+ const masterDb = new CouchDB("clientAppLookup")
+ const { clientId } = await masterDb.get(appId)
+
// the clientDB needs to store a map of users against the app
- const db = new CouchDB(clientDb.name(env.CLIENT_ID))
+ const db = new CouchDB(clientDb.name(clientId))
const app = await db.get(appId)
app.userInstanceMap = {
diff --git a/packages/server/src/api/routes/tests/application.spec.js b/packages/server/src/api/routes/tests/application.spec.js
index 4c193613fd..7f64419778 100644
--- a/packages/server/src/api/routes/tests/application.spec.js
+++ b/packages/server/src/api/routes/tests/application.spec.js
@@ -5,6 +5,7 @@ const {
destroyClientDatabase,
builderEndpointShouldBlockNormalUsers,
supertest,
+ TEST_CLIENT_ID,
defaultHeaders,
} = require("./couchTestUtils")
@@ -51,6 +52,7 @@ describe("/applications", () => {
body: { name: "My App" }
})
})
+
})
describe("fetch", () => {
@@ -68,6 +70,37 @@ describe("/applications", () => {
expect(res.body.length).toBe(2)
})
+ it("lists only applications in requested client databse", async () => {
+ await createApplication(request, "app1")
+ await createClientDatabase("new_client")
+
+ const blah = await request
+ .post("/api/applications")
+ .send({ name: "app2", clientId: "new_client"})
+ .set(defaultHeaders)
+ .expect('Content-Type', /json/)
+ //.expect(200)
+
+ const client1Res = await request
+ .get(`/api/applications?clientId=${TEST_CLIENT_ID}`)
+ .set(defaultHeaders)
+ .expect('Content-Type', /json/)
+ .expect(200)
+
+ expect(client1Res.body.length).toBe(1)
+ expect(client1Res.body[0].name).toBe("app1")
+
+ const client2Res = await request
+ .get(`/api/applications?clientId=new_client`)
+ .set(defaultHeaders)
+ .expect('Content-Type', /json/)
+ .expect(200)
+
+ expect(client2Res.body.length).toBe(1)
+ expect(client2Res.body[0].name).toBe("app2")
+
+ })
+
it("should apply authorization to endpoint", async () => {
const otherApplication = await createApplication(request)
const instance = await createInstance(request, otherApplication._id)
diff --git a/packages/server/src/api/routes/tests/couchTestUtils.js b/packages/server/src/api/routes/tests/couchTestUtils.js
index f70fc0ced1..6029e080cc 100644
--- a/packages/server/src/api/routes/tests/couchTestUtils.js
+++ b/packages/server/src/api/routes/tests/couchTestUtils.js
@@ -9,6 +9,7 @@ const {
const TEST_CLIENT_ID = "test-client-id"
+exports.TEST_CLIENT_ID = TEST_CLIENT_ID
exports.supertest = async () => {
let request
let port = 4002
@@ -59,7 +60,7 @@ exports.createView = async (request, instanceId, view) => {
return res.body
}
-exports.createClientDatabase = async () => await create(TEST_CLIENT_ID)
+exports.createClientDatabase = async id => await create(id || TEST_CLIENT_ID)
exports.createApplication = async (request, name = "test_application") => {
const res = await request
diff --git a/packages/standard-components/src/DataForm.svelte b/packages/standard-components/src/DataForm.svelte
index ab515551dd..6a9788458e 100644
--- a/packages/standard-components/src/DataForm.svelte
+++ b/packages/standard-components/src/DataForm.svelte
@@ -87,7 +87,6 @@
.form-content {
margin-bottom: 20px;
-
}
.input {
diff --git a/packages/standard-components/src/DataTable.svelte b/packages/standard-components/src/DataTable.svelte
index c0db923d58..90716383da 100644
--- a/packages/standard-components/src/DataTable.svelte
+++ b/packages/standard-components/src/DataTable.svelte
@@ -65,7 +65,7 @@
}
thead {
- background: #393C44;
+ background: #393c44;
border: 1px solid #ccc;
height: 40px;
text-align: left;
@@ -87,7 +87,7 @@
tbody tr {
border-bottom: 1px solid #ccc;
transition: 0.3s background-color;
- color: #393C44;
+ color: #393c44;
font-size: 14px;
height: 40px;
}
diff --git a/packages/standard-components/src/Image.svelte b/packages/standard-components/src/Image.svelte
index 84f515b287..cd204f5fc2 100644
--- a/packages/standard-components/src/Image.svelte
+++ b/packages/standard-components/src/Image.svelte
@@ -7,6 +7,8 @@
export let height
export let width
+ export let _bb
+
$: style = buildStyle({ height, width })