diff --git a/.gitignore b/.gitignore index edad41cdec..b6cfa424ad 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,6 @@ typings/ # Mac files .DS_Store + +# Nova Editor +.nova \ No newline at end of file diff --git a/README.md b/README.md index 50ef3dc855..f1eeda2588 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,10 @@

- Build business apps 50x faster + Build internal tools 50x faster

- Budibase is an open-source low-code platform that helps developers and IT professionals design, build, and ship business apps 50x faster. + Budibase is an open-source low-code platform that helps developers and IT professionals build, automate and ship internal tools 50x faster.

@@ -67,18 +67,26 @@ When other platforms chose the closed source route, we decided to go open source - **Automate processes, integrate with other tools, and connect to webhooks.** Save time by automating manual processes and workflows. From connecting to webhooks, to automating emails, simply tell Budibase what to do and let it work for you. You can easily [create new automations for Budibase here](https://github.com/Budibase/automations) or [request new integrations here](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). -- **Cloud hosting (available) and self-hosting (coming soon).** Users will soon have the option to host with Budibase in AWS (available now) or self-host (coming very soon). From the very beginning, we wanted our users to have the option to self-host. We understand the importance of having full control over data. This is why we are working incredibly hard to offer an easy path to self-hosting. If you are interested in self-hosting, please [join the conversation and add your thoughts](https://github.com/Budibase/budibase/discussions/648). +- **Cloud hosting and self-hosting.** Users can self-host (see below), or host their apps with Budibase. Currently, our cloud hosting offering is limited to the free tier but we aim to change this in the future. For heavy usage, we advise users to self-host. + + +## 🤖 Self-hosting + +

+ +

+ +Budibase wants to make sure anyone can use the tools we develop and we know a lot of people need to be able to host the apps they make on their own systems - that is why we've decided to try and make self hosting as easy as possible! + +Currently, you can host your apps using Docker. The documentation for self-hosting can be found [here](https://docs.budibase.com/self-hosting/introduction-to-self-hosting). ## ⌛ Status - [x] Alpha: We are demoing Budibase to users and receiving feedback - [x] Private Beta: We are testing Budibase with a closed set of customers -- [x] Public Beta: Anyone can [sign-up and use Budibase](https://portal.budi.live/signup) but it's not production ready. We cannot ensure backwards compatibility +- [x] Public Beta: Anyone can [sign-up and use Budibase](https://portal.budi.live/signup). - [ ] Official Launch: Production-ready - -We are currently in Public Beta. Until our official launch, we cannot ensure backwards compatibility for your Budibase applications between versions. Issues may arise when trying to edit apps created with old versions of the Budibase builder. - Watch "releases" of this repo to get notified of major updates, and give the star button a click whilst you're there.

diff --git a/hosting/docker-compose.yaml b/hosting/docker-compose.yaml index c4a6c669b3..5b7c266c1e 100644 --- a/hosting/docker-compose.yaml +++ b/hosting/docker-compose.yaml @@ -6,8 +6,11 @@ services: ports: - "${APP_PORT}:4002" environment: - SELF_HOSTED: 1 + SELF_HOSTED: 1 + CLOUD: 1 COUCH_DB_URL: http://${COUCH_DB_USER}:${COUCH_DB_PASSWORD}@couchdb-service:5984 + WORKER_URL: http://worker-service:4003 + HOSTING_KEY: ${HOSTING_KEY} BUDIBASE_ENVIRONMENT: ${BUDIBASE_ENVIRONMENT} PORT: 4002 JWT_SECRET: ${JWT_SECRET} @@ -20,14 +23,13 @@ services: - "${WORKER_PORT}:4003" environment: SELF_HOSTED: 1, - DEPLOYMENT_API_KEY: ${WORKER_API_KEY} PORT: 4003 MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY} MINIO_SECRET_KEY: ${MINIO_SECRET_KEY} RAW_MINIO_URL: http://minio-service:9000 COUCH_DB_USERNAME: ${COUCH_DB_USER} COUCH_DB_PASSWORD: ${COUCH_DB_PASSWORD} - RAW_COUCH_DB_URL: http://couchdb-service:5984 + COUCH_DB_URL: http://${COUCH_DB_USER}:${COUCH_DB_PASSWORD}@couchdb-service:5984 SELF_HOST_KEY: ${HOSTING_KEY} depends_on: - minio-service diff --git a/hosting/hosting.properties b/hosting/hosting.properties index 2ef83543a4..ad047a3826 100644 --- a/hosting/hosting.properties +++ b/hosting/hosting.properties @@ -12,7 +12,6 @@ MINIO_ACCESS_KEY=budibase MINIO_SECRET_KEY=budibase COUCH_DB_PASSWORD=budibase COUCH_DB_USER=budibase -WORKER_API_KEY=budibase # This section contains variables that do not need to be altered under normal circumstances APP_PORT=4002 diff --git a/package-lock.json b/package-lock.json index 9a1f0055cb..40073784fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -615,6 +615,17 @@ "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "make-fetch-happen": { @@ -1004,6 +1015,17 @@ "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "make-fetch-happen": { @@ -1087,6 +1109,17 @@ "npmlog": "^4.1.2", "path-exists": "^3.0.0", "rimraf": "^2.6.2" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "@lerna/run": { @@ -1900,6 +1933,17 @@ "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "cache-base": { @@ -2494,6 +2538,17 @@ "mkdirp": "^0.5.1", "rimraf": "^2.5.4", "run-queue": "^1.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "copy-descriptor": { @@ -3394,6 +3449,17 @@ "flatted": "^2.0.0", "rimraf": "2.6.3", "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "flatted": { @@ -3767,6 +3833,12 @@ "pump": "^3.0.0" } }, + "get-them-args": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/get-them-args/-/get-them-args-1.3.2.tgz", + "integrity": "sha1-dKILqKSr7OWuGZrQPyvMaP38m6U=", + "dev": true + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -4354,9 +4426,9 @@ "dev": true }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, "init-package-json": { @@ -4829,6 +4901,16 @@ "verror": "1.10.0" } }, + "kill-port": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/kill-port/-/kill-port-1.6.1.tgz", + "integrity": "sha512-un0Y55cOM7JKGaLnGja28T38tDDop0AQ8N0KlAdyh+B1nmMoX8AnNmqPNZbS3mUMgiST51DCVqmbFT1gNJpVNw==", + "dev": true, + "requires": { + "get-them-args": "1.3.2", + "shell-exec": "1.0.2" + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -5370,6 +5452,17 @@ "mkdirp": "^0.5.1", "rimraf": "^2.5.4", "run-queue": "^1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "ms": { @@ -5469,6 +5562,15 @@ "which": "1" }, "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", @@ -5966,6 +6068,15 @@ "which": "^1.3.1" }, "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -6192,13 +6303,10 @@ } }, "prettier-plugin-svelte": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-0.7.0.tgz", - "integrity": "sha512-SuZSeMh48rx42kCFEpI/xE1XgjxQcS3r22Yo7jIhBYRhwbAa8laNxiIHsfeWWkX8BdyELkEayaTQp4ricckwTQ==", - "dev": true, - "requires": { - "tslib": "^1.9.3" - } + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-1.4.2.tgz", + "integrity": "sha512-O9VsNwII+raTG8QPoQWouk5ABQy/hmLm4dZ2eqJ7DPnbO35A+BxMSjlfqkw0cNP+UcbykHFYU8zNXm93ytWP9g==", + "dev": true }, "process-nextick-args": { "version": "2.0.1", @@ -6603,9 +6711,9 @@ "dev": true }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" @@ -6733,6 +6841,12 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "shell-exec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/shell-exec/-/shell-exec-1.0.2.tgz", + "integrity": "sha512-jyVd+kU2X+mWKMmGhx4fpWbPsjvD53k9ivqetutVW/BQ+WIZoDoP4d8vUMGezV6saZsiNoW2f9GIhg9Dondohg==", + "dev": true + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -7213,9 +7327,9 @@ } }, "svelte": { - "version": "3.29.0", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.29.0.tgz", - "integrity": "sha512-f+A65eyOQ5ujETLy+igNXtlr6AEjAQLYd1yJE1VwNiXMQO5Z/Vmiy3rL+zblV/9jd7rtTTWqO1IcuXsP2Qv0OA==", + "version": "3.31.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.31.2.tgz", + "integrity": "sha512-TxZGrXzX2ggFH3BIKY5fmbeMdJuZrMIMDYPMX6R9255bueuYIuVaBQSLUeY2oD7W4IdeqRZiAVGCjDw2POKBRA==", "dev": true }, "table": { diff --git a/package.json b/package.json index fd601dd099..37efca51ba 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,5 @@ "test:e2e": "lerna run cy:test", "test:e2e:ci": "lerna run cy:ci", "build:docker": "cd hosting/scripts/linux/ && ./release-to-docker-hub.sh && cd -" - }, - "dependencies": { - "@fortawesome/fontawesome": "^1.1.8" } } diff --git a/packages/builder/package.json b/packages/builder/package.json index 32601d97dd..f1c91e9e7c 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -63,14 +63,14 @@ } }, "dependencies": { - "@budibase/bbui": "^1.52.4", + "@budibase/bbui": "^1.54.0", "@budibase/client": "^0.5.3", "@budibase/colorpicker": "^1.0.1", "@budibase/svelte-ag-grid": "^0.0.16", - "@fortawesome/fontawesome-free": "^5.14.0", "@sentry/browser": "5.19.1", "@svelteschool/svelte-forms": "^0.7.0", "britecharts": "^2.16.0", + "codemirror": "^5.59.0", "d3-selection": "^1.4.1", "deepmerge": "^4.2.2", "fast-sort": "^2.2.0", diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index 7d3f390220..6b114891b0 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -10,9 +10,10 @@ const CAPTURE_VAR_INSIDE_MUSTACHE = /{{([^}]+)}}/g * Gets all bindable data context fields and instance fields. */ export const getBindableProperties = (rootComponent, componentId) => { - const bindableContexts = getBindableContexts(rootComponent, componentId) - const bindableComponents = getBindableComponents(rootComponent) - return [...bindableContexts, ...bindableComponents] + const contextBindings = getContextBindings(rootComponent, componentId) + const queryBindings = getQueryBindings(rootComponent, componentId) + const componentBindings = getComponentBindings(rootComponent) + return [...contextBindings, ...queryBindings, componentBindings] } /** @@ -71,11 +72,10 @@ export const getDatasourceForProvider = component => { * Gets all bindable data contexts. These are fields of schemas of data contexts * provided by data provider components, such as lists or row detail components. */ -export const getBindableContexts = (rootComponent, componentId) => { - const dataProviders = getDataProviderComponents(rootComponent, componentId) - +export const getContextBindings = (rootComponent, componentId) => { // Extract any components which provide data contexts - let contexts = [] + const dataProviders = getDataProviderComponents(rootComponent, componentId) + let contextBindings = [] dataProviders.forEach(component => { const datasource = getDatasourceForProvider(component) if (!datasource) { @@ -99,7 +99,7 @@ export const getBindableContexts = (rootComponent, componentId) => { runtimeBoundKey = `${key}_first` } - contexts.push({ + contextBindings.push({ type: "context", runtimeBinding: `${component._id}.${runtimeBoundKey}`, readableBinding: `${component._instanceName}.${table.name}.${key}`, @@ -110,14 +110,14 @@ export const getBindableContexts = (rootComponent, componentId) => { }) }) }) - return contexts + return contextBindings } /** * Gets all bindable components. These are form components which allow their * values to be bound to. */ -export const getBindableComponents = rootComponent => { +export const getComponentBindings = rootComponent => { if (!rootComponent) { return [] } @@ -137,6 +137,47 @@ export const getBindableComponents = rootComponent => { }) } +/** + * Gets all bindable query fields. These are fields of schemas of data contexts + * provided by data provider components, such as lists or row detail components. + */ +export const getQueryBindings = (rootComponent, componentId) => { + // Extract any components which provide data contexts + const dataProviders = getDataProviderComponents(rootComponent, componentId) + const queries = get(backendUiStore).queries + let queryBindings = [] + dataProviders.forEach(component => { + const datasource = getDatasourceForProvider(component) + if (!datasource) { + return + } + + // Find a query for this table ID + const queryId = datasource.tableId + const query = queries.find(query => query._id === queryId) + const schema = query?.schema + if (!schema) { + return + } + + // Add all schema fields as bindable values + const keys = Object.keys(schema).sort() + keys.forEach(key => { + const fieldSchema = schema[key] + queryBindings.push({ + type: "context", + fieldSchema, + runtimeBinding: `${component._id}.${key}`, + readableBinding: `${component._instanceName}.${query.name}.${key}`, + providerId: component._id, + tableId: datasource.tableId, + field: key, + }) + }) + }) + return queryBindings +} + /** * Gets a schema for a datasource object. */ diff --git a/packages/builder/src/builderStore/store/backend.js b/packages/builder/src/builderStore/store/backend.js index 5592488590..0a235dd9a0 100644 --- a/packages/builder/src/builderStore/store/backend.js +++ b/packages/builder/src/builderStore/store/backend.js @@ -7,6 +7,9 @@ const INITIAL_BACKEND_UI_STATE = { views: [], users: [], roles: [], + datasources: [], + queries: [], + integrations: {}, selectedDatabase: {}, selectedTable: {}, draftTable: {}, @@ -21,9 +24,19 @@ export const getBackendUiStore = () => { select: async db => { const tablesResponse = await api.get(`/api/tables`) const tables = await tablesResponse.json() + const datasourcesResponse = await api.get(`/api/datasources`) + const datasources = await datasourcesResponse.json() + const queriesResponse = await api.get(`/api/queries`) + const queries = await queriesResponse.json() + const integrationsResponse = await api.get("/api/integrations") + const integrations = await integrationsResponse.json() + store.update(state => { state.selectedDatabase = db state.tables = tables + state.datasources = datasources + state.queries = queries + state.integrations = integrations return state }) }, @@ -45,6 +58,107 @@ export const getBackendUiStore = () => { return state }), }, + datasources: { + fetch: async () => { + const response = await api.get(`/api/datasources`) + const json = await response.json() + store.update(state => { + state.datasources = json + return state + }) + return json + }, + select: async datasourceId => { + store.update(state => { + state.selectedDatasourceId = datasourceId + state.selectedQueryId = null + return state + }) + }, + save: async datasource => { + const response = await api.post("/api/datasources", datasource) + const json = await response.json() + store.update(state => { + const currentIdx = state.datasources.findIndex( + ds => ds._id === json._id + ) + + if (currentIdx >= 0) { + state.datasources.splice(currentIdx, 1, json) + } else { + state.datasources.push(json) + } + + state.datasources = state.datasources + state.selectedDatasourceId = json._id + return state + }) + return json + }, + delete: async datasource => { + await api.delete( + `/api/datasources/${datasource._id}/${datasource._rev}` + ) + store.update(state => { + state.datasources = state.datasources.filter( + existing => existing._id !== datasource._id + ) + state.selectedDatasourceId = null + return state + }) + }, + }, + queries: { + fetch: async () => { + const response = await api.get(`/api/queries`) + const json = await response.json() + store.update(state => { + state.queries = json + return state + }) + return json + }, + save: async (datasourceId, query) => { + query.datasourceId = datasourceId + const response = await api.post(`/api/queries`, query) + const json = await response.json() + store.update(state => { + const currentIdx = state.queries.findIndex( + query => query._id === json._id + ) + + if (currentIdx >= 0) { + state.queries.splice(currentIdx, 1, json) + } else { + state.queries.push(json) + } + + state.queries = state.queries + state.selectedQueryId = json._id + return state + }) + return json + }, + select: query => + store.update(state => { + state.selectedDatasourceId = query.datasourceId + state.selectedQueryId = query._id + return state + }), + delete: async query => { + await api.delete(`/api/queries/${query._id}/${query._rev}`) + store.update(state => { + state.queries = state.queries.filter( + existing => existing._id !== query._id + ) + if (state.selectedQueryId === query._id) { + state.selectedQueryId = null + } + + return state + }) + }, + }, tables: { fetch: async () => { const tablesResponse = await api.get(`/api/tables`) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 80b572c8e2..340e2829aa 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -19,6 +19,7 @@ import { uuid } from "../uuid" const INITIAL_FRONTEND_STATE = { apps: [], name: "", + url: "", description: "", layouts: [], screens: [], @@ -48,6 +49,7 @@ export const getFrontendStore = () => { name: application.name, description: application.description, appId: application._id, + url: application.url, layouts, screens, hasAppPackage: true, diff --git a/packages/builder/src/builderStore/store/hosting.js b/packages/builder/src/builderStore/store/hosting.js index 36067773b5..f180d4157a 100644 --- a/packages/builder/src/builderStore/store/hosting.js +++ b/packages/builder/src/builderStore/store/hosting.js @@ -1,13 +1,16 @@ import { writable } from "svelte/store" -import api from "../api" +import api, { get } from "../api" -const INITIAL_BACKEND_UI_STATE = { +const INITIAL_HOSTING_UI_STATE = { hostingInfo: {}, appUrl: "", + deployedApps: {}, + deployedAppNames: [], + deployedAppUrls: [], } export const getHostingStore = () => { - const store = writable({ ...INITIAL_BACKEND_UI_STATE }) + const store = writable({ ...INITIAL_HOSTING_UI_STATE }) store.actions = { fetch: async () => { const responses = await Promise.all([ @@ -33,6 +36,16 @@ export const getHostingStore = () => { return state }) }, + fetchDeployedApps: async () => { + let deployments = await (await get("/api/hosting/apps")).json() + store.update(state => { + state.deployedApps = deployments + state.deployedAppNames = Object.values(deployments).map(app => app.name) + state.deployedAppUrls = Object.values(deployments).map(app => app.url) + return state + }) + return deployments + }, } return store } diff --git a/packages/builder/src/components/automation/AutomationBuilder/BlockList.svelte b/packages/builder/src/components/automation/AutomationBuilder/BlockList.svelte index 5ea1d64e51..e00fb92e27 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/BlockList.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/BlockList.svelte @@ -50,7 +50,10 @@ function addBlockToAutomation(stepId, blockDefinition) { const newBlock = $automationStore.selectedAutomation.constructBlock( - selectedTab, stepId, blockDefinition) + selectedTab, + stepId, + blockDefinition + ) automationStore.actions.addBlockToAutomation(newBlock) closePopover() if (stepId === "WEBHOOK") { diff --git a/packages/builder/src/components/automation/SetupPanel/BindableInput.svelte b/packages/builder/src/components/automation/SetupPanel/BindableInput.svelte index 76624abd06..b1521bee20 100644 --- a/packages/builder/src/components/automation/SetupPanel/BindableInput.svelte +++ b/packages/builder/src/components/automation/SetupPanel/BindableInput.svelte @@ -1,9 +1,13 @@

diff --git a/packages/builder/src/components/automation/SetupPanel/GenericBindingPopover.svelte b/packages/builder/src/components/automation/SetupPanel/GenericBindingPopover.svelte index 0b53ab91ad..a326f60a08 100644 --- a/packages/builder/src/components/automation/SetupPanel/GenericBindingPopover.svelte +++ b/packages/builder/src/components/automation/SetupPanel/GenericBindingPopover.svelte @@ -50,7 +50,9 @@ {binding.label} {binding.type}
-
{binding.description}
+
+ {binding.description || ''} +
{/each} {/each} diff --git a/packages/builder/src/components/automation/SetupPanel/SchemaSetup.svelte b/packages/builder/src/components/automation/SetupPanel/SchemaSetup.svelte index 51c105e9f4..8855fb6895 100644 --- a/packages/builder/src/components/automation/SetupPanel/SchemaSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/SchemaSetup.svelte @@ -36,9 +36,7 @@
-
- -
+
{#each fieldsArray as field}
@@ -57,13 +55,11 @@ - removeField(field.name)} /> - -
{/each} -
diff --git a/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte b/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte new file mode 100644 index 0000000000..9be0481c58 --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte @@ -0,0 +1,28 @@ + + +{#if error} +
{error}
+{/if} + + + diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte index 1e6c53bd11..070ebec080 100644 --- a/packages/builder/src/components/backend/DataTable/Table.svelte +++ b/packages/builder/src/components/backend/DataTable/Table.svelte @@ -199,7 +199,6 @@ align-items: stretch; } .grid-wrapper :global(> *) { - height: auto; flex: 1 1 auto; } :global(.grid-wrapper) { @@ -236,6 +235,7 @@ } :global(.ag-filter) { + background: var(--background); padding: var(--spacing-s); outline: none; box-sizing: border-box; diff --git a/packages/builder/src/components/backend/DataTable/api.js b/packages/builder/src/components/backend/DataTable/api.js index 629405a9fc..91ebc19b26 100644 --- a/packages/builder/src/components/backend/DataTable/api.js +++ b/packages/builder/src/components/backend/DataTable/api.js @@ -23,5 +23,10 @@ export async function fetchDataForView(view) { const FETCH_ROWS_URL = `/api/views/${view.name}` const response = await api.get(FETCH_ROWS_URL) - return await response.json() + const json = await response.json() + + if (response.status !== 200) { + throw new Error(json.message) + } + return json } diff --git a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte new file mode 100644 index 0000000000..aa8954cb8d --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte @@ -0,0 +1,63 @@ + + +{#if $backendUiStore.selectedDatabase && $backendUiStore.selectedDatabase._id} +
+ {#each $backendUiStore.datasources as datasource, idx} + 0} + text={datasource.name} + selected={$backendUiStore.selectedDatasourceId === datasource._id} + on:click={() => selectDatasource(datasource)}> +
+ +
+ +
+ {#each $backendUiStore.queries.filter(query => query.datasourceId === datasource._id) as query} + onClickQuery(query)}> + + + {/each} + {/each} +
+{/if} diff --git a/packages/builder/src/components/backend/DatasourceNavigator/ListItem.svelte b/packages/builder/src/components/backend/DatasourceNavigator/ListItem.svelte new file mode 100644 index 0000000000..f148eaf8b2 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/ListItem.svelte @@ -0,0 +1,55 @@ + + +
+ + {title} + +
+ + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte new file mode 100644 index 0000000000..79bb951c01 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte @@ -0,0 +1,16 @@ + + +
+ {#each Object.keys(integration) as configKey} + + + {/each} + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte new file mode 100644 index 0000000000..dbe289caba --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte @@ -0,0 +1,97 @@ + + +
+
+ {#each Object.keys(integrations) as integrationType} +
selectIntegration(integrationType)}> + + {integrationType} +
+ {/each} +
+ + {#if schema} + {#each Object.keys(schema) as configKey} + + + {/each} + {/if} +
+ + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/Airtable.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/Airtable.svelte new file mode 100644 index 0000000000..ce689df205 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/Airtable.svelte @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/CouchDB.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/CouchDB.svelte new file mode 100644 index 0000000000..1ed737fc81 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/CouchDB.svelte @@ -0,0 +1,35 @@ + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/DynamoDB.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/DynamoDB.svelte new file mode 100644 index 0000000000..1ec061ca20 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/DynamoDB.svelte @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/Elasticsearch.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/Elasticsearch.svelte new file mode 100644 index 0000000000..20df3bd620 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/Elasticsearch.svelte @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/MongoDB.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/MongoDB.svelte new file mode 100644 index 0000000000..823ec3a2fa --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/MongoDB.svelte @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/Postgres.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/Postgres.svelte new file mode 100644 index 0000000000..56268f75b3 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/Postgres.svelte @@ -0,0 +1,149 @@ + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/S3.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/S3.svelte new file mode 100644 index 0000000000..23168a3501 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/S3.svelte @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/SQLServer.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/SQLServer.svelte new file mode 100644 index 0000000000..6aa2fcafca --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/SQLServer.svelte @@ -0,0 +1,391 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js new file mode 100644 index 0000000000..afedb9e78f --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js @@ -0,0 +1,19 @@ +import Postgres from "./Postgres.svelte" +import DynamoDB from "./DynamoDB.svelte" +import Elasticsearch from "./Elasticsearch.svelte" +import MongoDB from "./MongoDB.svelte" +import CouchDB from "./CouchDB.svelte" +import S3 from "./S3.svelte" +import Airtable from "./Airtable.svelte" +import SqlServer from "./SQLServer.svelte" + +export default { + POSTGRES: Postgres, + DYNAMODB: DynamoDB, + MONGODB: MongoDB, + ELASTICSEARCH: Elasticsearch, + COUCHDB: CouchDB, + SQL_SERVER: SqlServer, + S3: S3, + AIRTABLE: Airtable, +} diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte new file mode 100644 index 0000000000..a41cbd2466 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte @@ -0,0 +1,61 @@ + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateQueryModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateQueryModal.svelte new file mode 100644 index 0000000000..6a98313335 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateQueryModal.svelte @@ -0,0 +1,61 @@ + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditDatasourcePopover.svelte b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditDatasourcePopover.svelte new file mode 100644 index 0000000000..1bbaddf5ef --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditDatasourcePopover.svelte @@ -0,0 +1,87 @@ + + +
+
+ +
+ + + + + +
+ + Are you sure you wish to delete the datasource + {datasource.name}? + This action cannot be undone. + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte new file mode 100644 index 0000000000..92a541ac1d --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte @@ -0,0 +1,84 @@ + + +
+
+ +
+ + + + + +
+ + Are you sure you wish to delete this query? This action cannot be undone. + + + diff --git a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte index 810d7960f5..293f52b487 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte @@ -5,7 +5,7 @@ import api from "builderStore/api" const BYTES_IN_MB = 1000000 - const FILE_SIZE_LIMIT = BYTES_IN_MB * 1 + const FILE_SIZE_LIMIT = BYTES_IN_MB * 5 export let files = [] export let dataImport = { diff --git a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte index 30a0568e8e..3719d62c62 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte @@ -2,13 +2,11 @@ import { goto } from "@sveltech/routify" import { backendUiStore } from "builderStore" import { TableNames } from "constants" - import CreateTableModal from "./modals/CreateTableModal.svelte" import EditTablePopover from "./popovers/EditTablePopover.svelte" import EditViewPopover from "./popovers/EditViewPopover.svelte" - import { Modal } from "@budibase/bbui" + import { Switcher } from "@budibase/bbui" import NavItem from "components/common/NavItem.svelte" - let modal $: selectedView = $backendUiStore.selectedView && $backendUiStore.selectedView.name @@ -34,10 +32,6 @@ {#if $backendUiStore.selectedDatabase && $backendUiStore.selectedDatabase._id} -
-

Tables

- -
{#each $backendUiStore.tables as table, idx} {/if} - - - - - diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index 3338072ec2..faadbdeb49 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -2,7 +2,14 @@ import { goto } from "@sveltech/routify" import { backendUiStore, store } from "builderStore" import { notifier } from "builderStore/store/notifications" - import { Input, Label, ModalContent, Toggle } from "@budibase/bbui" + import { + Input, + Label, + ModalContent, + Button, + Spacer, + Toggle, + } from "@budibase/bbui" import TableDataImport from "../TableDataImport.svelte" import analytics from "analytics" import screenTemplates from "builderStore/store/screenTemplates" diff --git a/packages/builder/src/components/common/NavItem.svelte b/packages/builder/src/components/common/NavItem.svelte index 8eca07b1b0..972427328b 100644 --- a/packages/builder/src/components/common/NavItem.svelte +++ b/packages/builder/src/components/common/NavItem.svelte @@ -29,6 +29,8 @@
{/if} + + {#if icon}
{/if} diff --git a/packages/builder/src/components/deploy/DeploymentHistory.svelte b/packages/builder/src/components/deploy/DeploymentHistory.svelte index fe95b1369f..8061e08ed1 100644 --- a/packages/builder/src/components/deploy/DeploymentHistory.svelte +++ b/packages/builder/src/components/deploy/DeploymentHistory.svelte @@ -6,7 +6,7 @@ import api from "builderStore/api" import { notifier } from "builderStore/store/notifications" import CreateWebhookDeploymentModal from "./CreateWebhookDeploymentModal.svelte" - import { hostingStore } from "builderStore" + import { store, hostingStore } from "builderStore" const DeploymentStatus = { SUCCESS: "SUCCESS", @@ -36,7 +36,9 @@ let errorReason let poll let deployments = [] - let deploymentUrl = `${$hostingStore.appUrl}/${appId}` + let urlComponent = + $hostingStore.hostingInfo.type === "self" ? $store.url : `/${appId}` + let deploymentUrl = `${$hostingStore.appUrl}${urlComponent}` const formatDate = (date, format) => Intl.DateTimeFormat("en-GB", DATE_OPTIONS[format]).format(date) diff --git a/packages/builder/src/components/design/AppPreview/iframeTemplate.html b/packages/builder/src/components/design/AppPreview/iframeTemplate.html index cec0b789da..49df3b5c0b 100644 --- a/packages/builder/src/components/design/AppPreview/iframeTemplate.html +++ b/packages/builder/src/components/design/AppPreview/iframeTemplate.html @@ -1,6 +1,7 @@ + diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/BindingDropdown.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/BindingDropdown.svelte deleted file mode 100644 index 018691ee51..0000000000 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/BindingDropdown.svelte +++ /dev/null @@ -1,142 +0,0 @@ - - -
-
- {#if hasBindableProperties} - {#if context} - Datasources - -
    - {#each context as { readableBinding }} -
  • addToText(readableBinding)}> - {readableBinding} -
  • - {/each} -
- {/if} - {#if instance} - - Components - -
    - {#each instance as { readableBinding }} -
  • addToText(readableBinding)}> - {readableBinding} -
  • - {/each} -
- {/if} - {:else} -
There aren't any bindable properties available.
- {/if} -
-
- Data Binding - - - Binding connects one piece of data to another and makes it dynamic. Click - the objects on the left to add them to the extbox. - - -