From bcf431a476bffe9691824f1561aed5843bff4c04 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 8 Nov 2023 14:43:28 +0000 Subject: [PATCH 001/102] Fix a few svelte bugs with the LinkedRowSelector component to fix adding/editing relationships via a model from the data UI --- .../automation/SetupPanel/RowSelectorTypes.svelte | 2 +- .../components/backend/DataTable/RowFieldControl.svelte | 7 ++++++- .../builder/src/components/common/LinkedRowSelector.svelte | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte index 373d174541..f6cce6182c 100644 --- a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte +++ b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte @@ -64,7 +64,7 @@ {:else if schema.type === "link"} onChange(e, field)} /> diff --git a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte index 61b706e28e..1ec32cb3fd 100644 --- a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte +++ b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte @@ -70,7 +70,12 @@ options={meta.constraints.inclusion} /> {:else if type === "link"} - + (value = e.detail)} + /> {:else if type === "longform"} {#if meta.useRichText} diff --git a/packages/builder/src/components/common/LinkedRowSelector.svelte b/packages/builder/src/components/common/LinkedRowSelector.svelte index d357a0a54d..c45b7be195 100644 --- a/packages/builder/src/components/common/LinkedRowSelector.svelte +++ b/packages/builder/src/components/common/LinkedRowSelector.svelte @@ -56,12 +56,12 @@ /> {:else} row._id} sort - on:change={() => dispatch("change", linkedIds)} + on:change /> {/if} From cade7d339ef2e962a019d1dd721f6cbffc05b2a2 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 20 Nov 2023 17:03:38 +0000 Subject: [PATCH 002/102] First pass at updating Helm chart. --- charts/budibase/Chart.yaml | 6 +--- charts/budibase/templates/alb-ingress.yaml | 16 ++++----- .../templates/app-service-deployment.yaml | 8 +---- .../templates/app-service-service.yaml | 10 ++---- charts/budibase/templates/couchdb-backup.yaml | 8 ----- .../minio-data-persistentvolumeclaim.yaml | 1 - .../templates/minio-service-deployment.yaml | 8 ----- .../templates/minio-service-service.yaml | 4 --- .../templates/proxy-service-deployment.yaml | 5 --- .../templates/proxy-service-service.yaml | 12 +++---- .../redis-data-persistentvolumeclaim.yaml | 1 - .../templates/redis-service-deployment.yaml | 8 ----- .../templates/redis-service-service.yaml | 4 --- .../templates/worker-service-deployment.yaml | 6 ---- .../templates/worker-service-service.yaml | 10 ++---- charts/budibase/values.yaml | 33 +++++-------------- 16 files changed, 29 insertions(+), 111 deletions(-) diff --git a/charts/budibase/Chart.yaml b/charts/budibase/Chart.yaml index 05b3f24dbd..b62c1ca248 100644 --- a/charts/budibase/Chart.yaml +++ b/charts/budibase/Chart.yaml @@ -17,10 +17,6 @@ version: 0.0.0 appVersion: 0.0.0 dependencies: - name: couchdb - version: 3.3.4 + version: 4.4.5 repository: https://apache.github.io/couchdb-helm condition: services.couchdb.enabled - - name: ingress-nginx - version: 4.0.13 - repository: https://kubernetes.github.io/ingress-nginx - condition: ingress.nginx diff --git a/charts/budibase/templates/alb-ingress.yaml b/charts/budibase/templates/alb-ingress.yaml index 6cd1cf2cba..01d7fe0bf0 100644 --- a/charts/budibase/templates/alb-ingress.yaml +++ b/charts/budibase/templates/alb-ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.aws }} +{{- if .Values.awsAlbIngress.enabled }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -9,22 +9,22 @@ metadata: alb.ingress.kubernetes.io/target-type: ip alb.ingress.kubernetes.io/success-codes: 200,301 alb.ingress.kubernetes.io/healthcheck-path: / - {{- if .Values.ingress.certificateArn }} + {{- if .Values.awsAlbIngress.certificateArn }} alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}' alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' - alb.ingress.kubernetes.io/certificate-arn: {{ .Values.ingress.certificateArn }} + alb.ingress.kubernetes.io/certificate-arn: {{ .Values.awsAlbIngress.certificateArn }} {{- end }} - {{- if .Values.ingress.sslPolicy }} - alb.ingress.kubernetes.io/actions.ssl-policy: {{ .Values.ingress.sslPolicy }} + {{- if .Values.awsAlbIngress.sslPolicy }} + alb.ingress.kubernetes.io/actions.ssl-policy: {{ .Values.awsAlbIngress.sslPolicy }} {{- end }} - {{- if .Values.ingress.securityGroups }} - alb.ingress.kubernetes.io/security-groups: {{ .Values.ingress.securityGroups }} + {{- if .Values.awsAlbIngress.securityGroups }} + alb.ingress.kubernetes.io/security-groups: {{ .Values.awsAlbIngress.securityGroups }} {{- end }} spec: rules: - http: paths: - {{- if .Values.ingress.certificateArn }} + {{- if .Values.awsAlbIngress.certificateArn }} - path: / pathType: Prefix backend: diff --git a/charts/budibase/templates/app-service-deployment.yaml b/charts/budibase/templates/app-service-deployment.yaml index 73c6d990d2..fbe9c1d363 100644 --- a/charts/budibase/templates/app-service-deployment.yaml +++ b/charts/budibase/templates/app-service-deployment.yaml @@ -2,12 +2,9 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) {{ if .Values.services.apps.deploymentAnnotations }} {{- toYaml .Values.services.apps.deploymentAnnotations | indent 4 -}} {{ end }} - creationTimestamp: null labels: io.kompose.service: app-service {{ if .Values.services.apps.deploymentLabels }} @@ -24,12 +21,9 @@ spec: template: metadata: annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) {{ if .Values.services.apps.templateAnnotations }} {{- toYaml .Values.services.apps.templateAnnotations | indent 8 -}} {{ end }} - creationTimestamp: null labels: io.kompose.service: app-service {{ if .Values.services.apps.templateLabels }} @@ -200,7 +194,7 @@ spec: {{ end }} image: budibase/apps:{{ .Values.globals.appVersion | default .Chart.AppVersion }} - imagePullPolicy: Always + imagePullPolicy: IfNotPresent {{- if .Values.services.apps.startupProbe }} {{- with .Values.services.apps.startupProbe }} startupProbe: diff --git a/charts/budibase/templates/app-service-service.yaml b/charts/budibase/templates/app-service-service.yaml index 5247b4de09..390fbfe782 100644 --- a/charts/budibase/templates/app-service-service.yaml +++ b/charts/budibase/templates/app-service-service.yaml @@ -1,18 +1,14 @@ apiVersion: v1 kind: Service metadata: - annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) - creationTimestamp: null labels: io.kompose.service: app-service name: app-service spec: ports: - - name: {{ .Values.services.apps.port | quote }} - port: {{ .Values.services.apps.port }} - targetPort: {{ .Values.services.apps.port }} + - name: {{ .Values.services.apps.port | quote }} + port: {{ .Values.services.apps.port }} + targetPort: {{ .Values.services.apps.port }} selector: io.kompose.service: app-service status: diff --git a/charts/budibase/templates/couchdb-backup.yaml b/charts/budibase/templates/couchdb-backup.yaml index 7396f97476..6f842537a7 100644 --- a/charts/budibase/templates/couchdb-backup.yaml +++ b/charts/budibase/templates/couchdb-backup.yaml @@ -2,10 +2,6 @@ apiVersion: apps/v1 kind: Deployment metadata: - annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) - creationTimestamp: null labels: app.kubernetes.io/name: couchdb-backup name: couchdb-backup @@ -18,10 +14,6 @@ spec: type: Recreate template: metadata: - annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) - creationTimestamp: null labels: app.kubernetes.io/name: couchdb-backup spec: diff --git a/charts/budibase/templates/minio-data-persistentvolumeclaim.yaml b/charts/budibase/templates/minio-data-persistentvolumeclaim.yaml index abcf341bc5..c17001a436 100644 --- a/charts/budibase/templates/minio-data-persistentvolumeclaim.yaml +++ b/charts/budibase/templates/minio-data-persistentvolumeclaim.yaml @@ -2,7 +2,6 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - creationTimestamp: null labels: io.kompose.service: minio-data name: minio-data diff --git a/charts/budibase/templates/minio-service-deployment.yaml b/charts/budibase/templates/minio-service-deployment.yaml index f98ecc139d..28e8eb9991 100644 --- a/charts/budibase/templates/minio-service-deployment.yaml +++ b/charts/budibase/templates/minio-service-deployment.yaml @@ -2,10 +2,6 @@ apiVersion: apps/v1 kind: Deployment metadata: - annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) - creationTimestamp: null labels: io.kompose.service: minio-service name: minio-service @@ -18,10 +14,6 @@ spec: type: Recreate template: metadata: - annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) - creationTimestamp: null labels: io.kompose.service: minio-service spec: diff --git a/charts/budibase/templates/minio-service-service.yaml b/charts/budibase/templates/minio-service-service.yaml index cfdb22002b..ce89f1347c 100644 --- a/charts/budibase/templates/minio-service-service.yaml +++ b/charts/budibase/templates/minio-service-service.yaml @@ -2,10 +2,6 @@ apiVersion: v1 kind: Service metadata: - annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) - creationTimestamp: null labels: io.kompose.service: minio-service name: minio-service diff --git a/charts/budibase/templates/proxy-service-deployment.yaml b/charts/budibase/templates/proxy-service-deployment.yaml index 53bba6232d..26f4265e4c 100644 --- a/charts/budibase/templates/proxy-service-deployment.yaml +++ b/charts/budibase/templates/proxy-service-deployment.yaml @@ -2,12 +2,9 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) {{ if .Values.services.proxy.deploymentAnnotations }} {{- toYaml .Values.services.proxy.deploymentAnnotations | indent 4 -}} {{ end }} - creationTimestamp: null labels: app.kubernetes.io/name: budibase-proxy {{ if .Values.services.proxy.deploymentLabels }} @@ -24,8 +21,6 @@ spec: template: metadata: annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) {{ if .Values.services.proxy.templateAnnotations }} {{- toYaml .Values.services.proxy.templateAnnotations | indent 8 -}} {{ end }} diff --git a/charts/budibase/templates/proxy-service-service.yaml b/charts/budibase/templates/proxy-service-service.yaml index bf2b199ee5..e5dde301fa 100644 --- a/charts/budibase/templates/proxy-service-service.yaml +++ b/charts/budibase/templates/proxy-service-service.yaml @@ -1,19 +1,15 @@ apiVersion: v1 kind: Service metadata: - annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) - creationTimestamp: null labels: app.kubernetes.io/name: budibase-proxy name: proxy-service spec: ports: - - name: {{ .Values.services.proxy.port | quote }} - port: {{ .Values.services.proxy.port }} - targetPort: {{ .Values.services.proxy.port }} + - name: { { .Values.services.proxy.port | quote } } + port: { { .Values.services.proxy.port } } + targetPort: { { .Values.services.proxy.port } } selector: app.kubernetes.io/name: budibase-proxy status: - loadBalancer: {} \ No newline at end of file + loadBalancer: {} diff --git a/charts/budibase/templates/redis-data-persistentvolumeclaim.yaml b/charts/budibase/templates/redis-data-persistentvolumeclaim.yaml index 6f11492b58..e605a98376 100644 --- a/charts/budibase/templates/redis-data-persistentvolumeclaim.yaml +++ b/charts/budibase/templates/redis-data-persistentvolumeclaim.yaml @@ -2,7 +2,6 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - creationTimestamp: null labels: io.kompose.service: redis-data name: redis-data diff --git a/charts/budibase/templates/redis-service-deployment.yaml b/charts/budibase/templates/redis-service-deployment.yaml index 9b39d14291..bca40d2237 100644 --- a/charts/budibase/templates/redis-service-deployment.yaml +++ b/charts/budibase/templates/redis-service-deployment.yaml @@ -2,10 +2,6 @@ apiVersion: apps/v1 kind: Deployment metadata: - annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) - creationTimestamp: null labels: io.kompose.service: redis-service name: redis-service @@ -18,10 +14,6 @@ spec: type: Recreate template: metadata: - annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) - creationTimestamp: null labels: io.kompose.service: redis-service spec: diff --git a/charts/budibase/templates/redis-service-service.yaml b/charts/budibase/templates/redis-service-service.yaml index 55ca40ed88..b8f998925a 100644 --- a/charts/budibase/templates/redis-service-service.yaml +++ b/charts/budibase/templates/redis-service-service.yaml @@ -2,10 +2,6 @@ apiVersion: v1 kind: Service metadata: - annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) - creationTimestamp: null labels: io.kompose.service: redis-service name: redis-service diff --git a/charts/budibase/templates/worker-service-deployment.yaml b/charts/budibase/templates/worker-service-deployment.yaml index 5e0addb9dd..6427aa70e8 100644 --- a/charts/budibase/templates/worker-service-deployment.yaml +++ b/charts/budibase/templates/worker-service-deployment.yaml @@ -2,12 +2,9 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) {{ if .Values.services.worker.deploymentAnnotations }} {{- toYaml .Values.services.worker.deploymentAnnotations | indent 4 -}} {{ end }} - creationTimestamp: null labels: io.kompose.service: worker-service {{ if .Values.services.worker.deploymentLabels }} @@ -24,12 +21,9 @@ spec: template: metadata: annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) {{ if .Values.services.worker.templateAnnotations }} {{- toYaml .Values.services.worker.templateAnnotations | indent 8 -}} {{ end }} - creationTimestamp: null labels: io.kompose.service: worker-service {{ if .Values.services.worker.templateLabels }} diff --git a/charts/budibase/templates/worker-service-service.yaml b/charts/budibase/templates/worker-service-service.yaml index a79ba1e04b..214ee7191e 100644 --- a/charts/budibase/templates/worker-service-service.yaml +++ b/charts/budibase/templates/worker-service-service.yaml @@ -1,18 +1,14 @@ apiVersion: v1 kind: Service metadata: - annotations: - kompose.cmd: kompose convert - kompose.version: 1.21.0 (992df58d8) - creationTimestamp: null labels: io.kompose.service: worker-service name: worker-service spec: ports: - - name: {{ .Values.services.worker.port | quote }} - port: {{ .Values.services.worker.port }} - targetPort: {{ .Values.services.worker.port }} + - name: { { .Values.services.worker.port | quote } } + port: { { .Values.services.worker.port } } + targetPort: { { .Values.services.worker.port } } selector: io.kompose.service: worker-service status: diff --git a/charts/budibase/values.yaml b/charts/budibase/values.yaml index 857067d0f1..5c750ce2ca 100644 --- a/charts/budibase/values.yaml +++ b/charts/budibase/values.yaml @@ -2,11 +2,6 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. -image: - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "" - imagePullSecrets: [] nameOverride: "" # fullnameOverride: "" @@ -41,14 +36,10 @@ service: ingress: enabled: true - aws: false - nginx: true - certificateArn: "" className: "" annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/client-max-body-size: 150M - nginx.ingress.kubernetes.io/proxy-body-size: 50m + # nginx.ingress.kubernetes.io/client-max-body-size: 150M + # nginx.ingress.kubernetes.io/proxy-body-size: 50m hosts: - host: # change if using custom domain paths: @@ -60,6 +51,10 @@ ingress: port: number: 10000 +awsAlbIngress: + enabled: false + certificateArn: "" + autoscaling: enabled: false minReplicas: 1 @@ -111,7 +106,6 @@ globals: # globalAgentNoProxy: services: - budibaseVersion: latest dns: cluster.local # tlsRejectUnauthorized: 0 @@ -145,10 +139,6 @@ services: scheme: HTTP failureThreshold: 3 periodSeconds: 5 - # annotations: - # co.elastic.logs/module: nginx - # co.elastic.logs/fileset.stdout: access - # co.elastic.logs/fileset.stderr: error apps: port: 4002 @@ -280,7 +270,6 @@ couchdb: # adminUsername: budibase # adminPassword: budibase - # adminHash: -pbkdf2-this_is_not_necessarily_secure_either # cookieAuthSecret: admin ## When enabled, will deploy a networkpolicy that allows CouchDB pods to @@ -310,16 +299,12 @@ couchdb: ## The CouchDB image image: - repository: couchdb - tag: 3.1.1 + repository: budibase/couchdb + tag: 3.2.1 pullPolicy: IfNotPresent ## Experimental integration with Lucene-powered fulltext search - enableSearch: true - searchImage: - repository: kocolosk/couchdb-search - tag: 0.2.0 - pullPolicy: IfNotPresent + enableSearch: false initImage: repository: busybox From 08d45820a2d752de1e26e420a607aca649800ed4 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 21 Nov 2023 10:29:27 +0000 Subject: [PATCH 003/102] Update chart dependencies, fix some syntactic errors. --- charts/budibase/Chart.lock | 9 +- charts/budibase/Chart.yaml | 2 +- charts/budibase/charts/couchdb-3.3.4.tgz | Bin 9668 -> 0 bytes charts/budibase/charts/couchdb-4.3.0.tgz | Bin 0 -> 14629 bytes .../budibase/charts/ingress-nginx-4.0.13.tgz | Bin 27766 -> 0 bytes .../templates/app-service-service.yaml | 6 +- .../templates/proxy-service-service.yaml | 6 +- .../templates/worker-service-service.yaml | 6 +- charts/budibase/values.yaml | 175 +++++++++++++++--- 9 files changed, 159 insertions(+), 45 deletions(-) delete mode 100644 charts/budibase/charts/couchdb-3.3.4.tgz create mode 100644 charts/budibase/charts/couchdb-4.3.0.tgz delete mode 100644 charts/budibase/charts/ingress-nginx-4.0.13.tgz diff --git a/charts/budibase/Chart.lock b/charts/budibase/Chart.lock index 75b9de07b5..3ee752a362 100644 --- a/charts/budibase/Chart.lock +++ b/charts/budibase/Chart.lock @@ -1,9 +1,6 @@ dependencies: - name: couchdb repository: https://apache.github.io/couchdb-helm - version: 3.3.4 -- name: ingress-nginx - repository: https://kubernetes.github.io/ingress-nginx - version: 4.0.13 -digest: sha256:20892705c2d8e64c98257d181063a514ac55013e2b43399a6e54868a97f97845 -generated: "2021-12-30T18:55:30.878411Z" + version: 4.3.0 +digest: sha256:94449a7f195b186f5af33ec5aa66d58b36bede240fae710f021ca87837b30606 +generated: "2023-11-20T17:43:02.777596Z" diff --git a/charts/budibase/Chart.yaml b/charts/budibase/Chart.yaml index b62c1ca248..e2c9378f2c 100644 --- a/charts/budibase/Chart.yaml +++ b/charts/budibase/Chart.yaml @@ -17,6 +17,6 @@ version: 0.0.0 appVersion: 0.0.0 dependencies: - name: couchdb - version: 4.4.5 + version: 4.3.0 repository: https://apache.github.io/couchdb-helm condition: services.couchdb.enabled diff --git a/charts/budibase/charts/couchdb-3.3.4.tgz b/charts/budibase/charts/couchdb-3.3.4.tgz deleted file mode 100644 index f7ebfd3e9653ef2e0fba07de4e498b46311de606..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9668 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 GzyJWm|L4yD diff --git a/charts/budibase/charts/couchdb-4.3.0.tgz b/charts/budibase/charts/couchdb-4.3.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..d3cce28ee630a20d8bdc0051746b6e2dac95f7fd GIT binary patch literal 14629 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 K0YMK1@&5p2<=4Le diff --git a/charts/budibase/templates/app-service-service.yaml b/charts/budibase/templates/app-service-service.yaml index 390fbfe782..6d19590d45 100644 --- a/charts/budibase/templates/app-service-service.yaml +++ b/charts/budibase/templates/app-service-service.yaml @@ -6,9 +6,9 @@ metadata: name: app-service spec: ports: - - name: {{ .Values.services.apps.port | quote }} - port: {{ .Values.services.apps.port }} - targetPort: {{ .Values.services.apps.port }} + - name: {{ .Values.services.apps.port | quote }} + port: {{ .Values.services.apps.port }} + targetPort: {{ .Values.services.apps.port }} selector: io.kompose.service: app-service status: diff --git a/charts/budibase/templates/proxy-service-service.yaml b/charts/budibase/templates/proxy-service-service.yaml index e5dde301fa..988c540599 100644 --- a/charts/budibase/templates/proxy-service-service.yaml +++ b/charts/budibase/templates/proxy-service-service.yaml @@ -6,9 +6,9 @@ metadata: name: proxy-service spec: ports: - - name: { { .Values.services.proxy.port | quote } } - port: { { .Values.services.proxy.port } } - targetPort: { { .Values.services.proxy.port } } + - name: {{ .Values.services.proxy.port | quote }} + port: {{ .Values.services.proxy.port }} + targetPort: {{ .Values.services.proxy.port }} selector: app.kubernetes.io/name: budibase-proxy status: diff --git a/charts/budibase/templates/worker-service-service.yaml b/charts/budibase/templates/worker-service-service.yaml index 214ee7191e..c5f56ba205 100644 --- a/charts/budibase/templates/worker-service-service.yaml +++ b/charts/budibase/templates/worker-service-service.yaml @@ -6,9 +6,9 @@ metadata: name: worker-service spec: ports: - - name: { { .Values.services.worker.port | quote } } - port: { { .Values.services.worker.port } } - targetPort: { { .Values.services.worker.port } } + - name: {{ .Values.services.worker.port | quote }} + port: {{ .Values.services.worker.port }} + targetPort: {{ .Values.services.worker.port }} selector: io.kompose.service: worker-service status: diff --git a/charts/budibase/values.yaml b/charts/budibase/values.yaml index 5c750ce2ca..9409363bbb 100644 --- a/charts/budibase/values.yaml +++ b/charts/budibase/values.yaml @@ -261,22 +261,45 @@ services: # Override values in couchDB subchart couchdb: - ## clusterSize is the initial size of the CouchDB cluster. - clusterSize: 1 + # -- the initial number of nodes in the CouchDB cluster. + clusterSize: 3 + + # -- If allowAdminParty is enabled the cluster will start up without any database + # administrator account; i.e., all users will be granted administrative + # access. Otherwise, the system will look for a Secret called + # -couchdb containing `adminUsername`, `adminPassword` and + # `cookieAuthSecret` keys. See the `createAdminSecret` flag. + # ref: https://kubernetes.io/docs/concepts/configuration/secret/ allowAdminParty: false - # Secret Management + # -- If createAdminSecret is enabled a Secret called -couchdb will + # be created containing auto-generated credentials. Users who prefer to set + # these values themselves have a couple of options: + # + # 1) The `adminUsername`, `adminPassword`, `adminHash`, and `cookieAuthSecret` + # can be defined directly in the chart's values. Note that all of a chart's + # values are currently stored in plaintext in a ConfigMap in the tiller + # namespace. + # + # 2) This flag can be disabled and a Secret with the required keys can be + # created ahead of time. createAdminSecret: true - # adminUsername: budibase - # adminPassword: budibase - # cookieAuthSecret: admin + adminUsername: admin + # adminPassword: this_is_not_secure + # adminHash: -pbkdf2-this_is_not_necessarily_secure_either + # cookieAuthSecret: neither_is_this ## When enabled, will deploy a networkpolicy that allows CouchDB pods to ## communicate with each other for clustering and ingress on port 5984 networkPolicy: enabled: true + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + # Use a service account serviceAccount: enabled: true @@ -285,25 +308,28 @@ couchdb: # imagePullSecrets: # - name: myimagepullsecret - ## The storage volume used by each Pod in the StatefulSet. If a - ## persistentVolume is not enabled, the Pods will use `emptyDir` ephemeral - ## local storage. Setting the storageClass attribute to "-" disables dynamic - ## provisioning of Persistent Volumes; leaving it unset will invoke the default - ## provisioner. + # -- The storage volume used by each Pod in the StatefulSet. If a + # persistentVolume is not enabled, the Pods will use `emptyDir` ephemeral + # local storage. Setting the storageClass attribute to "-" disables dynamic + # provisioning of Persistent Volumes; leaving it unset will invoke the default + # provisioner. persistentVolume: enabled: false + # NOTE: the number of existing claims must match the cluster size + existingClaims: [] + annotations: {} accessModes: - ReadWriteOnce size: 10Gi - storageClass: "" + # storageClass: "-" ## The CouchDB image image: repository: budibase/couchdb - tag: 3.2.1 + tag: v3.2.1 pullPolicy: IfNotPresent - ## Experimental integration with Lucene-powered fulltext search + # -- Flip this to flag to include the Search container in each Pod enableSearch: false initImage: @@ -316,19 +342,52 @@ couchdb: ## `OrderedReady` podManagementPolicy: Parallel + ## To better tolerate Node failures, we can prevent Kubernetes scheduler from + ## assigning more than one Pod of CouchDB StatefulSet per Node using podAntiAffinity. + affinity: + {} + # podAntiAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # - labelSelector: + # matchExpressions: + # - key: "app" + # operator: In + # values: + # - couchdb + # topologyKey: "kubernetes.io/hostname" + + ## To control how Pods are spread across your cluster among failure-domains such as regions, + ## zones, nodes, and other user-defined topology domains use topologySpreadConstraints. + topologySpreadConstraints: + {} + # topologySpreadConstraints: + # - maxSkew: 1 + # topologyKey: "topology.kubernetes.io/zone" + # whenUnsatisfiable: ScheduleAnyway + # labelSelector: + # matchLabels: + # app: couchdb + + ## Optional pod labels + labels: {} + ## Optional pod annotations annotations: {} ## Optional tolerations tolerations: [] - affinity: {} - + ## A StatefulSet requires a headless Service to establish the stable network + ## identities of the Pods, and that Service is created automatically by this + ## chart without any additional configuration. The Service block below refers + ## to a second Service that governs how clients connect to the CouchDB cluster. service: - # annotations: + annotations: {} enabled: true type: ClusterIP externalPort: 5984 + targetPort: 5984 + labels: {} ## An Ingress resource can provide name-based virtual hosting and TLS ## termination among other things for CouchDB deployments which are accessed @@ -336,11 +395,12 @@ couchdb: ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/ ingress: enabled: false + # className: nginx hosts: - chart-example.local path: / annotations: - [] + {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" tls: @@ -360,21 +420,35 @@ couchdb: # cpu: 56 # memory: 256Gi - ## erlangFlags is a map that is passed to the Erlang VM as flags using the - ## ERL_FLAGS env. `name` and `setcookie` flags are minimally required to - ## establish connectivity between cluster nodes. - ## ref: http://erlang.org/doc/man/erl.html#init_flags + ## Optional resource requests and limits for the CouchDB init container + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + initResources: + {} + # requests: + # cpu: 100m + # memory: 128Mi + # limits: + # cpu: 500m + # memory: 128Mi + + # -- erlangFlags is a map that is passed to the Erlang VM as flags using the + # ERL_FLAGS env. The `name` flag is required to establish connectivity + # between cluster nodes. + # ref: http://erlang.org/doc/man/erl.html#init_flags erlangFlags: name: couchdb - setcookie: monster + # Older versions of the official CouchDB image (anything prior to 3.2.1) + # do not act on the COUCHDB_ERLANG_COOKIE environment variable, so if you + # want to cluster these deployments it's necessary to pass in a cookie here + # setcookie: make-something-up - ## couchdbConfig will override default CouchDB configuration settings. - ## The contents of this map are reformatted into a .ini file laid down - ## by a ConfigMap object. - ## ref: http://docs.couchdb.org/en/latest/config/index.html + # -- couchdbConfig will override default CouchDB configuration settings. + # The contents of this map are reformatted into a .ini file laid down + # by a ConfigMap object. + # ref: http://docs.couchdb.org/en/latest/config/index.html couchdbConfig: couchdb: - uuid: budibase-couchdb # REQUIRED: Unique identifier for this CouchDB server instance + uuid: decafbaddecafbaddecafbaddecafbad # Unique identifier for this CouchDB server instance # cluster: # q: 8 # Create 8 shards for each database chttpd: @@ -382,6 +456,9 @@ couchdb: # chttpd.require_valid_user disables all the anonymous requests to the port # 5984 when is set to true. require_valid_user: false + # required to use Fauxton if chttpd.require_valid_user is set to true + # httpd: + # WWW-Authenticate: "Basic realm=\"administrator\"" # Kubernetes local cluster domain. # This is used to generate FQDNs for peers when joining the CouchDB cluster. @@ -390,16 +467,56 @@ couchdb: ## Configure liveness and readiness probe values ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes - # FOR COUCHDB livenessProbe: + enabled: true failureThreshold: 3 initialDelaySeconds: 0 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1 readinessProbe: + enabled: true failureThreshold: 3 initialDelaySeconds: 0 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1 + + # Control an optional pod disruption budget + podDisruptionBudget: + # toggle creation of pod disruption budget, disabled by default + enabled: false + # minAvailable: 1 + maxUnavailable: 1 + + # CouchDB 3.2.0 adds in a metrics endpoint on the path `/_node/_local/_prometheus`. + # Optionally, a standalone, unauthenticated port can be exposed for these metrics. + prometheusPort: + enabled: false + bind_address: "0.0.0.0" + port: 17986 + + # Configure arbitrary sidecar containers for CouchDB pods created by the + # StatefulSet + sidecars: + {} + # - name: foo + # image: "busybox" + # imagePullPolicy: IfNotPresent + # resources: + # requests: + # cpu: "0.1" + # memory: 10Mi + # command: ['echo "foo";'] + # volumeMounts: + # - name: database-storage + # mountPath: /opt/couchdb/data/ + + # Placement manager to annotate each document in the nodes DB with "zone" attribute + # recording the zone where node has been scheduled + # Ref: https://docs.couchdb.org/en/stable/cluster/sharding.html#specifying-database-placement + placementConfig: + enabled: false + image: + repository: caligrafix/couchdb-autoscaler-placement-manager + tag: 0.1.0 From 217ac49628c520f522cd3f503e577971f6232496 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Tue, 21 Nov 2023 13:02:06 +0000 Subject: [PATCH 004/102] wip --- packages/bbui/src/Form/Core/TextField.svelte | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/bbui/src/Form/Core/TextField.svelte b/packages/bbui/src/Form/Core/TextField.svelte index 7afd8f86c3..cbc062d99a 100644 --- a/packages/bbui/src/Form/Core/TextField.svelte +++ b/packages/bbui/src/Form/Core/TextField.svelte @@ -21,7 +21,7 @@ let focus = false const updateValue = newValue => { - if (readonly) { + if (readonly || disabled) { return } if (type === "number") { @@ -32,14 +32,14 @@ } const onFocus = () => { - if (readonly) { + if (readonly || disabled) { return } focus = true } const onBlur = event => { - if (readonly) { + if (readonly || disabled) { return } focus = false @@ -47,14 +47,14 @@ } const onInput = event => { - if (readonly || !updateOnChange) { + if (readonly || !updateOnChange || disabled) { return } updateValue(event.target.value) } const updateValueOnEnter = event => { - if (readonly) { + if (readonly || disabled) { return } if (event.key === "Enter") { @@ -66,10 +66,12 @@ if (type === "bigint") { return "numeric" } - return type === "number" ? "decimal" : "text" + return type === "number" ? +"decimal" : "text" } onMount(() => { + if (disabled) return; focus = autofocus if (focus) field.focus() }) @@ -119,4 +121,8 @@ .spectrum-Textfield { width: 100%; } + + input:focus:hover::placeholder { + color: var(--grey-8) !important; + } From 96046dab3dc56246056fadfc490030ca22517d42 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Tue, 21 Nov 2023 14:51:01 +0000 Subject: [PATCH 005/102] component name tooltips --- .../builder/src/builderStore/componentUtils.js | 17 +++++++++++++++++ .../src/components/common/NavItem.svelte | 5 ++++- .../builder/src/components/design/Panel.svelte | 7 +++++-- .../Component/ComponentSettingsPanel.svelte | 8 +++++--- .../ComponentList/ComponentTree.svelte | 2 ++ 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/builder/src/builderStore/componentUtils.js b/packages/builder/src/builderStore/componentUtils.js index 522dbae416..85171ece66 100644 --- a/packages/builder/src/builderStore/componentUtils.js +++ b/packages/builder/src/builderStore/componentUtils.js @@ -1,4 +1,5 @@ import { store } from "./index" +import { get } from "svelte/store" import { Helpers } from "@budibase/bbui" import { decodeJSBinding, @@ -238,6 +239,10 @@ export const makeComponentUnique = component => { } export const getComponentText = component => { + if (component == null) { + return "" + } + if (component?._instanceName) { return component._instanceName } @@ -246,3 +251,15 @@ export const getComponentText = component => { "component" return capitalise(type) } + +export const getComponentName = component => { + if (component == null) { + return "" + } + + const components = get(store)?.components || {}; + const componentDefinition = components[component._component] || {}; + const name = componentDefinition.friendlyName || componentDefinition.name || ""; + + return name; +} diff --git a/packages/builder/src/components/common/NavItem.svelte b/packages/builder/src/components/common/NavItem.svelte index 2c8a862535..5ec399a7ea 100644 --- a/packages/builder/src/components/common/NavItem.svelte +++ b/packages/builder/src/components/common/NavItem.svelte @@ -1,10 +1,11 @@ {#if $selectedComponent} {#key $selectedComponent._id} - + { if (e.key.toLowerCase() === "enter") { e.target.blur() diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentTree.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentTree.svelte index 8adc6cb5d4..6b5d356deb 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentTree.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentTree.svelte @@ -11,6 +11,7 @@ import { findComponentPath, getComponentText, + getComponentName, } from "builderStore/componentUtils" import { get } from "svelte/store" import { dndStore } from "./dndStore" @@ -109,6 +110,7 @@ on:drop={onDrop} text={getComponentText(component)} icon={getComponentIcon(component)} + iconTooltip={getComponentName(component)} withArrow={componentHasChildren(component)} indentLevel={level} selected={$store.selectedComponentId === component._id} From b7a704d06e1a6dc57d9967aa63468e2fb2998bbb Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 21 Nov 2023 17:32:58 +0000 Subject: [PATCH 006/102] Remove -name param from CouchDB image. Clouseau works without it and it fixes the image in Kubernetes. --- hosting/couchdb/clouseau/clouseau.ini | 3 --- hosting/couchdb/couch/vm.args | 1 - 2 files changed, 4 deletions(-) diff --git a/hosting/couchdb/clouseau/clouseau.ini b/hosting/couchdb/clouseau/clouseau.ini index 578a5acafa..9e03f8bc6e 100644 --- a/hosting/couchdb/clouseau/clouseau.ini +++ b/hosting/couchdb/clouseau/clouseau.ini @@ -1,8 +1,5 @@ [clouseau] -; the name of the Erlang node created by the service, leave this unchanged -name=clouseau@127.0.0.1 - ; set this to the same distributed Erlang cookie used by the CouchDB nodes cookie=monster diff --git a/hosting/couchdb/couch/vm.args b/hosting/couchdb/couch/vm.args index e9e4416863..33873b91a7 100644 --- a/hosting/couchdb/couch/vm.args +++ b/hosting/couchdb/couch/vm.args @@ -11,7 +11,6 @@ # the License. # erlang cookie for clouseau security --name couchdb@127.0.0.1 -setcookie monster # Ensure that the Erlang VM listens on a known port From 4af7bf459a64e6c041c1ac710e58c38cc1f7f386 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 21 Nov 2023 17:35:23 +0000 Subject: [PATCH 007/102] Remove extraneous creation timestamp. --- charts/budibase/templates/proxy-service-deployment.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/charts/budibase/templates/proxy-service-deployment.yaml b/charts/budibase/templates/proxy-service-deployment.yaml index 26f4265e4c..2e23f12793 100644 --- a/charts/budibase/templates/proxy-service-deployment.yaml +++ b/charts/budibase/templates/proxy-service-deployment.yaml @@ -24,7 +24,6 @@ spec: {{ if .Values.services.proxy.templateAnnotations }} {{- toYaml .Values.services.proxy.templateAnnotations | indent 8 -}} {{ end }} - creationTimestamp: null labels: app.kubernetes.io/name: budibase-proxy {{ if .Values.services.proxy.templateLabels }} From 423fa68f34007d36c83a51b3732d6fd1be8c7fb1 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 22 Nov 2023 10:35:53 +0000 Subject: [PATCH 008/102] Make the CouchDB image use the right directory on Kubernetes. --- hosting/couchdb/build-target-paths.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hosting/couchdb/build-target-paths.sh b/hosting/couchdb/build-target-paths.sh index 34227011f4..8bad974a1f 100644 --- a/hosting/couchdb/build-target-paths.sh +++ b/hosting/couchdb/build-target-paths.sh @@ -18,6 +18,11 @@ if [[ "${TARGETBUILD}" = "aas" ]]; then /etc/init.d/ssh restart sed -i "s#DATA_DIR#/home#g" /opt/clouseau/clouseau.ini sed -i "s#DATA_DIR#/home#g" /opt/couchdb/etc/local.ini +elif [[ -n $KUBERNETES_SERVICE_HOST ]]; then + # In Kubernetes the directory /opt/couchdb/data has a persistent volume + # mount for storing database data. + sed -i "s#DATA_DIR#/opt/couchdb/data#g" /opt/clouseau/clouseau.ini + sed -i "s#DATA_DIR#/opt/couchdb/data#g" /opt/couchdb/etc/local.ini else sed -i "s#DATA_DIR#/data#g" /opt/clouseau/clouseau.ini sed -i "s#DATA_DIR#/data#g" /opt/couchdb/etc/local.ini From 08ae4ef1328e87f8f420f0e6a494e2e4870be37f Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 22 Nov 2023 16:20:47 +0000 Subject: [PATCH 009/102] Fix the single image build working on Kubernetes. --- hosting/couchdb/build-target-paths.sh | 5 ++++- hosting/couchdb/runner.sh | 1 + hosting/single/Dockerfile.v2 | 1 - hosting/single/runner.sh | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/hosting/couchdb/build-target-paths.sh b/hosting/couchdb/build-target-paths.sh index 8bad974a1f..90d9130cd4 100644 --- a/hosting/couchdb/build-target-paths.sh +++ b/hosting/couchdb/build-target-paths.sh @@ -18,7 +18,10 @@ if [[ "${TARGETBUILD}" = "aas" ]]; then /etc/init.d/ssh restart sed -i "s#DATA_DIR#/home#g" /opt/clouseau/clouseau.ini sed -i "s#DATA_DIR#/home#g" /opt/couchdb/etc/local.ini -elif [[ -n $KUBERNETES_SERVICE_HOST ]]; then +elif [[ "${TARGETBUILD}" = "single" ]]; then + sed -i "s#DATA_DIR#/data#g" /opt/clouseau/clouseau.ini + sed -i "s#DATA_DIR#/data#g" /opt/couchdb/etc/local.ini +elif [[ -n $KUBERNETES_SERVICE_HOST ]]; then # In Kubernetes the directory /opt/couchdb/data has a persistent volume # mount for storing database data. sed -i "s#DATA_DIR#/opt/couchdb/data#g" /opt/clouseau/clouseau.ini diff --git a/hosting/couchdb/runner.sh b/hosting/couchdb/runner.sh index 4102d2a751..32951cb539 100644 --- a/hosting/couchdb/runner.sh +++ b/hosting/couchdb/runner.sh @@ -1,6 +1,7 @@ #!/bin/bash DATA_DIR=${DATA_DIR:-/data} + mkdir -p ${DATA_DIR} mkdir -p ${DATA_DIR}/couch/{dbs,views} mkdir -p ${DATA_DIR}/search diff --git a/hosting/single/Dockerfile.v2 b/hosting/single/Dockerfile.v2 index ec03a1b5a2..dd61c779b5 100644 --- a/hosting/single/Dockerfile.v2 +++ b/hosting/single/Dockerfile.v2 @@ -94,7 +94,6 @@ RUN chmod +x ./healthcheck.sh # For Azure App Service install SSH & point data locations to /home COPY hosting/single/ssh/sshd_config /etc/ COPY hosting/single/ssh/ssh_setup.sh /tmp -RUN /build-target-paths.sh # setup letsencrypt certificate diff --git a/hosting/single/runner.sh b/hosting/single/runner.sh index 87201c95c0..f4b2b5b127 100644 --- a/hosting/single/runner.sh +++ b/hosting/single/runner.sh @@ -22,11 +22,11 @@ declare -a DOCKER_VARS=("APP_PORT" "APPS_URL" "ARCHITECTURE" "BUDIBASE_ENVIRONME # Azure App Service customisations if [[ "${TARGETBUILD}" = "aas" ]]; then - DATA_DIR="${DATA_DIR:-/home}" + export DATA_DIR="${DATA_DIR:-/home}" WEBSITES_ENABLE_APP_SERVICE_STORAGE=true /etc/init.d/ssh start else - DATA_DIR=${DATA_DIR:-/data} + export DATA_DIR=${DATA_DIR:-/data} fi mkdir -p ${DATA_DIR} # Mount NFS or GCP Filestore if env vars exist for it From f1c3d50930c288b2d4d0aa33a373aecefce8b7c5 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Wed, 22 Nov 2023 20:50:55 +0000 Subject: [PATCH 010/102] functioning resize --- .../_components/ScreenList/index.svelte | 90 +++++-------- .../_components/ScreenList/resizable.js | 121 ++++++++++++++++++ 2 files changed, 150 insertions(+), 61 deletions(-) create mode 100644 packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte index ef5911c0f8..a7d56e047a 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte @@ -9,51 +9,42 @@ import NavItem from "components/common/NavItem.svelte" import RoleIndicator from "./RoleIndicator.svelte" import DropdownMenu from "./DropdownMenu.svelte" - import { onMount, tick } from "svelte" import { goto } from "@roxi/routify" + import { getVerticalResizeActions } from './resizable'; + import { tick } from "svelte" - let search = false + const [resizable, resizableHandle] = getVerticalResizeActions(); + + let searching = false let resizing = false let searchValue = "" let searchInput let container let screensContainer let scrolling = false - let previousHeight = null - let dragOffset $: filteredScreens = getFilteredScreens($sortedScreens, searchValue) - const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) - const openSearch = async () => { - search = true + searching = true await tick() searchInput.focus() screensContainer.scroll({ top: 0, behavior: "smooth" }) - previousHeight = $screensHeight - $screensHeight = "calc(100% + 1px)" } const closeSearch = async () => { - if (previousHeight) { - // Restore previous height and wait for animation - $screensHeight = previousHeight - previousHeight = null - await sleep(300) - } - search = false + searching = false searchValue = "" } - const getFilteredScreens = (screens, search) => { + const getFilteredScreens = (screens, searchValue) => { return screens.filter(screen => { - return !search || screen.routing.route.includes(search) + return !searchValue || screen.routing.route.includes(searchValue) }) } const handleAddButton = () => { - if (search) { + if (searching) { closeSearch() } else { $goto("../new") @@ -70,67 +61,35 @@ scrolling = e.target.scrollTop !== 0 } - const startResizing = e => { - // Reset the height store to match the true height - $screensHeight = `${container.getBoundingClientRect().height}px` - - // Store an offset to easily compute new height when moving the mouse - dragOffset = parseInt($screensHeight) - e.clientY - - // Add event listeners - resizing = true - document.addEventListener("mousemove", resize) - document.addEventListener("mouseup", stopResizing) - } - - const resize = e => { - // Prevent negative heights as this screws with layout - const newHeight = Math.max(0, e.clientY + dragOffset) - if (newHeight == null || isNaN(newHeight)) { - return - } - $screensHeight = `${newHeight}px` - } - - const stopResizing = () => { - resizing = false - document.removeEventListener("mousemove", resize) - } - - onMount(() => { - // Ensure we aren't stuck at 100% height from leaving while searching - if ($screensHeight == null || isNaN(parseInt($screensHeight))) { - $screensHeight = "210px" - } - })
-
+
Screens
-
+
@@ -164,8 +123,10 @@
screensHeight.set("210px")} />
@@ -177,10 +138,11 @@ min-height: 147px; max-height: calc(100% - 147px); position: relative; - } - .screens.search { transition: height 300ms ease-out; + } + .screens.searching { max-height: none; + height: 100% !important; } .screens.resizing { user-select: none; @@ -219,7 +181,7 @@ .input::placeholder { color: var(--spectrum-global-color-gray-600); } - .screens.search input { + .screens.searching input { display: block; } @@ -305,4 +267,10 @@ .divider:hover:after { background: var(--spectrum-global-color-gray-300); } + .divider.disabled { + cursor: auto; + } + .divider.disabled:after { + background: var(--spectrum-global-color-gray-200); + } diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js new file mode 100644 index 0000000000..c3bccf024d --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js @@ -0,0 +1,121 @@ +export const getHorizontalResizeActions = (initialValue, setValue = () => {}) => { + let element = null; + + const elementAction = (node) => { + element = node; + + if (initialValue != null) { + element.style.height = `${initialValue}px` + } + + return { + destroy() { + element = null; + } + } + } + + const dragHandleAction = (node) => { + let startWidth = null; + let startPosition = null; + + const handleMouseMove = (e) => { + const change = e.pageX - startPosition; + element.style.width = `${startWidth + change}px` + } + + const handleMouseUp = () => { + window.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("mouseup", handleMouseUp); + element.style.removeProperty('transition'); // remove temporary transition override + setValue(element.clientHeight); + } + + const handleMouseDown = (e) => { + if (e.target.hasAttribute("disabled") && e.target.getAttribute("disabled") !== "false") { + return; + } + + element.style.transition = "width 0ms"; // temporarily override any width transitions + startWidth = element.clientWidth; + startPosition = e.pageX; + + window.addEventListener("mousemove", handleMouseMove); + window.addEventListener("mouseup", handleMouseUp); + }; + + node.addEventListener("mousedown", handleMouseDown); + + return { + destroy() { + node.removeEventListener("mousedown", handleMouseDown); + } + } + } + + return [ + elementAction, + dragHandleAction + ] +}; + +export const getVerticalResizeActions = (initialValue, setValue = () => {}) => { + let element = null; + + const elementAction = (node) => { + element = node; + + if (initialValue != null) { + element.style.height = `${initialValue}px` + } + + return { + destroy() { + element = null; + } + } + } + + const dragHandleAction = (node) => { + let startHeight = null; + let startPosition = null; + + const handleMouseMove = (e) => { + const change = e.pageY - startPosition; + element.style.height = `${startHeight + change}px` + } + + const handleMouseUp = () => { + window.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("mouseup", handleMouseUp); + element.style.removeProperty('transition'); // remove temporary transition override + setValue(element.clientHeight); + } + + const handleMouseDown = (e) => { + if (e.target.hasAttribute("disabled") && e.target.getAttribute("disabled") !== "false") { + return; + } + + element.style.transition = "height 0ms"; // temporarily override any height transitions + startHeight = element.clientHeight; + startPosition = e.pageY; + + window.addEventListener("mousemove", handleMouseMove); + window.addEventListener("mouseup", handleMouseUp); + }; + + node.addEventListener("mousedown", handleMouseDown); + + return { + destroy() { + node.removeEventListener("mousedown", handleMouseDown); + } + } + } + + return [ + elementAction, + dragHandleAction + ] +}; From 3d39a2f77b85b56fd0c563e7ddf59ba2d91f44aa Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 23 Nov 2023 09:47:07 +0000 Subject: [PATCH 011/102] Fix relationship filter setting being ignored, and debounce searches --- .../app/forms/RelationshipField.svelte | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/client/src/components/app/forms/RelationshipField.svelte b/packages/client/src/components/app/forms/RelationshipField.svelte index 41a071686e..f047b53a94 100644 --- a/packages/client/src/components/app/forms/RelationshipField.svelte +++ b/packages/client/src/components/app/forms/RelationshipField.svelte @@ -1,6 +1,6 @@
-
-
- -
-
- {#if $store.clientFeatures.devicePreview} - - {/if} -
-
- {#key $store.version} - - {/key} +
+
+ +
+
+ {#if $store.clientFeatures.devicePreview} + + {/if} +
+
+
+ {#key $store.version} + + {/key} +
+ From e0e27aa3c96a09c1fedc2060c9bc0e6c307266b5 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Thu, 23 Nov 2023 14:09:14 +0000 Subject: [PATCH 016/102] wip --- .../[screenId]/_components/AppPanel.svelte | 42 +++++------------ .../[screenId]/_components/LeftPanel.svelte | 46 +++++++++++++++++-- .../_components/ScreenList/resizable.js | 1 + 3 files changed, 55 insertions(+), 34 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPanel.svelte index 7cd3261bce..09f97302fd 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPanel.svelte @@ -3,42 +3,28 @@ import AppPreview from "./AppPreview.svelte" import { store, screenHistoryStore } from "builderStore" import UndoRedoControl from "components/common/UndoRedoControl.svelte" - import { getHorizontalResizeActions } from './resizable'; - - const [resizable, resizableHandle] = getHorizontalResizeActions();
-
-
-
- -
-
- {#if $store.clientFeatures.devicePreview} - - {/if} -
+
+
+
-
- {#key $store.version} - - {/key} +
+ {#if $store.clientFeatures.devicePreview} + + {/if}
- diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte index 96d4579eeb..4f77588e9b 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte @@ -1,21 +1,59 @@ -
- - +
+
+ + +
+
+
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js index e74b72c6e5..4b9f365018 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js @@ -20,6 +20,7 @@ const getResizeActions = (cssProperty, mouseMoveEventProperty, elementProperty, let startPosition = null; const handleMouseMove = (e) => { + e.preventDefault(); // Prevent highlighting while dragging const change = e[mouseMoveEventProperty] - startPosition; element.style[cssProperty] = `${startProperty + change}px` } From 4175ccbffb2aa843cda75d5ab7a8db72b003d559 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Thu, 23 Nov 2023 14:12:54 +0000 Subject: [PATCH 017/102] wip --- .../[application]/design/[screenId]/_components/LeftPanel.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte index 4f77588e9b..e6248b1d0a 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte @@ -30,6 +30,7 @@ } .content { + overflow: scroll; flex-grow: 1; height: 100%; display: flex; From 23f5941bf52210eb42b2cc0dc5031717055bacb4 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Thu, 23 Nov 2023 17:05:49 +0000 Subject: [PATCH 018/102] move position of screen nav item menu --- .../builder/src/components/common/NavItem.svelte | 4 +++- .../builder/src/components/design/Panel.svelte | 2 ++ .../design/[screenId]/_components/AppPanel.svelte | 1 + .../[screenId]/_components/LeftPanel.svelte | 4 ++-- .../_components/ScreenList/index.svelte | 3 ++- .../_components/ScreenList/resizable.js | 15 ++++++++++++++- .../design/[screenId]/_layout.svelte | 1 + 7 files changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/components/common/NavItem.svelte b/packages/builder/src/components/common/NavItem.svelte index 5ec399a7ea..1a18d92eca 100644 --- a/packages/builder/src/components/common/NavItem.svelte +++ b/packages/builder/src/components/common/NavItem.svelte @@ -166,9 +166,10 @@ justify-content: flex-start; align-items: center; gap: var(--spacing-xs); - width: max-content; + width: 100%; position: relative; padding-left: var(--spacing-l); + box-sizing: border-box; } /* Needed to fully display the actions icon */ @@ -266,6 +267,7 @@ } .right { + margin-left: auto; order: 10; } diff --git a/packages/builder/src/components/design/Panel.svelte b/packages/builder/src/components/design/Panel.svelte index 6cfc65a1a4..f9190bea60 100644 --- a/packages/builder/src/components/design/Panel.svelte +++ b/packages/builder/src/components/design/Panel.svelte @@ -66,6 +66,7 @@ diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldSetting.svelte b/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldSetting.svelte index 1d9ce733b8..8c40c455c8 100644 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldSetting.svelte +++ b/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldSetting.svelte @@ -58,7 +58,15 @@
{readableText}
- + { + e.stopPropagation() + }} + text="" + value={item.active} + thin + />
diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 07645d874a..8573d0a376 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -6040,18 +6040,6 @@ "options": ["Create", "Update", "View"], "defaultValue": "Create" }, - { - "type": "text", - "label": "Title", - "key": "title", - "nested": true - }, - { - "type": "text", - "label": "Description", - "key": "description", - "nested": true - }, { "section": true, "dependsOn": { @@ -6059,7 +6047,7 @@ "value": "Create", "invert": true }, - "name": "Row details", + "name": "Row ID", "info": "How to pass a row ID using bindings", "settings": [ { @@ -6079,8 +6067,20 @@ }, { "section": true, - "name": "Fields", + "name": "Details", "settings": [ + { + "type": "text", + "label": "Title", + "key": "title", + "nested": true + }, + { + "type": "text", + "label": "Description", + "key": "description", + "nested": true + }, { "type": "fieldConfiguration", "key": "fields", From 22dc6a682b870691fb05a9fa9cc4dead31a7db28 Mon Sep 17 00:00:00 2001 From: Dean Date: Thu, 23 Nov 2023 18:15:33 +0000 Subject: [PATCH 022/102] Lint --- .../design/settings/controls/DraggableList/DraggableList.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte b/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte index 18b6700040..cce11e4b17 100644 --- a/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte +++ b/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte @@ -92,7 +92,7 @@ on:click={() => { get(store).actions.select(draggableItem.id) }} - on:mousedown={e => { + on:mousedown={() => { get(store).actions.select() }} bind:this={anchors[draggableItem.id]} From 1fb61e6ed9969eb229f0407b6c2a0f2d7e76b7a9 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Fri, 24 Nov 2023 10:18:49 +0000 Subject: [PATCH 023/102] wip --- packages/bbui/src/Form/Core/TextField.svelte | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/bbui/src/Form/Core/TextField.svelte b/packages/bbui/src/Form/Core/TextField.svelte index f29cb379a2..008007256a 100644 --- a/packages/bbui/src/Form/Core/TextField.svelte +++ b/packages/bbui/src/Form/Core/TextField.svelte @@ -121,7 +121,15 @@ width: 100%; } - input:focus:hover::placeholder { - color: var(--grey-8) !important; + input::placeholder { + color: var(--grey-7); + } + + input:hover::placeholder { + color: var(--grey-7) !important; + } + + input:focus::placeholder { + color: var(--grey-7) !important; } From 6674bf139b08375d00f75c2380bf64afaaa8bf79 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 27 Nov 2023 17:12:13 +0000 Subject: [PATCH 024/102] Expand chart documentation, fix HPA definition. --- charts/budibase/README.md | 194 +++++- charts/budibase/README.md.gotmpl | 92 +++ .../budibase/templates/app-service-hpa.yaml | 32 + charts/budibase/templates/hpa.yaml | 28 - .../budibase/templates/proxy-service-hpa.yaml | 32 + .../templates/worker-service-hpa.yaml | 32 + charts/budibase/values.yaml | 598 ++++++++---------- 7 files changed, 630 insertions(+), 378 deletions(-) create mode 100644 charts/budibase/README.md.gotmpl create mode 100644 charts/budibase/templates/app-service-hpa.yaml delete mode 100644 charts/budibase/templates/hpa.yaml create mode 100644 charts/budibase/templates/proxy-service-hpa.yaml create mode 100644 charts/budibase/templates/worker-service-hpa.yaml diff --git a/charts/budibase/README.md b/charts/budibase/README.md index efa78ba75c..961776b977 100644 --- a/charts/budibase/README.md +++ b/charts/budibase/README.md @@ -1,39 +1,193 @@ -# Budibase +# budibase -[Budibase](https://budibase.com/) Budibase is an open source low-code platform, helping thousands of teams build apps for their workplace in minutes. - -## TL;DR; -```console -$ cd chart -$ helm install budibase . -``` - -## Introduction - -This chart bootstraps a [Budibase](https://budibase.com/) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. +Budibase is an open source low-code platform, helping thousands of teams build apps for their workplace in minutes. ## Prerequisites -- helm v3 or above +- `helm` v3 or above - Kubernetes 1.4+ -- PV provisioner support in the underlying infrastructure (with persistence storage enabled) +- A storage controller (for `PersistentVolume` creation, if you want to ensure your data does not get lost when pods restart) ## Installing the Chart -To install the chart with the release name `budi-release`: +To install the chart from our repository: ```console -$ helm install budi-release . +$ helm repo add budibase https://budibase.github.io/budibase/ +$ helm repo update +$ helm install --create-namespace --namespace budibase budibase budibase/budibase ``` -The command deploys Budibase on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. +To install the chart from this repo: -> **Tip**: List all releases using `helm list` +```console +$ git clone git@github.com:budibase/budibase.git +$ cd budibase/charts/budibase +$ helm install --create-namespace --namespace budibase budibase . +``` + +## Example minimal configuration + +Here's an example `values.yaml` that would get a Budibase instance running in a home +cluster using an nginx ingress controller and NFS as cluster storage (basically one of our +staff's homelabs). + +
+ +```yaml +ingress: + enabled: true + annotations: + nginx.ingress.kubernetes.io/client-max-body-size: 150M + nginx.ingress.kubernetes.io/proxy-body-size: 50m + className: "nginx" + hosts: + - host: budibase.local # set this to whatever DNS name you'd use + paths: + - backend: + service: + name: proxy-service + port: + number: 10000 + path: / + pathType: Prefix + +couchdb: + persistentVolume: + enabled: true + storageClass: "nfs-client" + adminPassword: admin + + image: + repository: samwho/test + tag: latest + pullPolicy: Always + + objectStore: + storageClass: "nfs-client" + redis: + storageClass: "nfs-client" +``` + +If you wanted to use this when bringing up Budibase in your own cluster, you could save it +to your hard disk and run the following: + +```console +$ helm install --create-namespace --namespace budibase budibase . -f values.yaml +``` + +
+ +## Configuration reference + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Sets the affinity for all pods created by this chart. Should not ordinarily need to be changed. See for more information on affinity. | +| awsAlbIngress.certificateArn | string | `""` | If you're wanting to use HTTPS, you'll need to create an ACM certificate and specify the ARN here. | +| awsAlbIngress.enabled | bool | `false` | Whether to create an ALB Ingress resource pointing to the Budibase proxy. Requires the AWS ALB Ingress Controller. | +| couchdb.clusterSize | int | `1` | The number of replicas to run in the CouchDB cluster. We set this to 1 by default to make things simpler, but you can set it to 3 if you need a high-availability CouchDB cluster. | +| couchdb.couchdbConfig.couchdb.uuid | string | `"budibase-couchdb"` | Unique identifier for this CouchDB server instance. You shouldn't need to change this. | +| couchdb.image | object | `{}` | We use a custom CouchDB image for running Budibase and we don't support using any other CouchDB image. You shouldn't change this, and if you do we can't guarantee that Budibase will work. | +| globals.apiEncryptionKey | string | `""` | Used for encrypting API keys and environment variables when stored in the database. You don't need to set this if `createSecrets` is true. | +| globals.appVersion | string | `""` | The version of Budibase to deploy. Defaults to what's specified by {{ .Chart.AppVersion }}. Ends up being used as the image version tag for the apps, proxy, and worker images. | +| globals.automationMaxIterations | string | `"200"` | The maximum number of iterations allows for an automation loop step. You can read more about looping here: . | +| globals.budibaseEnv | string | `"PRODUCTION"` | Sets the environment variable BUDIBASE_ENVIRONMENT for the apps and worker pods. Should not ordinarily need to be changed. | +| globals.cookieDomain | string | `""` | Sets the domain attribute of the cookie that Budibase uses to store session information. See for details on why you might want to set this. | +| globals.createSecrets | bool | `true` | Create an internal API key, JWT secret, object store access key and secret, and store them in a Kubernetes `Secret`. | +| globals.enableAnalytics | string | `"1"` | Whether to enable analytics or not. You can read more about our analytics here: . | +| globals.google | object | `{"clientId":"","secret":""}` | Google OAuth settings. These can also be set in the Budibase UI, see for details. | +| globals.google.clientId | string | `""` | Client ID of your Google OAuth app. | +| globals.google.secret | string | `""` | Client secret of your Google OAuth app. | +| globals.httpMigrations | string | `"0"` | Whether or not to enable doing data migrations over the HTTP API. If this is set to "0", migrations are run on startup. You shouldn't ordinarily need to change this. | +| globals.internalApiKey | string | `""` | API key used for internal Budibase API calls. You don't need to set this if `createSecrets` is true. | +| globals.internalApiKeyFallback | string | `""` | A fallback value for `internalApiKey`. If you're rotating your encryption key, you can set this to the old value for the duration of the rotation. | +| globals.jwtSecret | string | `""` | Secret used for signing JWTs. You don't need to set this if `createSecrets` is true. | +| globals.jwtSecretFallback | string | `""` | A fallback value for `jwtSecret`. If you're rotating your JWT secret, you can set this to the old value for the duration of the rotation. | +| globals.multiTenancy | string | `"0"` | Whether to enable the multi-tenancy feature or not. "0" means an installation can only have one tenant. "1" allows multiple tenants. | +| globals.platformUrl | string | `""` | Set the `platformUrl` binding. You can also do this in Settings > Organisation if you are self-hosting. | +| globals.smtp.enabled | bool | `false` | Whether to enable SMTP or not. | +| globals.smtp.from | string | `""` | The email address to use in the "From:" field of emails sent by Budibase. | +| globals.smtp.host | string | `""` | The hostname of your SMTP server. | +| globals.smtp.password | string | `""` | The password to use when authenticating with your SMTP server. | +| globals.smtp.port | string | `"587"` | The port of your SMTP server. | +| globals.smtp.user | string | `""` | The username to use when authenticating with your SMTP server. | +| globals.tenantFeatureFlags | string | `"*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR"` | Sets what feature flags are enabled and for which tenants. Should not ordinarily need to be changed. | +| imagePullSecrets | list | `[]` | Passed to all pods created by this chart. Should not ordinarily need to be changed. | +| ingress.className | string | `""` | What ingress class to use. | +| ingress.enabled | bool | `true` | Whether to create an Ingress resource pointing to the Budibase proxy. | +| ingress.hosts | list | `[]` | Standard hosts block for the Ingress resource. Defaults to pointing to the Budibase proxy. | +| nameOverride | string | `""` | Override the name of the deploymen. Defaults to {{ .Chart.Name }}. | +| service.port | int | `10000` | Port to expose on the service. | +| service.type | string | `"ClusterIP"` | Service type for the service that points to the main Budibase proxy pod. | +| serviceAccount.annotations | object | `{}` | Annotations to add to the service account | +| serviceAccount.create | bool | `true` | Specifies whether a service account should be created | +| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | +| services.apps.autoscaling.enabled | bool | `false` | Whether to enable horizontal pod autoscaling for the apps service. | +| services.apps.autoscaling.maxReplicas | int | `10` | | +| services.apps.autoscaling.minReplicas | int | `1` | | +| services.apps.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for the apps service. Note that for autoscaling to work, you will need to have metrics-server configured, and resources set for the apps pods. | +| services.apps.httpLogging | int | `1` | Whether or not to log HTTP requests to the apps service. | +| services.apps.livenessProbe | object | HTTP health checks. | Liveness probe configuration for apps pods. You shouldn't need to change this, but if you want to you can find more information here: | +| services.apps.logLevel | string | `"info"` | The log level for the apps service. | +| services.apps.readinessProbe | object | HTTP health checks. | Readiness probe configuration for apps pods. You shouldn't need to change this, but if you want to you can find more information here: | +| services.apps.replicaCount | int | `1` | The number of apps replicas to run. | +| services.apps.resources | object | `{}` | The resources to use for apps pods. See for more information on how to set these. | +| services.apps.startupProbe | object | HTTP health checks. | Startup probe configuration for apps pods. You shouldn't need to change this, but if you want to you can find more information here: | +| services.couchdb.backup.enabled | bool | `false` | Whether or not to enable periodic CouchDB backups. This works by replicating to another CouchDB instance. | +| services.couchdb.backup.interval | string | `""` | Backup interval in seconds | +| services.couchdb.backup.resources | object | `{}` | The resources to use for CouchDB backup pods. See for more information on how to set these. | +| services.couchdb.backup.target | string | `""` | Target couchDB instance to back up to, either a hostname or an IP address. | +| services.couchdb.enabled | bool | `true` | Whether or not to spin up a CouchDB instance in your cluster. True by default, and the configuration for the CouchDB instance is under the `couchdb` key at the root of this file. You can see what options are available to you by looking at the official CouchDB Helm chart: . | +| services.couchdb.port | int | `5984` | | +| services.dns | string | `"cluster.local"` | The DNS suffix to use for service discovery. You only need to change this if you've configured your cluster to use a different DNS suffix. | +| services.objectStore.accessKey | string | `""` | AWS_ACCESS_KEY if using S3 | +| services.objectStore.browser | bool | `true` | Whether to enable the Minio web console or not. If you're exposing Minio to the Internet (via a custom Ingress record, for example), you should set this to false. If you're only exposing Minio to your cluster, you can leave this as true. | +| services.objectStore.cloudfront.cdn | string | `""` | Set the url of a distribution to enable cloudfront. | +| services.objectStore.cloudfront.privateKey64 | string | `""` | Base64 encoded private key for the above public key. | +| services.objectStore.cloudfront.publicKeyId | string | `""` | ID of public key stored in cloudfront. | +| services.objectStore.minio | bool | `true` | Set to false if using another object store, such as S3. You will need to set `services.objectStore.url` to point to your bucket if you do this. | +| services.objectStore.region | string | `""` | AWS_REGION if using S3 | +| services.objectStore.resources | object | `{}` | The resources to use for Minio pods. See for more information on how to set these. | +| services.objectStore.secretKey | string | `""` | AWS_SECRET_ACCESS_KEY if using S3 | +| services.objectStore.storage | string | `"100Mi"` | How much storage to give Minio in its PersistentVolumeClaim. | +| services.objectStore.storageClass | string | `""` | If defined, storageClassName: If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. | +| services.objectStore.url | string | `"http://minio-service:9000"` | URL to use for object storage. Only change this if you're using an external object store, such as S3. Remember to set `minio: false` if you do this. | +| services.proxy.autoscaling.enabled | bool | `false` | Whether to enable horizontal pod autoscaling for the proxy service. | +| services.proxy.autoscaling.maxReplicas | int | `10` | | +| services.proxy.autoscaling.minReplicas | int | `1` | | +| services.proxy.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for the proxy service. Note that for autoscaling to work, you will need to have metrics-server configured, and resources set for the proxy pods. | +| services.proxy.livenessProbe | object | HTTP health checks. | Liveness probe configuration for proxy pods. You shouldn't need to change this, but if you want to you can find more information here: | +| services.proxy.readinessProbe | object | HTTP health checks. | Readiness probe configuration for proxy pods. You shouldn't need to change this, but if you want to you can find more information here: | +| services.proxy.replicaCount | int | `1` | The number of proxy replicas to run. | +| services.proxy.resources | object | `{}` | The resources to use for proxy pods. See for more information on how to set these. | +| services.proxy.startupProbe | object | HTTP health checks. | Startup probe configuration for proxy pods. You shouldn't need to change this, but if you want to you can find more information here: | +| services.redis.enabled | bool | `true` | Whether or not to deploy a Redis pod into your cluster. | +| services.redis.password | string | `"budibase"` | The password to use when connecting to Redis. It's recommended that you change this from the default if you're running Redis in-cluster. | +| services.redis.port | int | `6379` | Port to expose Redis on. | +| services.redis.resources | object | `{}` | The resources to use for Redis pods. See for more information on how to set these. | +| services.redis.storage | string | `"100Mi"` | How much persistent storage to allocate to Redis. | +| services.redis.storageClass | string | `""` | If defined, storageClassName: If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. | +| services.redis.url | string | `""` | If you choose to run Redis externally to this chart, you can specify the connection details here. | +| services.worker.autoscaling.enabled | bool | `false` | Whether to enable horizontal pod autoscaling for the worker service. | +| services.worker.autoscaling.maxReplicas | int | `10` | | +| services.worker.autoscaling.minReplicas | int | `1` | | +| services.worker.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for the worker service. Note that for autoscaling to work, you will need to have metrics-server configured, and resources set for the worker pods. | +| services.worker.httpLogging | int | `1` | Whether or not to log HTTP requests to the worker service. | +| services.worker.livenessProbe | object | HTTP health checks. | Liveness probe configuration for worker pods. You shouldn't need to change this, but if you want to you can find more information here: | +| services.worker.logLevel | string | `"info"` | The log level for the worker service. | +| services.worker.readinessProbe | object | HTTP health checks. | Readiness probe configuration for worker pods. You shouldn't need to change this, but if you want to you can find more information here: | +| services.worker.replicaCount | int | `1` | The number of worker replicas to run. | +| services.worker.resources | object | `{}` | The resources to use for worker pods. See for more information on how to set these. | +| services.worker.startupProbe | object | HTTP health checks. | Startup probe configuration for worker pods. You shouldn't need to change this, but if you want to you can find more information here: | +| tolerations | list | `[]` | Sets the tolerations for all pods created by this chart. Should not ordinarily need to be changed. See for more information on tolerations. | ## Uninstalling the Chart -To uninstall/delete the `my-release` deployment: +To uninstall the chart, assuming you named the release `budibase` (both commands in the installation section do so): ```console -$ helm delete my-release +$ helm uninstall --namespace budibase budibase ``` + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.11.3](https://github.com/norwoodj/helm-docs/releases/v1.11.3) diff --git a/charts/budibase/README.md.gotmpl b/charts/budibase/README.md.gotmpl new file mode 100644 index 0000000000..eacd832e74 --- /dev/null +++ b/charts/budibase/README.md.gotmpl @@ -0,0 +1,92 @@ +{{ template "chart.header" . }} +{{ template "chart.description" . }} + +## Prerequisites + +- `helm` v3 or above +- Kubernetes 1.4+ +- A storage controller (for `PersistentVolume` creation, if you want to ensure your data does not get lost when pods restart) + +## Installing the Chart + +To install the chart from our repository: + +```console +$ helm repo add budibase https://budibase.github.io/budibase/ +$ helm repo update +$ helm install --create-namespace --namespace budibase budibase budibase/budibase +``` + +To install the chart from this repo: + +```console +$ git clone git@github.com:budibase/budibase.git +$ cd budibase/charts/budibase +$ helm install --create-namespace --namespace budibase budibase . +``` + +## Example minimal configuration + +Here's an example `values.yaml` that would get a Budibase instance running in a home +cluster using an nginx ingress controller and NFS as cluster storage (basically one of our +staff's homelabs). + +
+ +```yaml +ingress: + enabled: true + annotations: + nginx.ingress.kubernetes.io/client-max-body-size: 150M + nginx.ingress.kubernetes.io/proxy-body-size: 50m + className: "nginx" + hosts: + - host: budibase.local # set this to whatever DNS name you'd use + paths: + - backend: + service: + name: proxy-service + port: + number: 10000 + path: / + pathType: Prefix + +couchdb: + persistentVolume: + enabled: true + storageClass: "nfs-client" + adminPassword: admin + + image: + repository: samwho/test + tag: latest + pullPolicy: Always + + objectStore: + storageClass: "nfs-client" + redis: + storageClass: "nfs-client" +``` + +If you wanted to use this when bringing up Budibase in your own cluster, you could save it +to your hard disk and run the following: + +```console +$ helm install --create-namespace --namespace budibase budibase . -f values.yaml +``` + +
+ +## Configuration reference + +{{ template "chart.valuesTable" . }} + +## Uninstalling the Chart + +To uninstall the chart, assuming you named the release `budibase` (both commands in the installation section do so): + +```console +$ helm uninstall --namespace budibase budibase +``` + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/budibase/templates/app-service-hpa.yaml b/charts/budibase/templates/app-service-hpa.yaml new file mode 100644 index 0000000000..e819ecb9e3 --- /dev/null +++ b/charts/budibase/templates/app-service-hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.services.apps.autoscaling.enabled }} +apiVersion: {{ ternary "autoscaling/v2" "autoscaling/v2beta2" (.Capabilities.APIVersions.Has "autoscaling/v2") }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "budibase.fullname" . }}-apps + labels: + {{- include "budibase.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: app-service + minReplicas: {{ .Values.services.apps.autoscaling.minReplicas }} + maxReplicas: {{ .Values.services.apps.autoscaling.maxReplicas }} + metrics: + {{- if .Values.services.apps.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.services.apps.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.services.apps.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.services.apps.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/budibase/templates/hpa.yaml b/charts/budibase/templates/hpa.yaml deleted file mode 100644 index 2f901b4664..0000000000 --- a/charts/budibase/templates/hpa.yaml +++ /dev/null @@ -1,28 +0,0 @@ -{{- if .Values.autoscaling.enabled }} -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "budibase.fullname" . }} - labels: - {{- include "budibase.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "budibase.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/charts/budibase/templates/proxy-service-hpa.yaml b/charts/budibase/templates/proxy-service-hpa.yaml new file mode 100644 index 0000000000..b6c6022008 --- /dev/null +++ b/charts/budibase/templates/proxy-service-hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.services.proxy.autoscaling.enabled }} +apiVersion: {{ ternary "autoscaling/v2" "autoscaling/v2beta2" (.Capabilities.APIVersions.Has "autoscaling/v2") }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "budibase.fullname" . }}-proxy + labels: + {{- include "budibase.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: proxy-service + minReplicas: {{ .Values.services.proxy.autoscaling.minReplicas }} + maxReplicas: {{ .Values.services.proxy.autoscaling.maxReplicas }} + metrics: + {{- if .Values.services.proxy.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.services.proxy.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.services.proxy.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.services.proxy.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/budibase/templates/worker-service-hpa.yaml b/charts/budibase/templates/worker-service-hpa.yaml new file mode 100644 index 0000000000..a04cc259a0 --- /dev/null +++ b/charts/budibase/templates/worker-service-hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.services.worker.autoscaling.enabled }} +apiVersion: {{ ternary "autoscaling/v2" "autoscaling/v2beta2" (.Capabilities.APIVersions.Has "autoscaling/v2") }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "budibase.fullname" . }}-worker + labels: + {{- include "budibase.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: worker-service + minReplicas: {{ .Values.services.worker.autoscaling.minReplicas }} + maxReplicas: {{ .Values.services.worker.autoscaling.maxReplicas }} + metrics: + {{- if .Values.services.worker.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.services.worker.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.services.worker.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.services.worker.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/budibase/values.yaml b/charts/budibase/values.yaml index 5df6e980f0..0b693a4df3 100644 --- a/charts/budibase/values.yaml +++ b/charts/budibase/values.yaml @@ -1,47 +1,32 @@ -# Default values for budibase. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - +# -- Passed to all pods created by this chart. Should not ordinarily need to be changed. imagePullSecrets: [] +# -- Override the name of the deploymen. Defaults to {{ .Chart.Name }}. nameOverride: "" -# fullnameOverride: "" serviceAccount: - # Specifies whether a service account should be created + # -- Specifies whether a service account should be created create: true - # Annotations to add to the service account + # -- Annotations to add to the service account annotations: {} - # The name of the service account to use. + # -- The name of the service account to use. # If not set and create is true, a name is generated using the fullname template name: "" -podAnnotations: {} - -podSecurityContext: - {} - # fsGroup: 2000 - -securityContext: - {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - service: + # -- Service type for the service that points to the main Budibase proxy pod. type: ClusterIP + # -- Port to expose on the service. port: 10000 ingress: + # -- Whether to create an Ingress resource pointing to the Budibase proxy. enabled: true + # -- What ingress class to use. className: "" - annotations: - # nginx.ingress.kubernetes.io/client-max-body-size: 150M - # nginx.ingress.kubernetes.io/proxy-body-size: 50m + # -- Standard hosts block for the Ingress resource. Defaults to pointing to the Budibase proxy. hosts: - - host: # change if using custom domain + # @ignore + - host: paths: - path: / pathType: Prefix @@ -52,473 +37,426 @@ ingress: number: 10000 awsAlbIngress: + # -- Whether to create an ALB Ingress resource pointing to the Budibase proxy. Requires the AWS ALB Ingress Controller. enabled: false + # -- If you're wanting to use HTTPS, you'll need to create an ACM certificate and specify the ARN here. certificateArn: "" -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -nodeSelector: {} - +# -- Sets the tolerations for all pods created by this chart. Should not ordinarily need to be changed. +# See for more information +# on tolerations. tolerations: [] +# -- Sets the affinity for all pods created by this chart. Should not ordinarily +# need to be changed. See +# +# for more information on affinity. affinity: {} globals: - appVersion: "" # Use as an override to .Chart.AppVersion + # -- The version of Budibase to deploy. Defaults to what's specified by {{ .Chart.AppVersion }}. + # Ends up being used as the image version tag for the apps, proxy, and worker images. + appVersion: "" + # -- Sets the environment variable BUDIBASE_ENVIRONMENT for the apps and worker pods. Should not + # ordinarily need to be changed. budibaseEnv: PRODUCTION + # -- Sets what feature flags are enabled and for which tenants. Should not ordinarily need to be + # changed. tenantFeatureFlags: "*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR" + # -- Whether to enable analytics or not. You can read more about our analytics here: + # . enableAnalytics: "1" + # @ignore (only used if enableAnalytics is set to 1) posthogToken: "phc_bIjZL7oh2GEUd2vqvTBH8WvrX0fWTFQMs6H5KQxiUxU" - selfHosted: "1" # set to 0 for budibase cloud environment, set to 1 for self-hosted setup - multiTenancy: "0" # set to 0 to disable multiple orgs, set to 1 to enable multiple orgs - offlineMode: "0" # set to 1 to enable offline mode + # @ignore (should not normally need to be changed, we only set this to "0" + # when deploying to our Cloud environment) + selfHosted: "1" + # -- Whether to enable the multi-tenancy feature or not. "0" means an installation can only + # have one tenant. "1" allows multiple tenants. + multiTenancy: "0" + # @ignore (only currently used to determine whether to fetch licenses offline or not, should + # not normally need to be changed) + offlineMode: "0" + # @ignore (only needs to be set in our cloud environment) accountPortalUrl: "" + # @ignore (only needs to be set in our cloud environment) accountPortalApiKey: "" + # -- Sets the domain attribute of the cookie that Budibase uses to store session information. + # See + # for details on why you might want to set this. cookieDomain: "" + # -- Set the `platformUrl` binding. You can also do this in Settings > Organisation if you are + # self-hosting. platformUrl: "" + # -- Whether or not to enable doing data migrations over the HTTP API. If this is set to "0", + # migrations are run on startup. You shouldn't ordinarily need to change this. httpMigrations: "0" + # -- Google OAuth settings. These can also be set in the Budibase UI, see + # for details. google: + # -- Client ID of your Google OAuth app. clientId: "" + # -- Client secret of your Google OAuth app. secret: "" + # -- The maximum number of iterations allows for an automation loop step. You can read more about + # looping here: . automationMaxIterations: "200" - createSecrets: true # creates an internal API key, JWT secrets and redis password for you + # -- Create an internal API key, JWT secret, object store access key and + # secret, and store them in a Kubernetes `Secret`. + createSecrets: true - # if createSecrets is set to false, you can hard-code your secrets here + # -- Used for encrypting API keys and environment variables when stored in the database. + # You don't need to set this if `createSecrets` is true. apiEncryptionKey: "" + # -- API key used for internal Budibase API calls. You don't need to set this + # if `createSecrets` is true. internalApiKey: "" + # -- Secret used for signing JWTs. You don't need to set this if `createSecrets` is true. jwtSecret: "" - cdnUrl: "" - # fallback values used during live rotation + + # -- A fallback value for `internalApiKey`. If you're rotating your encryption key, you can + # set this to the old value for the duration of the rotation. internalApiKeyFallback: "" + # -- A fallback value for `jwtSecret`. If you're rotating your JWT secret, you can set this + # to the old value for the duration of the rotation. jwtSecretFallback: "" smtp: + # -- Whether to enable SMTP or not. enabled: false - -# globalAgentHttpProxy: -# globalAgentHttpsProxy: -# globalAgentNoProxy: + # -- The hostname of your SMTP server. + host: "" + # -- The port of your SMTP server. + port: "587" + # -- The email address to use in the "From:" field of emails sent by Budibase. + from: "" + # -- The username to use when authenticating with your SMTP server. + user: "" + # -- The password to use when authenticating with your SMTP server. + password: "" services: + # -- The DNS suffix to use for service discovery. You only need to change this + # if you've configured your cluster to use a different DNS suffix. dns: cluster.local - # tlsRejectUnauthorized: 0 proxy: + # @ignore (you shouldn't need to change this) port: 10000 + # -- The number of proxy replicas to run. replicaCount: 1 + # @ignore (you should never need to change this) upstreams: apps: "http://app-service.{{ .Release.Namespace }}.svc.{{ .Values.services.dns }}:{{ .Values.services.apps.port }}" worker: "http://worker-service.{{ .Release.Namespace }}.svc.{{ .Values.services.dns }}:{{ .Values.services.worker.port }}" minio: "http://minio-service.{{ .Release.Namespace }}.svc.{{ .Values.services.dns }}:{{ .Values.services.objectStore.port }}" couchdb: "http://{{ .Release.Name }}-svc-couchdb:{{ .Values.services.couchdb.port }}" + # -- The resources to use for proxy pods. See + # + # for more information on how to set these. resources: {} + # -- Startup probe configuration for proxy pods. You shouldn't need to + # change this, but if you want to you can find more information here: + # + # @default -- HTTP health checks. startupProbe: + # @ignore httpGet: path: /health port: 10000 scheme: HTTP + # @ignore failureThreshold: 30 + # @ignore periodSeconds: 3 + # -- Readiness probe configuration for proxy pods. You shouldn't need to + # change this, but if you want to you can find more information here: + # + # @default -- HTTP health checks. readinessProbe: + # @ignore httpGet: path: /health port: 10000 scheme: HTTP + # @ignore periodSeconds: 3 + # @ignore failureThreshold: 1 + # -- Liveness probe configuration for proxy pods. You shouldn't need to + # change this, but if you want to you can find more information here: + # + # @default -- HTTP health checks. livenessProbe: + # @ignore httpGet: path: /health port: 10000 scheme: HTTP + # @ignore failureThreshold: 3 + # @ignore periodSeconds: 5 + autoscaling: + # -- Whether to enable horizontal pod autoscaling for the proxy service. + enabled: false + minReplicas: 1 + maxReplicas: 10 + # -- Target CPU utilization percentage for the proxy service. Note that + # for autoscaling to work, you will need to have metrics-server + # configured, and resources set for the proxy pods. + targetCPUUtilizationPercentage: 80 apps: + # @ignore (you shouldn't need to change this) port: 4002 + # -- The number of apps replicas to run. replicaCount: 1 + # -- The log level for the apps service. logLevel: info + # -- Whether or not to log HTTP requests to the apps service. httpLogging: 1 + # -- The resources to use for apps pods. See + # + # for more information on how to set these. resources: {} + # -- Startup probe configuration for apps pods. You shouldn't need to + # change this, but if you want to you can find more information here: + # + # @default -- HTTP health checks. startupProbe: + # @ignore httpGet: path: /health port: 4002 scheme: HTTP + # @ignore failureThreshold: 30 + # @ignore periodSeconds: 3 + # -- Readiness probe configuration for apps pods. You shouldn't need to + # change this, but if you want to you can find more information here: + # + # @default -- HTTP health checks. readinessProbe: + # @ignore httpGet: path: /health port: 4002 scheme: HTTP + # @ignore periodSeconds: 3 + # @ignore failureThreshold: 1 + # -- Liveness probe configuration for apps pods. You shouldn't need to + # change this, but if you want to you can find more information here: + # + # @default -- HTTP health checks. livenessProbe: + # @ignore httpGet: path: /health port: 4002 scheme: HTTP + # @ignore failureThreshold: 3 + # @ignore periodSeconds: 5 - # nodeDebug: "" # set the value of NODE_DEBUG - # annotations: - # co.elastic.logs/multiline.type: pattern - # co.elastic.logs/multiline.pattern: '^[[:space:]]' - # co.elastic.logs/multiline.negate: false - # co.elastic.logs/multiline.match: after + autoscaling: + # -- Whether to enable horizontal pod autoscaling for the apps service. + enabled: false + minReplicas: 1 + maxReplicas: 10 + # -- Target CPU utilization percentage for the apps service. Note that for + # autoscaling to work, you will need to have metrics-server configured, + # and resources set for the apps pods. + targetCPUUtilizationPercentage: 80 + worker: + # @ignore (you shouldn't need to change this) port: 4003 + # -- The number of worker replicas to run. replicaCount: 1 + # -- The log level for the worker service. logLevel: info + # -- Whether or not to log HTTP requests to the worker service. httpLogging: 1 + # -- The resources to use for worker pods. See + # + # for more information on how to set these. resources: {} + # -- Startup probe configuration for worker pods. You shouldn't need to + # change this, but if you want to you can find more information here: + # + # @default -- HTTP health checks. startupProbe: + # @ignore httpGet: path: /health port: 4003 scheme: HTTP + # @ignore failureThreshold: 30 + # @ignore periodSeconds: 3 + # -- Readiness probe configuration for worker pods. You shouldn't need to + # change this, but if you want to you can find more information here: + # + # @default -- HTTP health checks. readinessProbe: + # @ignore httpGet: path: /health port: 4003 scheme: HTTP + # @ignore periodSeconds: 3 + # @ignore failureThreshold: 1 + # -- Liveness probe configuration for worker pods. You shouldn't need to + # change this, but if you want to you can find more information here: + # + # @default -- HTTP health checks. livenessProbe: + # @ignore httpGet: path: /health port: 4003 scheme: HTTP + # @ignore failureThreshold: 3 + # @ignore periodSeconds: 5 - # annotations: - # co.elastic.logs/multiline.type: pattern - # co.elastic.logs/multiline.pattern: '^[[:space:]]' - # co.elastic.logs/multiline.negate: false - # co.elastic.logs/multiline.match: after + autoscaling: + # -- Whether to enable horizontal pod autoscaling for the worker service. + enabled: false + minReplicas: 1 + maxReplicas: 10 + # -- Target CPU utilization percentage for the worker service. Note that + # for autoscaling to work, you will need to have metrics-server + # configured, and resources set for the worker pods. + targetCPUUtilizationPercentage: 80 couchdb: + # -- Whether or not to spin up a CouchDB instance in your cluster. True by + # default, and the configuration for the CouchDB instance is under the + # `couchdb` key at the root of this file. You can see what options are + # available to you by looking at the official CouchDB Helm chart: + # . enabled: true # url: "" # only change if pointing to existing couch server # user: "" # only change if pointing to existing couch server # password: "" # only change if pointing to existing couch server port: 5984 backup: + # -- Whether or not to enable periodic CouchDB backups. This works by replicating + # to another CouchDB instance. enabled: false - # target couchDB instance to back up to + # -- Target couchDB instance to back up to, either a hostname or an IP address. target: "" - # backup interval in seconds + # -- Backup interval in seconds interval: "" + # -- The resources to use for CouchDB backup pods. See + # + # for more information on how to set these. resources: {} redis: - enabled: true # disable if using external redis + # -- Whether or not to deploy a Redis pod into your cluster. + enabled: true + # -- Port to expose Redis on. port: 6379 + # @ignore (you should leave this as 1, we don't support clustering Redis) replicaCount: 1 - url: "" # only change if pointing to existing redis cluster and enabled: false - password: "budibase" # recommended to override if using built-in redis + # -- If you choose to run Redis externally to this chart, you can specify the + # connection details here. + url: "" + # -- The password to use when connecting to Redis. It's recommended that you change + # this from the default if you're running Redis in-cluster. + password: "budibase" + # -- How much persistent storage to allocate to Redis. storage: 100Mi - ## If defined, storageClassName: - ## If set to "-", storageClassName: "", which disables dynamic provisioning - ## If undefined (the default) or set to null, no storageClassName spec is - ## set, choosing the default provisioner. + # -- If defined, storageClassName: If set to "-", + # storageClassName: "", which disables dynamic provisioning If undefined + # (the default) or set to null, no storageClassName spec is set, choosing + # the default provisioner. storageClass: "" + # -- The resources to use for Redis pods. See + # + # for more information on how to set these. resources: {} objectStore: - # Set to false if using another object store such as S3 + # -- Set to false if using another object store, such as S3. You will need + # to set `services.objectStore.url` to point to your bucket if you do this. minio: true + # -- Whether to enable the Minio web console or not. If you're exposing + # Minio to the Internet (via a custom Ingress record, for example), you + # should set this to false. If you're only exposing Minio to your cluster, + # you can leave this as true. browser: true + # @ignore port: 9000 + # @ignore (you should leave this as 1, we don't support clustering Minio) replicaCount: 1 - accessKey: "" # AWS_ACCESS_KEY if using S3 or existing minio access key - secretKey: "" # AWS_SECRET_ACCESS_KEY if using S3 or existing minio secret - region: "" # AWS_REGION if using S3 or existing minio secret - url: "http://minio-service:9000" # only change if pointing to existing minio cluster or S3 and minio: false + # -- AWS_ACCESS_KEY if using S3 + accessKey: "" + # -- AWS_SECRET_ACCESS_KEY if using S3 + secretKey: "" + # -- AWS_REGION if using S3 + region: "" + # -- URL to use for object storage. Only change this if you're using an + # external object store, such as S3. Remember to set `minio: false` if you + # do this. + url: "http://minio-service:9000" + # -- How much storage to give Minio in its PersistentVolumeClaim. storage: 100Mi - ## If defined, storageClassName: - ## If set to "-", storageClassName: "", which disables dynamic provisioning - ## If undefined (the default) or set to null, no storageClassName spec is - ## set, choosing the default provisioner. + # -- If defined, storageClassName: If set to "-", + # storageClassName: "", which disables dynamic provisioning If undefined + # (the default) or set to null, no storageClassName spec is set, choosing + # the default provisioner. storageClass: "" + # -- The resources to use for Minio pods. See + # + # for more information on how to set these. resources: {} cloudfront: - # Set the url of a distribution to enable cloudfront + # -- Set the url of a distribution to enable cloudfront. cdn: "" - # ID of public key stored in cloudfront + # -- ID of public key stored in cloudfront. publicKeyId: "" - # Base64 encoded private key for the above public key + # -- Base64 encoded private key for the above public key. privateKey64: "" -# Override values in couchDB subchart +# Override values in couchDB subchart. We're only specifying the values we're changing. +# If you want to see all of the available values, see: +# https://github.com/apache/couchdb-helm/tree/couchdb-4.3.0/couchdb couchdb: - # -- the initial number of nodes in the CouchDB cluster. + # -- The number of replicas to run in the CouchDB cluster. We set this to + # 1 by default to make things simpler, but you can set it to 3 if you need + # a high-availability CouchDB cluster. clusterSize: 1 - # -- If allowAdminParty is enabled the cluster will start up without any database - # administrator account; i.e., all users will be granted administrative - # access. Otherwise, the system will look for a Secret called - # -couchdb containing `adminUsername`, `adminPassword` and - # `cookieAuthSecret` keys. See the `createAdminSecret` flag. - # ref: https://kubernetes.io/docs/concepts/configuration/secret/ - allowAdminParty: false - - # -- If createAdminSecret is enabled a Secret called -couchdb will - # be created containing auto-generated credentials. Users who prefer to set - # these values themselves have a couple of options: - # - # 1) The `adminUsername`, `adminPassword`, `adminHash`, and `cookieAuthSecret` - # can be defined directly in the chart's values. Note that all of a chart's - # values are currently stored in plaintext in a ConfigMap in the tiller - # namespace. - # - # 2) This flag can be disabled and a Secret with the required keys can be - # created ahead of time. - createAdminSecret: true - - adminUsername: admin - # adminPassword: this_is_not_secure - # adminHash: -pbkdf2-this_is_not_necessarily_secure_either - # cookieAuthSecret: neither_is_this - - ## When enabled, will deploy a networkpolicy that allows CouchDB pods to - ## communicate with each other for clustering and ingress on port 5984 - networkPolicy: - enabled: true - - ## Use an alternate scheduler, e.g. "stork". - ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ - ## - # schedulerName: - - # Use a service account - serviceAccount: - enabled: true - create: true - # name: - # imagePullSecrets: - # - name: myimagepullsecret - - # -- The storage volume used by each Pod in the StatefulSet. If a - # persistentVolume is not enabled, the Pods will use `emptyDir` ephemeral - # local storage. Setting the storageClass attribute to "-" disables dynamic - # provisioning of Persistent Volumes; leaving it unset will invoke the default - # provisioner. - persistentVolume: - enabled: false - # NOTE: the number of existing claims must match the cluster size - existingClaims: [] - annotations: {} - accessModes: - - ReadWriteOnce - size: 10Gi - # storageClass: "-" - - ## The CouchDB image + # -- We use a custom CouchDB image for running Budibase and we don't support + # using any other CouchDB image. You shouldn't change this, and if you do we + # can't guarantee that Budibase will work. image: + # @ignore repository: budibase/couchdb + # @ignore tag: v3.2.1 - pullPolicy: IfNotPresent + # @ignore + pullPolicy: Always + # @ignore # This should remain false. We ship Clouseau ourselves as part of the # budibase/couchdb image, and it's not possible to disable it because it's a # core part of the Budibase experience. enableSearch: false - initImage: - repository: busybox - tag: latest - pullPolicy: Always - - ## CouchDB is happy to spin up cluster nodes in parallel, but if you encounter - ## problems you can try setting podManagementPolicy to the StatefulSet default - ## `OrderedReady` - podManagementPolicy: Parallel - - ## To better tolerate Node failures, we can prevent Kubernetes scheduler from - ## assigning more than one Pod of CouchDB StatefulSet per Node using podAntiAffinity. - affinity: - {} - # podAntiAffinity: - # requiredDuringSchedulingIgnoredDuringExecution: - # - labelSelector: - # matchExpressions: - # - key: "app" - # operator: In - # values: - # - couchdb - # topologyKey: "kubernetes.io/hostname" - - ## To control how Pods are spread across your cluster among failure-domains such as regions, - ## zones, nodes, and other user-defined topology domains use topologySpreadConstraints. - topologySpreadConstraints: - {} - # topologySpreadConstraints: - # - maxSkew: 1 - # topologyKey: "topology.kubernetes.io/zone" - # whenUnsatisfiable: ScheduleAnyway - # labelSelector: - # matchLabels: - # app: couchdb - - ## Optional pod labels - labels: {} - - ## Optional pod annotations - annotations: {} - - ## Optional tolerations - tolerations: [] - - ## A StatefulSet requires a headless Service to establish the stable network - ## identities of the Pods, and that Service is created automatically by this - ## chart without any additional configuration. The Service block below refers - ## to a second Service that governs how clients connect to the CouchDB cluster. - service: - annotations: {} - enabled: true - type: ClusterIP - externalPort: 5984 - targetPort: 5984 - labels: {} - - ## An Ingress resource can provide name-based virtual hosting and TLS - ## termination among other things for CouchDB deployments which are accessed - ## from outside the Kubernetes cluster. - ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/ - ingress: - enabled: false - # className: nginx - hosts: - - chart-example.local - path: / - annotations: - {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - tls: - # Secrets must be manually created in the namespace. - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - - ## Optional resource requests and limits for the CouchDB container - ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ - resources: - {} - # requests: - # cpu: 100m - # memory: 128Mi - # limits: - # cpu: 56 - # memory: 256Gi - - ## Optional resource requests and limits for the CouchDB init container - ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ - initResources: - {} - # requests: - # cpu: 100m - # memory: 128Mi - # limits: - # cpu: 500m - # memory: 128Mi - - # -- erlangFlags is a map that is passed to the Erlang VM as flags using the - # ERL_FLAGS env. The `name` flag is required to establish connectivity - # between cluster nodes. - # ref: http://erlang.org/doc/man/erl.html#init_flags - erlangFlags: - name: couchdb - # Older versions of the official CouchDB image (anything prior to 3.2.1) - # do not act on the COUCHDB_ERLANG_COOKIE environment variable, so if you - # want to cluster these deployments it's necessary to pass in a cookie here - # setcookie: make-something-up - - # -- couchdbConfig will override default CouchDB configuration settings. - # The contents of this map are reformatted into a .ini file laid down - # by a ConfigMap object. - # ref: http://docs.couchdb.org/en/latest/config/index.html couchdbConfig: couchdb: - uuid: budibase-couchdb # Unique identifier for this CouchDB server instance - # cluster: - # q: 8 # Create 8 shards for each database - chttpd: - bind_address: any - # chttpd.require_valid_user disables all the anonymous requests to the port - # 5984 when is set to true. - require_valid_user: false - # required to use Fauxton if chttpd.require_valid_user is set to true - # httpd: - # WWW-Authenticate: "Basic realm=\"administrator\"" - - # Kubernetes local cluster domain. - # This is used to generate FQDNs for peers when joining the CouchDB cluster. - dns: - clusterDomainSuffix: cluster.local - - ## Configure liveness and readiness probe values - ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes - livenessProbe: - enabled: true - failureThreshold: 3 - initialDelaySeconds: 0 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - readinessProbe: - enabled: true - failureThreshold: 3 - initialDelaySeconds: 0 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - - # Control an optional pod disruption budget - podDisruptionBudget: - # toggle creation of pod disruption budget, disabled by default - enabled: false - # minAvailable: 1 - maxUnavailable: 1 - - # CouchDB 3.2.0 adds in a metrics endpoint on the path `/_node/_local/_prometheus`. - # Optionally, a standalone, unauthenticated port can be exposed for these metrics. - prometheusPort: - enabled: false - bind_address: "0.0.0.0" - port: 17986 - - # Configure arbitrary sidecar containers for CouchDB pods created by the - # StatefulSet - sidecars: - {} - # - name: foo - # image: "busybox" - # imagePullPolicy: IfNotPresent - # resources: - # requests: - # cpu: "0.1" - # memory: 10Mi - # command: ['echo "foo";'] - # volumeMounts: - # - name: database-storage - # mountPath: /opt/couchdb/data/ - - # Placement manager to annotate each document in the nodes DB with "zone" attribute - # recording the zone where node has been scheduled - # Ref: https://docs.couchdb.org/en/stable/cluster/sharding.html#specifying-database-placement - placementConfig: - enabled: false - image: - repository: caligrafix/couchdb-autoscaler-placement-manager - tag: 0.1.0 + # -- Unique identifier for this CouchDB server instance. You shouldn't need + # to change this. + uuid: budibase-couchdb From 7e1036251519bc97f8fdd8e8330598650af450b6 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 27 Nov 2023 17:14:52 +0000 Subject: [PATCH 025/102] More README tweaks. --- charts/budibase/README.md | 9 ++++++++- charts/budibase/README.md.gotmpl | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/charts/budibase/README.md b/charts/budibase/README.md index 961776b977..9164ace2ce 100644 --- a/charts/budibase/README.md +++ b/charts/budibase/README.md @@ -6,7 +6,14 @@ Budibase is an open source low-code platform, helping thousands of teams build a - `helm` v3 or above - Kubernetes 1.4+ -- A storage controller (for `PersistentVolume` creation, if you want to ensure your data does not get lost when pods restart) +- A storage controller (if you want to use persistent storage) +- An ingress controller (if you want to define an `Ingress` resource) + +## Chart dependencies + +This chart depends on the official Apache CouchDB chart. You can see its +documentation here: +. ## Installing the Chart diff --git a/charts/budibase/README.md.gotmpl b/charts/budibase/README.md.gotmpl index eacd832e74..23c2399a15 100644 --- a/charts/budibase/README.md.gotmpl +++ b/charts/budibase/README.md.gotmpl @@ -5,7 +5,14 @@ - `helm` v3 or above - Kubernetes 1.4+ -- A storage controller (for `PersistentVolume` creation, if you want to ensure your data does not get lost when pods restart) +- A storage controller (if you want to use persistent storage) +- An ingress controller (if you want to define an `Ingress` resource) + +## Chart dependencies + +This chart depends on the official Apache CouchDB chart. You can see its +documentation here: +. ## Installing the Chart From f917e5624560fa0b370a2a7ab12a086e820d9708 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 27 Nov 2023 17:16:06 +0000 Subject: [PATCH 026/102] Note metrics-server dependency. --- charts/budibase/README.md | 1 + charts/budibase/README.md.gotmpl | 1 + 2 files changed, 2 insertions(+) diff --git a/charts/budibase/README.md b/charts/budibase/README.md index 9164ace2ce..ac25ccf7c9 100644 --- a/charts/budibase/README.md +++ b/charts/budibase/README.md @@ -8,6 +8,7 @@ Budibase is an open source low-code platform, helping thousands of teams build a - Kubernetes 1.4+ - A storage controller (if you want to use persistent storage) - An ingress controller (if you want to define an `Ingress` resource) +- `metrics-server` (if you want to make use of horizontal pod autoscaling) ## Chart dependencies diff --git a/charts/budibase/README.md.gotmpl b/charts/budibase/README.md.gotmpl index 23c2399a15..91f9822cb3 100644 --- a/charts/budibase/README.md.gotmpl +++ b/charts/budibase/README.md.gotmpl @@ -7,6 +7,7 @@ - Kubernetes 1.4+ - A storage controller (if you want to use persistent storage) - An ingress controller (if you want to define an `Ingress` resource) +- `metrics-server` (if you want to make use of horizontal pod autoscaling) ## Chart dependencies From a65b29eb880f264fbc4fc78d3468f6109bc7044b Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 27 Nov 2023 18:50:44 +0000 Subject: [PATCH 027/102] banner changes for new pricing, fix for onboarding to prevent flash of UI before onboarding tutorial --- packages/backend-core/src/objectStore/objectStore.ts | 4 ++-- .../src/pages/builder/portal/apps/_layout.svelte | 2 +- .../src/pages/builder/portal/apps/index.svelte | 12 ++++++++++++ packages/pro | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/backend-core/src/objectStore/objectStore.ts b/packages/backend-core/src/objectStore/objectStore.ts index cdaf19fa55..cfd988f5e0 100644 --- a/packages/backend-core/src/objectStore/objectStore.ts +++ b/packages/backend-core/src/objectStore/objectStore.ts @@ -260,12 +260,12 @@ export async function listAllObjects(bucketName: string, path: string) { } /** - * Generate a presigned url with a default TTL of 1 hour + * Generate a presigned url with a default TTL of 1 day */ export function getPresignedUrl( bucketName: string, key: string, - durationSeconds: number = 3600 + durationSeconds: number = 86400 ) { const objectStore = ObjectStore(bucketName, { presigning: true }) const params = { diff --git a/packages/builder/src/pages/builder/portal/apps/_layout.svelte b/packages/builder/src/pages/builder/portal/apps/_layout.svelte index 38c0274eca..8810edca9c 100644 --- a/packages/builder/src/pages/builder/portal/apps/_layout.svelte +++ b/packages/builder/src/pages/builder/portal/apps/_layout.svelte @@ -14,7 +14,7 @@ import PortalSideBar from "./_components/PortalSideBar.svelte" // Don't block loading if we've already hydrated state - let loaded = $apps.length != null + let loaded = !!$apps?.length onMount(async () => { try { diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index ad0d3658ea..fb0ba8bc2e 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -1,5 +1,6 @@ @@ -48,7 +48,7 @@ .dividerClickExtender { position: absolute; - cursor: row-resize; + cursor: col-resize; height: 100%; width: 12px; } diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte index f01533d6bd..e3e24d7b13 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte @@ -5,7 +5,7 @@ import RoleIndicator from "./RoleIndicator.svelte" import DropdownMenu from "./DropdownMenu.svelte" import { goto } from "@roxi/routify" - import { getVerticalResizeActions } from "./resizable" + import { getVerticalResizeActions } from "components/common/resizable" import NavHeader from "components/common/NavHeader.svelte" const [resizable, resizableHandle] = getVerticalResizeActions() From 9ac413a73fd0dcf858fdd92d21b96d381f79845b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 28 Nov 2023 11:20:19 +0000 Subject: [PATCH 038/102] Remove healthcheck script for now. --- hosting/couchdb/Dockerfile | 4 ---- hosting/couchdb/healthcheck.sh | 13 ------------- 2 files changed, 17 deletions(-) delete mode 100644 hosting/couchdb/healthcheck.sh diff --git a/hosting/couchdb/Dockerfile b/hosting/couchdb/Dockerfile index 254a676f63..f83df7038b 100644 --- a/hosting/couchdb/Dockerfile +++ b/hosting/couchdb/Dockerfile @@ -29,10 +29,6 @@ WORKDIR /opt/couchdb ADD couch/vm.args couch/local.ini ./etc/ WORKDIR / -ADD healthcheck.sh ./healthcheck.sh -RUN chmod +x ./healthcheck.sh -HEALTHCHECK --interval=15s --timeout=15s --start-period=45s CMD "/healthcheck.sh" - ADD runner.sh ./bbcouch-runner.sh RUN chmod +x ./bbcouch-runner.sh /opt/clouseau/bin/clouseau CMD ["./bbcouch-runner.sh"] diff --git a/hosting/couchdb/healthcheck.sh b/hosting/couchdb/healthcheck.sh deleted file mode 100644 index 758ee5111a..0000000000 --- a/hosting/couchdb/healthcheck.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -healthy=true - -if [[ $(curl -s -w "%{http_code}\n" http://localhost:5984/_up -o /dev/null) -ne 200 ]]; then - echo 'ERROR: CouchDB is not running'; - healthy=false -fi - -if [ $healthy == true ]; then - exit 0 -else - exit 1 -fi From 24357aa7de033002501b446eca22866500af2292 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 28 Nov 2023 11:42:20 +0000 Subject: [PATCH 039/102] update copy in banner --- packages/builder/src/pages/builder/portal/apps/index.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index fac5c502b3..cf2c61b11d 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -205,7 +205,7 @@ messages: [ { message: - "We've updated our pricing - read the blog post to learn more.", + "We've updated our pricing - see our website to learn more.", type: BANNER_TYPES.NEUTRAL, extraButtonText: "Learn More", extraButtonAction: () => From e35e4b592c441a3f075a47c1fb03793e3eece354 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Tue, 28 Nov 2023 11:48:11 +0000 Subject: [PATCH 040/102] Bump version to 2.13.19 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 38872a31b1..e54b76c5e9 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.13.18", + "version": "2.13.19", "npmClient": "yarn", "packages": [ "packages/*" From adeb29a6a7109cdc9e201f0aa5f50f14d1b1323a Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 28 Nov 2023 14:21:27 +0000 Subject: [PATCH 041/102] Update pro to same as master. --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 5e3d59fc40..1037b032d4 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 5e3d59fc4060fd44b14b2599269c207753d4e5be +Subproject commit 1037b032d49244678204704d1bca779a29e395eb From 204769b6e99be4aeef026c1d65f052718bff7c04 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 29 Nov 2023 09:19:08 +0000 Subject: [PATCH 042/102] Add @budibase/backend as code owners to packages/{server,worker,backend-core} --- packages/backend-core/CODEOWNERS | 1 + packages/server/CODEOWNERS | 1 + packages/worker/CODEOWNERS | 1 + 3 files changed, 3 insertions(+) create mode 100644 packages/backend-core/CODEOWNERS create mode 100644 packages/server/CODEOWNERS create mode 100644 packages/worker/CODEOWNERS diff --git a/packages/backend-core/CODEOWNERS b/packages/backend-core/CODEOWNERS new file mode 100644 index 0000000000..291ffa073e --- /dev/null +++ b/packages/backend-core/CODEOWNERS @@ -0,0 +1 @@ +* @budibase/backend \ No newline at end of file diff --git a/packages/server/CODEOWNERS b/packages/server/CODEOWNERS new file mode 100644 index 0000000000..291ffa073e --- /dev/null +++ b/packages/server/CODEOWNERS @@ -0,0 +1 @@ +* @budibase/backend \ No newline at end of file diff --git a/packages/worker/CODEOWNERS b/packages/worker/CODEOWNERS new file mode 100644 index 0000000000..291ffa073e --- /dev/null +++ b/packages/worker/CODEOWNERS @@ -0,0 +1 @@ +* @budibase/backend \ No newline at end of file From fb3c072165cc48b128ab8bcbdb11e33c73e6654f Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 29 Nov 2023 09:22:47 +0000 Subject: [PATCH 043/102] Capitalise @Budibase. --- packages/backend-core/CODEOWNERS | 2 +- packages/server/CODEOWNERS | 2 +- packages/worker/CODEOWNERS | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend-core/CODEOWNERS b/packages/backend-core/CODEOWNERS index 291ffa073e..84313fb9cf 100644 --- a/packages/backend-core/CODEOWNERS +++ b/packages/backend-core/CODEOWNERS @@ -1 +1 @@ -* @budibase/backend \ No newline at end of file +* @Budibase/backend \ No newline at end of file diff --git a/packages/server/CODEOWNERS b/packages/server/CODEOWNERS index 291ffa073e..84313fb9cf 100644 --- a/packages/server/CODEOWNERS +++ b/packages/server/CODEOWNERS @@ -1 +1 @@ -* @budibase/backend \ No newline at end of file +* @Budibase/backend \ No newline at end of file diff --git a/packages/worker/CODEOWNERS b/packages/worker/CODEOWNERS index 291ffa073e..84313fb9cf 100644 --- a/packages/worker/CODEOWNERS +++ b/packages/worker/CODEOWNERS @@ -1 +1 @@ -* @budibase/backend \ No newline at end of file +* @Budibase/backend \ No newline at end of file From a5a3b12936e85755e71920ec738d0879332b58c2 Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:10:59 +0000 Subject: [PATCH 044/102] Return false don't throw (#12460) Co-authored-by: Sam Rose --- packages/shared-core/src/filters.ts | 2 +- .../shared-core/src/tests/filters.test.ts | 36 +++++++++---------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index 564e8a52c9..5e24b640d4 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -315,7 +315,7 @@ export const runLuceneQuery = (docs: any[], query?: SearchQuery) => { new Date(docValue).getTime() > new Date(testValue.high).getTime() ) } - throw "Cannot perform range filter - invalid type." + return false } ) diff --git a/packages/shared-core/src/tests/filters.test.ts b/packages/shared-core/src/tests/filters.test.ts index 6f488cffbd..bddd6cb1f0 100644 --- a/packages/shared-core/src/tests/filters.test.ts +++ b/packages/shared-core/src/tests/filters.test.ts @@ -130,32 +130,28 @@ describe("runLuceneQuery", () => { expect(runLuceneQuery(docs, query).map(row => row.order_id)).toEqual([2]) }) - it("should throw an error is an invalid doc value is passed into a range filter", async () => { + it("should return return all docs if an invalid doc value is passed into a range filter", async () => { + const docs = [ + { + order_id: 4, + customer_id: 1758, + order_status: 5, + order_date: "{{ Binding.INVALID }}", + required_date: "2017-03-05T00:00:00.000Z", + shipped_date: "2017-03-03T00:00:00.000Z", + store_id: 2, + staff_id: 7, + description: undefined, + label: "", + }, + ] const query = buildQuery("range", { order_date: { low: "2016-01-04T00:00:00.000Z", high: "2016-01-11T00:00:00.000Z", }, }) - expect(() => - runLuceneQuery( - [ - { - order_id: 4, - customer_id: 1758, - order_status: 5, - order_date: "INVALID", - required_date: "2017-03-05T00:00:00.000Z", - shipped_date: "2017-03-03T00:00:00.000Z", - store_id: 2, - staff_id: 7, - description: undefined, - label: "", - }, - ], - query - ) - ).toThrowError("Cannot perform range filter - invalid type.") + expect(runLuceneQuery(docs, query)).toEqual(docs) }) it("should return rows with matches on empty filter", () => { From 37dc8ba6e4d480e5a13b27d9367ea36868eeea4a Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:23:21 +0000 Subject: [PATCH 045/102] Only export selected columns (#12438) * Only export selected columns * Refactor and unit test --- .../src/sdk/app/rows/search/external.ts | 16 +++++------ .../src/sdk/app/rows/search/internal.ts | 5 ++-- .../server/src/sdk/tests/rows/row.spec.ts | 28 ++++++++++++++++++- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/packages/server/src/sdk/app/rows/search/external.ts b/packages/server/src/sdk/app/rows/search/external.ts index 2fc6caeb39..8465f997e3 100644 --- a/packages/server/src/sdk/app/rows/search/external.ts +++ b/packages/server/src/sdk/app/rows/search/external.ts @@ -133,9 +133,14 @@ export async function exportRows( let result = await search({ tableId, query: requestQuery, sort, sortOrder }) let rows: Row[] = [] + let headers + + if (!tableName) { + throw new HTTPError("Could not find table name.", 400) + } + const schema = datasource.entities[tableName].schema // Filter data to only specified columns if required - if (columns && columns.length) { for (let i = 0; i < result.rows.length; i++) { rows[i] = {} @@ -143,22 +148,17 @@ export async function exportRows( rows[i][column] = result.rows[i][column] } } + headers = columns } else { rows = result.rows } - if (!tableName) { - throw new HTTPError("Could not find table name.", 400) - } - const schema = datasource.entities[tableName].schema let exportRows = cleanExportRows(rows, schema, format, columns) - let headers = Object.keys(schema) - let content: string switch (format) { case exporters.Format.CSV: - content = exporters.csv(headers, exportRows) + content = exporters.csv(headers ?? Object.keys(schema), exportRows) break case exporters.Format.JSON: content = exporters.json(exportRows) diff --git a/packages/server/src/sdk/app/rows/search/internal.ts b/packages/server/src/sdk/app/rows/search/internal.ts index 87a33c0ba0..22cb3985b7 100644 --- a/packages/server/src/sdk/app/rows/search/internal.ts +++ b/packages/server/src/sdk/app/rows/search/internal.ts @@ -110,7 +110,7 @@ export async function exportRows( let rows: Row[] = [] let schema = table.schema - + let headers // Filter data to only specified columns if required if (columns && columns.length) { for (let i = 0; i < result.length; i++) { @@ -119,6 +119,7 @@ export async function exportRows( rows[i][column] = result[i][column] } } + headers = columns } else { rows = result } @@ -127,7 +128,7 @@ export async function exportRows( if (format === Format.CSV) { return { fileName: "export.csv", - content: csv(Object.keys(rows[0]), exportRows), + content: csv(headers ?? Object.keys(rows[0]), exportRows), } } else if (format === Format.JSON) { return { diff --git a/packages/server/src/sdk/tests/rows/row.spec.ts b/packages/server/src/sdk/tests/rows/row.spec.ts index af3d405e15..8b01356e35 100644 --- a/packages/server/src/sdk/tests/rows/row.spec.ts +++ b/packages/server/src/sdk/tests/rows/row.spec.ts @@ -18,7 +18,6 @@ jest.mock("../../../utilities/rowProcessor", () => ({ jest.mock("../../../api/controllers/view/exporters", () => ({ ...jest.requireActual("../../../api/controllers/view/exporters"), - csv: jest.fn(), Format: { CSV: "csv", }, @@ -102,5 +101,32 @@ describe("external row sdk", () => { new HTTPError("Could not find table name.", 400) ) }) + + it("should only export specified columns", async () => { + mockDatasourcesGet.mockImplementation(async () => ({ + entities: { + tablename: { + schema: { + name: {}, + age: {}, + dob: {}, + }, + }, + }, + })) + const headers = ["name", "dob"] + + const result = await exportRows({ + tableId: "datasource__tablename", + format: Format.CSV, + query: {}, + columns: headers, + }) + + expect(result).toEqual({ + fileName: "export.csv", + content: `"name","dob"`, + }) + }) }) }) From c2a82bb02190a493db79b7b101f6f38752e972e1 Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Wed, 29 Nov 2023 14:48:50 +0000 Subject: [PATCH 046/102] FIX broken references in a list of actions (#12459) * Refactor * Update action bindings on delete * Update action bindings on move * Fix with additional tests * Ensure visible binding is updated on drag release * fix * Refresh visible binding when action is deleted * Refactor * Refactor --- .../builder/src/builderStore/dataBinding.js | 84 ++++ .../builderStore/store/automation/index.js | 33 +- .../builderStore/tests/dataBinding.test.js | 459 ++++++++++++++++++ .../ButtonActionDrawer.svelte | 32 +- 4 files changed, 581 insertions(+), 27 deletions(-) diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index 85ac822006..d86e94aba2 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -29,6 +29,12 @@ const CAPTURE_VAR_INSIDE_TEMPLATE = /{{([^}]+)}}/g const CAPTURE_VAR_INSIDE_JS = /\$\("([^")]+)"\)/g const CAPTURE_HBS_TEMPLATE = /{{[\S\s]*?}}/g +const UpdateReferenceAction = { + ADD: "add", + DELETE: "delete", + MOVE: "move", +} + /** * Gets all bindable data context fields and instance fields. */ @@ -1226,3 +1232,81 @@ export const runtimeToReadableBinding = ( "readableBinding" ) } + +/** + * Used to update binding references for automation or action steps + * + * @param obj - The object to be updated + * @param originalIndex - The original index of the step being moved. Not applicable to add/delete. + * @param modifiedIndex - The new index of the step being modified + * @param action - Used to determine if a step is being added, deleted or moved + * @param label - The binding text that describes the steps + */ +export const updateReferencesInObject = ({ + obj, + modifiedIndex, + action, + label, + originalIndex, +}) => { + const stepIndexRegex = new RegExp(`{{\\s*${label}\\.(\\d+)\\.`, "g") + const updateActionStep = (str, index, replaceWith) => + str.replace(`{{ ${label}.${index}.`, `{{ ${label}.${replaceWith}.`) + for (const key in obj) { + if (typeof obj[key] === "string") { + let matches + while ((matches = stepIndexRegex.exec(obj[key])) !== null) { + const referencedStep = parseInt(matches[1]) + if ( + action === UpdateReferenceAction.ADD && + referencedStep >= modifiedIndex + ) { + obj[key] = updateActionStep( + obj[key], + referencedStep, + referencedStep + 1 + ) + } else if ( + action === UpdateReferenceAction.DELETE && + referencedStep > modifiedIndex + ) { + obj[key] = updateActionStep( + obj[key], + referencedStep, + referencedStep - 1 + ) + } else if (action === UpdateReferenceAction.MOVE) { + if (referencedStep === originalIndex) { + obj[key] = updateActionStep(obj[key], referencedStep, modifiedIndex) + } else if ( + modifiedIndex <= referencedStep && + modifiedIndex < originalIndex + ) { + obj[key] = updateActionStep( + obj[key], + referencedStep, + referencedStep + 1 + ) + } else if ( + modifiedIndex >= referencedStep && + modifiedIndex > originalIndex + ) { + obj[key] = updateActionStep( + obj[key], + referencedStep, + referencedStep - 1 + ) + } + } + } + } else if (typeof obj[key] === "object" && obj[key] !== null) { + updateReferencesInObject({ + obj: obj[key], + modifiedIndex, + action, + label, + originalIndex, + }) + } + } +} diff --git a/packages/builder/src/builderStore/store/automation/index.js b/packages/builder/src/builderStore/store/automation/index.js index ba2458f414..af83f73dc6 100644 --- a/packages/builder/src/builderStore/store/automation/index.js +++ b/packages/builder/src/builderStore/store/automation/index.js @@ -4,6 +4,7 @@ import { cloneDeep } from "lodash/fp" import { generate } from "shortid" import { selectedAutomation } from "builderStore" import { notifications } from "@budibase/bbui" +import { updateReferencesInObject } from "builderStore/dataBinding" const initialAutomationState = { automations: [], @@ -22,34 +23,14 @@ export const getAutomationStore = () => { return store } -const updateReferencesInObject = (obj, modifiedIndex, action) => { - const regex = /{{\s*steps\.(\d+)\./g - for (const key in obj) { - if (typeof obj[key] === "string") { - let matches - while ((matches = regex.exec(obj[key])) !== null) { - const referencedStep = parseInt(matches[1]) - if (action === "add" && referencedStep >= modifiedIndex) { - obj[key] = obj[key].replace( - `{{ steps.${referencedStep}.`, - `{{ steps.${referencedStep + 1}.` - ) - } else if (action === "delete" && referencedStep > modifiedIndex) { - obj[key] = obj[key].replace( - `{{ steps.${referencedStep}.`, - `{{ steps.${referencedStep - 1}.` - ) - } - } - } else if (typeof obj[key] === "object" && obj[key] !== null) { - updateReferencesInObject(obj[key], modifiedIndex, action) - } - } -} - const updateStepReferences = (steps, modifiedIndex, action) => { steps.forEach(step => { - updateReferencesInObject(step.inputs, modifiedIndex, action) + updateReferencesInObject({ + obj: step.inputs, + modifiedIndex, + action, + label: "steps", + }) }) } diff --git a/packages/builder/src/builderStore/tests/dataBinding.test.js b/packages/builder/src/builderStore/tests/dataBinding.test.js index 47f6564749..039e33a94d 100644 --- a/packages/builder/src/builderStore/tests/dataBinding.test.js +++ b/packages/builder/src/builderStore/tests/dataBinding.test.js @@ -2,6 +2,7 @@ import { expect, describe, it, vi } from "vitest" import { runtimeToReadableBinding, readableToRuntimeBinding, + updateReferencesInObject, } from "../dataBinding" vi.mock("@budibase/frontend-core") @@ -84,3 +85,461 @@ describe("readableToRuntimeBinding", () => { ).toEqual(`Hello {{ [user].[firstName] }}! The count is {{ count }}.`) }) }) + +describe("updateReferencesInObject", () => { + it("should increment steps in sequence on 'add'", () => { + let obj = [ + { + id: "a0", + parameters: { + text: "Alpha", + }, + }, + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "b2", + parameters: { + text: "Banana {{ actions.1.row }}", + }, + }, + { + id: "c3", + parameters: { + text: "Carrot {{ actions.1.row }}", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.3.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.4.row }}", + }, + }, + ] + updateReferencesInObject({ + obj, + modifiedIndex: 0, + action: "add", + label: "actions", + }) + + expect(obj).toEqual([ + { + id: "a0", + parameters: { + text: "Alpha", + }, + }, + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "b2", + parameters: { + text: "Banana {{ actions.2.row }}", + }, + }, + { + id: "c3", + parameters: { + text: "Carrot {{ actions.2.row }}", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.4.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.5.row }}", + }, + }, + ]) + }) + + it("should decrement steps in sequence on 'delete'", () => { + let obj = [ + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "b2", + parameters: { + text: "Banana {{ actions.1.row }}", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.3.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.4.row }}", + }, + }, + ] + updateReferencesInObject({ + obj, + modifiedIndex: 2, + action: "delete", + label: "actions", + }) + + expect(obj).toEqual([ + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "b2", + parameters: { + text: "Banana {{ actions.1.row }}", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.2.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.3.row }}", + }, + }, + ]) + }) + + it("should handle on 'move' to a lower index", () => { + let obj = [ + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "b2", + parameters: { + text: "Banana {{ actions.0.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.3.row }}", + }, + }, + { + id: "c3", + parameters: { + text: "Carrot {{ actions.0.row }}", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.2.row }}", + }, + }, + ] + updateReferencesInObject({ + obj, + modifiedIndex: 2, + action: "move", + label: "actions", + originalIndex: 4, + }) + + expect(obj).toEqual([ + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "b2", + parameters: { + text: "Banana {{ actions.0.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.4.row }}", + }, + }, + { + id: "c3", + parameters: { + text: "Carrot {{ actions.0.row }}", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.3.row }}", + }, + }, + ]) + }) + + it("should handle on 'move' to a higher index", () => { + let obj = [ + { + id: "b2", + parameters: { + text: "Banana {{ actions.0.row }}", + }, + }, + { + id: "c3", + parameters: { + text: "Carrot {{ actions.0.row }}", + }, + }, + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.2.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.3.row }}", + }, + }, + ] + updateReferencesInObject({ + obj, + modifiedIndex: 2, + action: "move", + label: "actions", + originalIndex: 0, + }) + + expect(obj).toEqual([ + { + id: "b2", + parameters: { + text: "Banana {{ actions.2.row }}", + }, + }, + { + id: "c3", + parameters: { + text: "Carrot {{ actions.2.row }}", + }, + }, + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.1.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.3.row }}", + }, + }, + ]) + }) + + it("should handle on 'move' of action being referenced, dragged to a higher index", () => { + let obj = [ + { + "##eventHandlerType": "Validate Form", + id: "cCD0Dwcnq", + }, + { + "##eventHandlerType": "Close Screen Modal", + id: "3fbbIOfN0H", + }, + { + "##eventHandlerType": "Save Row", + parameters: { + tableId: "ta_bb_employee", + }, + id: "aehg5cTmhR", + }, + { + "##eventHandlerType": "Close Side Panel", + id: "mzkpf86cxo", + }, + { + "##eventHandlerType": "Navigate To", + id: "h0uDFeJa8A", + }, + { + parameters: { + autoDismiss: true, + type: "success", + message: "{{ actions.1.row }}", + }, + "##eventHandlerType": "Show Notification", + id: "JEI5lAyJZ", + }, + ] + updateReferencesInObject({ + obj, + modifiedIndex: 2, + action: "move", + label: "actions", + originalIndex: 1, + }) + + expect(obj).toEqual([ + { + "##eventHandlerType": "Validate Form", + id: "cCD0Dwcnq", + }, + { + "##eventHandlerType": "Close Screen Modal", + id: "3fbbIOfN0H", + }, + { + "##eventHandlerType": "Save Row", + parameters: { + tableId: "ta_bb_employee", + }, + id: "aehg5cTmhR", + }, + { + "##eventHandlerType": "Close Side Panel", + id: "mzkpf86cxo", + }, + { + "##eventHandlerType": "Navigate To", + id: "h0uDFeJa8A", + }, + { + parameters: { + autoDismiss: true, + type: "success", + message: "{{ actions.2.row }}", + }, + "##eventHandlerType": "Show Notification", + id: "JEI5lAyJZ", + }, + ]) + }) + + it("should handle on 'move' of action being referenced, dragged to a lower index", () => { + let obj = [ + { + "##eventHandlerType": "Save Row", + parameters: { + tableId: "ta_bb_employee", + }, + id: "aehg5cTmhR", + }, + { + "##eventHandlerType": "Validate Form", + id: "cCD0Dwcnq", + }, + { + "##eventHandlerType": "Close Screen Modal", + id: "3fbbIOfN0H", + }, + { + "##eventHandlerType": "Close Side Panel", + id: "mzkpf86cxo", + }, + { + "##eventHandlerType": "Navigate To", + id: "h0uDFeJa8A", + }, + { + parameters: { + autoDismiss: true, + type: "success", + message: "{{ actions.4.row }}", + }, + "##eventHandlerType": "Show Notification", + id: "JEI5lAyJZ", + }, + ] + updateReferencesInObject({ + obj, + modifiedIndex: 0, + action: "move", + label: "actions", + originalIndex: 4, + }) + + expect(obj).toEqual([ + { + "##eventHandlerType": "Save Row", + parameters: { + tableId: "ta_bb_employee", + }, + id: "aehg5cTmhR", + }, + { + "##eventHandlerType": "Validate Form", + id: "cCD0Dwcnq", + }, + { + "##eventHandlerType": "Close Screen Modal", + id: "3fbbIOfN0H", + }, + { + "##eventHandlerType": "Close Side Panel", + id: "mzkpf86cxo", + }, + { + "##eventHandlerType": "Navigate To", + id: "h0uDFeJa8A", + }, + { + parameters: { + autoDismiss: true, + type: "success", + message: "{{ actions.0.row }}", + }, + "##eventHandlerType": "Show Notification", + id: "JEI5lAyJZ", + }, + ]) + }) +}) diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/ButtonActionDrawer.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/ButtonActionDrawer.svelte index f9541ea79f..109f9f62a2 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/ButtonActionDrawer.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/ButtonActionDrawer.svelte @@ -15,6 +15,7 @@ getEventContextBindings, getActionBindings, makeStateBinding, + updateReferencesInObject, } from "builderStore/dataBinding" import { cloneDeep } from "lodash/fp" @@ -30,6 +31,7 @@ let actionQuery let selectedAction = actions?.length ? actions[0] : null + let originalActionIndex const setUpdateActions = actions => { return actions @@ -115,6 +117,14 @@ if (isSelected) { selectedAction = actions?.length ? actions[0] : null } + + // Update action binding references + updateReferencesInObject({ + obj: actions, + modifiedIndex: index, + action: "delete", + label: "actions", + }) } const toggleActionList = () => { @@ -146,9 +156,29 @@ function handleDndConsider(e) { actions = e.detail.items + + // set the initial index of the action being dragged + if (e.detail.info.trigger === "draggedEntered") { + originalActionIndex = actions.findIndex( + action => action.id === e.detail.info.id + ) + } } function handleDndFinalize(e) { actions = e.detail.items + + // Update action binding references + updateReferencesInObject({ + obj: actions, + modifiedIndex: actions.findIndex( + action => action.id === e.detail.info.id + ), + action: "move", + label: "actions", + originalIndex: originalActionIndex, + }) + + originalActionIndex = -1 } const getAllBindings = (actionBindings, eventContextBindings, actions) => { @@ -289,7 +319,7 @@ {#if selectedActionComponent && !showAvailableActions} - {#key selectedAction.id} + {#key (selectedAction.id, originalActionIndex)}
Date: Wed, 29 Nov 2023 14:49:55 +0000 Subject: [PATCH 047/102] Removing codecov upload during Budibase CI/on PRs, right now this isn't much use due to the NX caching - if we wish to have accurate code coverage reports we will need to run a separate job periodically to check coverage by running the whole suite, with no caching. --- .github/workflows/budibase_ci.yml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 6e04ca6f67..cb713c93ac 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -99,11 +99,6 @@ jobs: else yarn test --ignore=@budibase/worker --ignore=@budibase/server --ignore=@budibase/pro fi - - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos - name: codecov-umbrella - verbose: true test-worker: runs-on: ubuntu-latest @@ -129,12 +124,6 @@ jobs: yarn test --scope=@budibase/worker fi - - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN || github.token }} # not required for public repos - name: codecov-umbrella - verbose: true - test-server: runs-on: ubuntu-latest steps: @@ -159,12 +148,6 @@ jobs: yarn test --scope=@budibase/server fi - - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN || github.token }} # not required for public repos - name: codecov-umbrella - verbose: true - test-pro: runs-on: ubuntu-latest if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase' From 5ac30510c685a558dfe332cd734ba3cf55aaa0e5 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 29 Nov 2023 15:14:53 +0000 Subject: [PATCH 048/102] Bump version to 2.13.20 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index e54b76c5e9..deb273884d 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.13.19", + "version": "2.13.20", "npmClient": "yarn", "packages": [ "packages/*" From 7ba412d5c85a5cc8e816eb9868604301dd7f2fa9 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 29 Nov 2023 16:53:17 +0000 Subject: [PATCH 049/102] List breaking changes between 2.x and 3.0.0 in chart readme. --- charts/budibase/README.md | 29 ++++++++++++++++++++++++++--- charts/budibase/README.md.gotmpl | 29 ++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/charts/budibase/README.md b/charts/budibase/README.md index ac25ccf7c9..808943186e 100644 --- a/charts/budibase/README.md +++ b/charts/budibase/README.md @@ -16,7 +16,30 @@ This chart depends on the official Apache CouchDB chart. You can see its documentation here: . -## Installing the Chart +## Upgrading + +### `2.x` to `3.0.0` + +We made a number of breaking changes in this release to make the chart more +idiomatic and easier to use. + +1. We no longer bundle `ingress-nginx`. If you were relying on this to supply + an ingress controller to your cluster, you will now need to deploy that + separately. You'll find guidance for that here: + . +2. We've upgraded the version of the [CouchDB chart](https://github.com/apache/couchdb-helm) + we use from `3.3.4` to `4.3.0`. The primary motivation for this was to align + the CouchDB chart used with the CouchDB version used, which has also updated + from 3.1.1 to 3.2.1. +3. We've separated out the supplied AWS ALB ingress resource for those deploying + into EKS. Where previously you enabled this by setting `ingress.enabled: false` + and `ingress.aws: true`, you now set `awsAlbIngress.enabled: true` and all + configuration for it is under `awsAlbIngress`. +4. The `HorizontalPodAutoscaler` that was configured at `hpa.enabled: true` has + been split into 3 separate HPAs, one for each of `apps`, `worker`, and `proxy`. + They are configured at `services.{apps,worker,proxy}.autoscaling`. + +## Installing To install the chart from our repository: @@ -86,7 +109,7 @@ $ helm install --create-namespace --namespace budibase budibase . -f values.yaml -## Configuration reference +## Configuring | Key | Type | Default | Description | |-----|------|---------|-------------| @@ -189,7 +212,7 @@ $ helm install --create-namespace --namespace budibase budibase . -f values.yaml | services.worker.startupProbe | object | HTTP health checks. | Startup probe configuration for worker pods. You shouldn't need to change this, but if you want to you can find more information here: | | tolerations | list | `[]` | Sets the tolerations for all pods created by this chart. Should not ordinarily need to be changed. See for more information on tolerations. | -## Uninstalling the Chart +## Uninstalling To uninstall the chart, assuming you named the release `budibase` (both commands in the installation section do so): diff --git a/charts/budibase/README.md.gotmpl b/charts/budibase/README.md.gotmpl index 91f9822cb3..66c8370ab8 100644 --- a/charts/budibase/README.md.gotmpl +++ b/charts/budibase/README.md.gotmpl @@ -15,7 +15,30 @@ This chart depends on the official Apache CouchDB chart. You can see its documentation here: . -## Installing the Chart +## Upgrading + +### `2.x` to `3.0.0` + +We made a number of breaking changes in this release to make the chart more +idiomatic and easier to use. + +1. We no longer bundle `ingress-nginx`. If you were relying on this to supply + an ingress controller to your cluster, you will now need to deploy that + separately. You'll find guidance for that here: + . +2. We've upgraded the version of the [CouchDB chart](https://github.com/apache/couchdb-helm) + we use from `3.3.4` to `4.3.0`. The primary motivation for this was to align + the CouchDB chart used with the CouchDB version used, which has also updated + from 3.1.1 to 3.2.1. +3. We've separated out the supplied AWS ALB ingress resource for those deploying + into EKS. Where previously you enabled this by setting `ingress.enabled: false` + and `ingress.aws: true`, you now set `awsAlbIngress.enabled: true` and all + configuration for it is under `awsAlbIngress`. +4. The `HorizontalPodAutoscaler` that was configured at `hpa.enabled: true` has + been split into 3 separate HPAs, one for each of `apps`, `worker`, and `proxy`. + They are configured at `services.{apps,worker,proxy}.autoscaling`. + +## Installing To install the chart from our repository: @@ -85,11 +108,11 @@ $ helm install --create-namespace --namespace budibase budibase . -f values.yaml -## Configuration reference +## Configuring {{ template "chart.valuesTable" . }} -## Uninstalling the Chart +## Uninstalling To uninstall the chart, assuming you named the release `budibase` (both commands in the installation section do so): From e4b1cf77053556d7674a3a8b54f86e26f58abf58 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 29 Nov 2023 16:57:11 +0000 Subject: [PATCH 050/102] Note that we're moving to our own CouchDB image in the helm chart readme. --- charts/budibase/README.md.gotmpl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/charts/budibase/README.md.gotmpl b/charts/budibase/README.md.gotmpl index 66c8370ab8..ac3bfef77d 100644 --- a/charts/budibase/README.md.gotmpl +++ b/charts/budibase/README.md.gotmpl @@ -29,7 +29,8 @@ idiomatic and easier to use. 2. We've upgraded the version of the [CouchDB chart](https://github.com/apache/couchdb-helm) we use from `3.3.4` to `4.3.0`. The primary motivation for this was to align the CouchDB chart used with the CouchDB version used, which has also updated - from 3.1.1 to 3.2.1. + from 3.1.1 to 3.2.1. Additionally, we're moving away from the official CouchDB + to one we're building ourselves. 3. We've separated out the supplied AWS ALB ingress resource for those deploying into EKS. Where previously you enabled this by setting `ingress.enabled: false` and `ingress.aws: true`, you now set `awsAlbIngress.enabled: true` and all From 1642d18d7dcdfad9a858f3049a1fa5d69aed859a Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 29 Nov 2023 17:01:08 +0000 Subject: [PATCH 051/102] Remove incorrect image from values.yaml example. --- charts/budibase/README.md | 8 ++------ charts/budibase/README.md.gotmpl | 5 ----- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/charts/budibase/README.md b/charts/budibase/README.md index 808943186e..5d74e05e0b 100644 --- a/charts/budibase/README.md +++ b/charts/budibase/README.md @@ -30,7 +30,8 @@ idiomatic and easier to use. 2. We've upgraded the version of the [CouchDB chart](https://github.com/apache/couchdb-helm) we use from `3.3.4` to `4.3.0`. The primary motivation for this was to align the CouchDB chart used with the CouchDB version used, which has also updated - from 3.1.1 to 3.2.1. + from 3.1.1 to 3.2.1. Additionally, we're moving away from the official CouchDB + to one we're building ourselves. 3. We've separated out the supplied AWS ALB ingress resource for those deploying into EKS. Where previously you enabled this by setting `ingress.enabled: false` and `ingress.aws: true`, you now set `awsAlbIngress.enabled: true` and all @@ -89,11 +90,6 @@ couchdb: storageClass: "nfs-client" adminPassword: admin - image: - repository: samwho/test - tag: latest - pullPolicy: Always - objectStore: storageClass: "nfs-client" redis: diff --git a/charts/budibase/README.md.gotmpl b/charts/budibase/README.md.gotmpl index ac3bfef77d..d7bc25c789 100644 --- a/charts/budibase/README.md.gotmpl +++ b/charts/budibase/README.md.gotmpl @@ -89,11 +89,6 @@ couchdb: storageClass: "nfs-client" adminPassword: admin - image: - repository: samwho/test - tag: latest - pullPolicy: Always - objectStore: storageClass: "nfs-client" redis: From e1b16c06ea941ed8e07fceb5419365347d447044 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 29 Nov 2023 17:01:52 +0000 Subject: [PATCH 052/102] Remove unnecessary annotations from example values.yaml. --- charts/budibase/README.md.gotmpl | 3 --- 1 file changed, 3 deletions(-) diff --git a/charts/budibase/README.md.gotmpl b/charts/budibase/README.md.gotmpl index d7bc25c789..92e91f8e09 100644 --- a/charts/budibase/README.md.gotmpl +++ b/charts/budibase/README.md.gotmpl @@ -68,9 +68,6 @@ staff's homelabs). ```yaml ingress: enabled: true - annotations: - nginx.ingress.kubernetes.io/client-max-body-size: 150M - nginx.ingress.kubernetes.io/proxy-body-size: 50m className: "nginx" hosts: - host: budibase.local # set this to whatever DNS name you'd use From b86640772ba07a16d077d93973810a9c5f365aa4 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 29 Nov 2023 18:45:48 +0000 Subject: [PATCH 053/102] Fix for saving relationships that have the same field name used on both sides, previously this could cause a relationship to be cleared depending on how the relationship schema was configured. There is a chance when saving that this won't happen as which side of the relationship is denoted by doc1 and doc2 is random, so when this happens is random. Using the table to pick the correct side is safer than just using the field name. --- .../server/src/api/controllers/static/index.ts | 6 ++++-- .../server/src/db/linkedRows/LinkController.ts | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index 4c5415a6c6..2963546e7f 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -24,7 +24,7 @@ import AWS from "aws-sdk" import fs from "fs" import sdk from "../../../sdk" import * as pro from "@budibase/pro" -import { App, Ctx, ProcessAttachmentResponse, Upload } from "@budibase/types" +import { App, Ctx, ProcessAttachmentResponse } from "@budibase/types" const send = require("koa-send") @@ -212,7 +212,9 @@ export const serveBuilderPreview = async function (ctx: Ctx) { if (!env.isJest()) { let appId = context.getAppId() - const previewHbs = loadHandlebarsFile(`${__dirname}/preview.hbs`) + const templateLoc = join(__dirname, "templates") + const previewLoc = fs.existsSync(templateLoc) ? templateLoc : __dirname + const previewHbs = loadHandlebarsFile(join(previewLoc, "preview.hbs")) ctx.body = await processString(previewHbs, { clientLibPath: objectStore.clientLibraryUrl(appId!, appInfo.version), }) diff --git a/packages/server/src/db/linkedRows/LinkController.ts b/packages/server/src/db/linkedRows/LinkController.ts index c4eed1169a..f52694465f 100644 --- a/packages/server/src/db/linkedRows/LinkController.ts +++ b/packages/server/src/db/linkedRows/LinkController.ts @@ -251,9 +251,19 @@ class LinkController { // find the docs that need to be deleted let toDeleteDocs = thisFieldLinkDocs .filter(doc => { - let correctDoc = - doc.doc1.fieldName === fieldName ? doc.doc2 : doc.doc1 - return rowField.indexOf(correctDoc.rowId) === -1 + let correctDoc + if ( + doc.doc1.tableId === table._id! && + doc.doc1.fieldName === fieldName + ) { + correctDoc = doc.doc2 + } else if ( + doc.doc2.tableId === table._id! && + doc.doc2.fieldName === fieldName + ) { + correctDoc = doc.doc1 + } + return correctDoc && rowField.indexOf(correctDoc.rowId) === -1 }) .map(doc => { return { ...doc, _deleted: true } From 160fbf21258823fe0fd06941e9061fc4719699b7 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 29 Nov 2023 19:53:56 +0000 Subject: [PATCH 054/102] Adding test case and fixing issue that it revealed with external tables as well. --- .../server/src/api/routes/tests/row.spec.ts | 35 +++++++++++++++++++ .../src/sdk/app/tables/external/utils.ts | 12 ++++--- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index de49441f3a..ba80f36b1b 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -517,9 +517,22 @@ describe.each([ }) describe("patch", () => { + let otherTable: Table + beforeAll(async () => { const tableConfig = generateTableConfig() table = await createTable(tableConfig) + const otherTableConfig = generateTableConfig() + // need a short name of table here - for relationship tests + otherTableConfig.name = "a" + otherTableConfig.schema.relationship = { + name: "relationship", + type: FieldType.LINK, + fieldName: "relationship", + tableId: table._id!, + relationshipType: RelationshipType.ONE_TO_MANY, + } + otherTable = await createTable(otherTableConfig) }) it("should update only the fields that are supplied", async () => { @@ -615,6 +628,28 @@ describe.each([ expect(getResp.body.user1[0]._id).toEqual(user2._id) expect(getResp.body.user2[0]._id).toEqual(user2._id) }) + + it("should be able to update relationships when both columns are same name", async () => { + let row = await config.api.row.save(table._id!, { + name: "test", + description: "test", + }) + let row2 = await config.api.row.save(otherTable._id!, { + name: "test", + description: "test", + relationship: [row._id], + }) + row = (await config.api.row.get(table._id!, row._id!)).body + expect(row.relationship.length).toBe(1) + const resp = await config.api.row.patch(table._id!, { + _id: row._id!, + _rev: row._rev!, + tableId: row.tableId!, + name: "test2", + relationship: [row2._id], + }) + expect(resp.relationship.length).toBe(1) + }) }) describe("destroy", () => { diff --git a/packages/server/src/sdk/app/tables/external/utils.ts b/packages/server/src/sdk/app/tables/external/utils.ts index bde812dd3d..50ea98eb39 100644 --- a/packages/server/src/sdk/app/tables/external/utils.ts +++ b/packages/server/src/sdk/app/tables/external/utils.ts @@ -1,5 +1,6 @@ import { Datasource, + FieldType, ManyToManyRelationshipFieldMetadata, ManyToOneRelationshipFieldMetadata, OneToManyRelationshipFieldMetadata, @@ -42,10 +43,13 @@ export function cleanupRelationships( for (let [relatedKey, relatedSchema] of Object.entries( relatedTable.schema )) { - if ( - relatedSchema.type === FieldTypes.LINK && - relatedSchema.fieldName === foreignKey - ) { + if (relatedSchema.type !== FieldType.LINK) { + continue + } + // if they both have the same field name it will appear as if it needs to be removed, + // don't cleanup in this scenario + const sameFieldNameForBoth = relatedSchema.name === schema.name + if (relatedSchema.fieldName === foreignKey && !sameFieldNameForBoth) { delete relatedTable.schema[relatedKey] } } From 4fcb4d56776aee881d7351a01c43c39036c5170a Mon Sep 17 00:00:00 2001 From: Christos Alexiou Date: Wed, 29 Nov 2023 23:39:41 +0200 Subject: [PATCH 055/102] fix: use /health and remove 301 --- charts/budibase/templates/alb-ingress.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/budibase/templates/alb-ingress.yaml b/charts/budibase/templates/alb-ingress.yaml index 6cd1cf2cba..fd38364ba6 100644 --- a/charts/budibase/templates/alb-ingress.yaml +++ b/charts/budibase/templates/alb-ingress.yaml @@ -7,8 +7,8 @@ metadata: kubernetes.io/ingress.class: alb alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip - alb.ingress.kubernetes.io/success-codes: 200,301 - alb.ingress.kubernetes.io/healthcheck-path: / + alb.ingress.kubernetes.io/success-codes: '200' + alb.ingress.kubernetes.io/healthcheck-path: '/health' {{- if .Values.ingress.certificateArn }} alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}' alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' From 3192a594c73528a7eca8561d1fe8e18ab929783b Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 30 Nov 2023 08:58:01 +0000 Subject: [PATCH 056/102] Allow querying of users table from automation --- .../src/components/automation/SetupPanel/TableSelector.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/automation/SetupPanel/TableSelector.svelte b/packages/builder/src/components/automation/SetupPanel/TableSelector.svelte index 3434219384..1645ded66b 100644 --- a/packages/builder/src/components/automation/SetupPanel/TableSelector.svelte +++ b/packages/builder/src/components/automation/SetupPanel/TableSelector.svelte @@ -22,7 +22,7 @@