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..bc2e10e69e 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,10 @@

- Build business apps 50x faster + Build internal tools 50x faster on your own infrastructure

- 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, helping developers and IT professionals build, automate, and ship internal tools 50x faster on their own infrastructure.

@@ -61,23 +61,31 @@ When other platforms chose the closed source route, we decided to go open source - **Open source and extensable.** Budibase is open-source. The builder is licensed AGPL v3, the server is GPL v3, and the client is MPL. This should fill you with confidence that Budibase will always be around. You can also code against Budibase or fork it and make changes as you please, providing a developer-friendly experience. -- **Load data or start from scratch.** Budibase pulls in data from multiple sources, whether it’s a CSV, an external database (coming very soon), or a REST API. And unlike other platforms, with Budibase you can start from scratch and create business apps with no data sources. [Request new data sources](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). +- **Load data or start from scratch.** Budibase pulls in data from multiple sources, including MongoDB, CouchDB, PostgreSQL, mySQL, Airtable, Google Sheets, S3, DyanmoDB, or a REST API. And unlike other platforms, with Budibase you can start from scratch and create business apps with no data sources. [Request new data sources](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). - **Design and build apps with powerful pre-made components.** Budibase comes out of the box with beautifully designed, powerful components which you can use like building blocks to build your UI. We also expose a lot of your favourite CSS styling options so you can go that extra creative mile. [Request new components](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). - **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 -- [ ] 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. +- [x] Public Beta: Anyone can [sign-up and use Budibase](https://portal.budi.live/signup). +- [ ] Official Launch 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 398118eb5c..a98da6e8d1 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -63,15 +63,15 @@ } }, "dependencies": { - "@budibase/bbui": "^1.52.4", + "@budibase/bbui": "^1.54.0", "@budibase/client": "^0.5.3", "@budibase/colorpicker": "^1.0.1", "@budibase/string-templates": "^0.5.3", "@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/fetchBindableProperties.js b/packages/builder/src/builderStore/fetchBindableProperties.js index 962fb80ebf..9302f3c0f0 100644 --- a/packages/builder/src/builderStore/fetchBindableProperties.js +++ b/packages/builder/src/builderStore/fetchBindableProperties.js @@ -23,13 +23,20 @@ import { cloneDeep, difference } from "lodash/fp" * @param {fetchBindablePropertiesParameter} param * @returns {Array.} */ -export default function({ componentInstanceId, screen, components, tables }) { +export default function({ + componentInstanceId, + screen, + components, + tables, + queries, +}) { const result = walk({ // cloning so we are free to mutate props (e.g. by adding _contexts) instance: cloneDeep(screen.props), targetId: componentInstanceId, components, tables, + queries, }) return [ @@ -37,6 +44,9 @@ export default function({ componentInstanceId, screen, components, tables }) { .filter(isInstanceInSharedContext(result)) .map(componentInstanceToBindable), ...(result.target?._contexts.map(contextToBindables(tables)).flat() ?? []), + ...(result.target?._contexts + .map(context => queriesToBindables(queries, context)) + .flat() ?? []), ] } @@ -61,9 +71,36 @@ const componentInstanceToBindable = i => { } } +const queriesToBindables = (queries, context) => { + let queryId = context.table._id + + const query = queries.find(query => query._id === queryId) + let schema = query?.schema + + // Avoid crashing whenever no data source has been selected + if (!schema) { + return [] + } + + const queryBindings = Object.entries(schema).map(([key, value]) => ({ + type: "context", + fieldSchema: value, + instance: context.instance, + // how the binding expression persists, and is used in the app at runtime + runtimeBinding: `${context.instance._id}.${key}`, + // how the binding expressions looks to the user of the builder + readableBinding: `${context.instance._instanceName}.${query.name}.${key}`, + // table / view info + table: context.table, + })) + + return queryBindings +} + const contextToBindables = tables => context => { - const tableId = context.table?.tableId ?? context.table - const table = tables.find(table => table._id === tableId) + let tableId = context.table?.tableId ?? context.table + + const table = tables.find(table => table._id === tableId || context.table._id) let schema = context.table?.type === "view" ? table?.views?.[context.table.name]?.schema @@ -152,8 +189,8 @@ const walk = ({ instance, targetId, components, tables, result }) => { const currentContexts = [...result.currentContexts] for (let child of instance._children || []) { - // attaching _contexts of components, for eas comparison later - // these have been deep cloned above, so shouln't modify the + // attaching _contexts of components, for easy comparison later + // these have been deep cloned above, so shouldn't modify the // original component instances child._contexts = currentContexts walk({ instance: child, targetId, components, tables, result }) 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 501725f029..2082a3e49b 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -28,6 +28,7 @@ import { const INITIAL_FRONTEND_STATE = { apps: [], name: "", + url: "", description: "", layouts: [], screens: [], @@ -62,6 +63,7 @@ export const getFrontendStore = () => { libraries: pkg.application.componentLibraries, components, name: pkg.application.name, + url: pkg.application.url, description: pkg.application.description, appId: pkg.application._id, layouts, 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/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/Dropdowns/DropdownItem.svelte b/packages/builder/src/components/common/Dropdowns/DropdownItem.svelte index 66a384fc90..f6f1ec2d12 100644 --- a/packages/builder/src/components/common/Dropdowns/DropdownItem.svelte +++ b/packages/builder/src/components/common/Dropdowns/DropdownItem.svelte @@ -11,7 +11,9 @@ on:click class:big={subtitle != null} {...$$restProps}> - {#if icon}{/if} + {#if icon} + + {/if}
{title}
{#if subtitle != null} @@ -56,7 +58,7 @@ } .title { - font-weight: 400; + font-weight: 500; } .subtitle { @@ -65,6 +67,10 @@ } i { - font-size: 16px; + padding: 0.5rem; + background-color: var(--grey-2); + font-size: 24px; + border-radius: var(--border-radius-s); + color: var(--ink); } 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/integration/QueryEditor.svelte b/packages/builder/src/components/integration/QueryEditor.svelte new file mode 100644 index 0000000000..22cddb5606 --- /dev/null +++ b/packages/builder/src/components/integration/QueryEditor.svelte @@ -0,0 +1,162 @@ + + +