diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 6c875f2dfe..c04031cc9d 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -1,5 +1,9 @@ name: Budibase CI +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + on: # Trigger the workflow on push or pull request, # but only for the master branch @@ -23,6 +27,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + with: + submodules: true + token: ${{ secrets.PERSONAL_ACCESS_TOKEN || github.token }} - name: Use Node.js 14.x uses: actions/setup-node@v3 with: @@ -135,15 +142,39 @@ jobs: with: submodules: true token: ${{ secrets.PERSONAL_ACCESS_TOKEN || github.token }} - fetch-depth: 0 - - name: Check submodule + + - name: Check pro commit + id: get_pro_commits run: | cd packages/pro - git fetch - if ! git merge-base --is-ancestor $(git log -n 1 --pretty=format:%H) origin/develop; then - echo "Current commit has not been merged to develop" - echo "Refer to the pro repo to merge your changes: https://github.com/Budibase/budibase-pro/blob/develop/docs/getting_started.md" - exit 1 + pro_commit=$(git rev-parse HEAD) + + branch=${{ github.base_ref || github.ref_name }} + echo "Running on branch `$branch` (base_ref=${{ github.base_ref }}, ref_name=${{ github.head_ref }})" + + if [[ "$branch" == "master" ]]; then + base_commit=$(git rev-parse origin/master) else - echo "All good, the submodule had been merged!" + base_commit=$(git rev-parse origin/develop) fi + + echo "pro_commit=$pro_commit" + echo "pro_commit=$pro_commit" >> "$GITHUB_OUTPUT" + echo "base_commit=$base_commit" + echo "base_commit=$base_commit" >> "$GITHUB_OUTPUT" + + - name: Check submodule merged to develop + uses: actions/github-script@v4 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const submoduleCommit = '${{ steps.get_pro_commits.outputs.pro_commit }}'; + const baseCommit = '${{ steps.get_pro_commits.outputs.base_commit }}'; + + if (submoduleCommit !== baseCommit) { + console.error('Submodule commit does not match the latest commit on the develop branch.'); + console.error('Refer to the pro repo to merge your changes: https://github.com/Budibase/budibase-pro/blob/develop/docs/getting_started.md') + process.exit(1); + } else { + console.log('All good, the submodule had been merged and setup correctly!') + } diff --git a/hosting/scripts/airgapped/airgappedDockerBuild.js b/hosting/scripts/airgapped/airgappedDockerBuild.js index cc0ea48eb3..58bc7c09a9 100755 --- a/hosting/scripts/airgapped/airgappedDockerBuild.js +++ b/hosting/scripts/airgapped/airgappedDockerBuild.js @@ -2,7 +2,9 @@ const fs = require("fs") const { execSync } = require("child_process") const path = require("path") -const IMAGES = { +const IS_SINGLE_IMAGE = process.env.SINGLE_IMAGE + +let IMAGES = { worker: "budibase/worker", apps: "budibase/apps", proxy: "budibase/proxy", @@ -10,7 +12,13 @@ const IMAGES = { couch: "ibmcom/couchdb3", curl: "curlimages/curl", redis: "redis", - watchtower: "containrrr/watchtower" + watchtower: "containrrr/watchtower", +} + +if (IS_SINGLE_IMAGE) { + IMAGES = { + budibase: "budibase/budibase" + } } const FILES = { @@ -39,11 +47,10 @@ for (let image in IMAGES) { } // copy config files -copyFile(FILES.COMPOSE) +if (!IS_SINGLE_IMAGE) { + copyFile(FILES.COMPOSE) +} copyFile(FILES.ENV) // compress -execSync(`tar -czf bb-airgapped.tar.gz hosting/scripts/bb-airgapped`) - -// clean up -fs.rmdirSync(OUTPUT_DIR, { recursive: true }) \ No newline at end of file +execSync(`tar -czf bb-airgapped.tar.gz hosting/scripts/bb-airgapped`) \ No newline at end of file diff --git a/hosting/single/Dockerfile b/hosting/single/Dockerfile index 56df8185a9..e43e5ad10c 100644 --- a/hosting/single/Dockerfile +++ b/hosting/single/Dockerfile @@ -37,6 +37,14 @@ COPY --from=build /worker /worker RUN apt-get update && \ apt-get install -y --no-install-recommends software-properties-common nginx uuid-runtime redis-server +# Install postgres client for pg_dump utils +RUN apt install software-properties-common apt-transport-https gpg -y \ + && curl -fsSl https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/postgresql.gpg > /dev/null \ + && echo deb [arch=amd64,arm64,ppc64el signed-by=/usr/share/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main | tee /etc/apt/sources.list.d/postgresql.list \ + && apt update -y \ + && apt install postgresql-client-15 -y \ + && apt remove software-properties-common apt-transport-https gpg -y + # install other dependencies, nodejs, oracle requirements, jdk8, redis, nginx WORKDIR /nodejs RUN curl -sL https://deb.nodesource.com/setup_16.x -o /tmp/nodesource_setup.sh && \ diff --git a/lerna.json b/lerna.json index b43faee706..0812f15061 100644 --- a/lerna.json +++ b/lerna.json @@ -1,22 +1,10 @@ { - "version": "2.7.33", + "version": "2.7.34-alpha.6", "npmClient": "yarn", "packages": [ - "packages/backend-core", - "packages/bbui", - "packages/builder", - "packages/cli", - "packages/client", - "packages/frontend-core", - "packages/sdk", - "packages/server", - "packages/shared-core", - "packages/string-templates", - "packages/types", - "packages/worker", - "packages/pro/packages/pro" + "packages/*" ], - "useWorkspaces": true, + "useNx": true, "command": { "publish": { "ignoreChanges": [ diff --git a/package.json b/package.json index 56f015f8c0..b521030caf 100644 --- a/package.json +++ b/package.json @@ -2,23 +2,22 @@ "name": "root", "private": true, "devDependencies": { - "@esbuild-plugins/node-resolve": "^0.2.2", "@esbuild-plugins/tsconfig-paths": "^0.1.2", "@nx/js": "16.2.1", "@rollup/plugin-json": "^4.0.2", "@typescript-eslint/parser": "5.45.0", "babel-eslint": "^10.0.3", "esbuild": "^0.17.18", + "esbuild-node-externals": "^1.7.0", "eslint": "^7.28.0", "eslint-plugin-cypress": "^2.11.3", "eslint-plugin-svelte3": "^3.2.0", "husky": "^8.0.3", "js-yaml": "^4.1.0", "kill-port": "^1.6.1", - "lerna": "7.0.0-alpha.0", + "lerna": "7.0.2", "madge": "^6.0.0", "minimist": "^1.2.8", - "nx": "^16.2.1", "prettier": "^2.3.1", "prettier-plugin-svelte": "^2.3.0", "rimraf": "^3.0.2", @@ -48,9 +47,9 @@ "kill-builder": "kill-port 3000", "kill-server": "kill-port 4001 4002", "kill-all": "yarn run kill-builder && yarn run kill-server", - "dev": "yarn run kill-all && lerna run --stream --parallel dev:builder --stream", - "dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && lerna run --stream --parallel dev:builder --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker", - "dev:server": "yarn run kill-server && lerna run --stream --parallel dev:builder --scope @budibase/worker --scope @budibase/server", + "dev": "yarn run kill-all && lerna run --stream --parallel dev:builder --stream", + "dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && lerna run --stream --parallel dev:builder --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker", + "dev:server": "yarn run kill-server && yarn build --projects=@budibase/client && lerna run --stream --parallel dev:builder --scope @budibase/worker --scope @budibase/server", "dev:built": "yarn run kill-all && cd packages/server && yarn dev:stack:up && cd ../../ && lerna run --stream --parallel dev:built", "dev:docker": "yarn build:docker:pre && docker-compose -f hosting/docker-compose.build.yaml -f hosting/docker-compose.dev.yaml --env-file hosting/.env up --build --scale proxy-service=0", "test": "lerna run --stream test --stream", @@ -67,6 +66,7 @@ "build:docker:selfhost": "lerna run --stream build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh latest && cd -", "build:docker:develop": "node scripts/pinVersions && lerna run --stream build:docker && yarn build:docker:proxy && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh develop && cd -", "build:docker:airgap": "node hosting/scripts/airgapped/airgappedDockerBuild", + "build:docker:airgap:single": "SINGLE_IMAGE=1 node hosting/scripts/airgapped/airgappedDockerBuild", "build:digitalocean": "cd hosting/digitalocean && ./build.sh && cd -", "build:docker:single:multiarch": "docker buildx build --platform linux/arm64,linux/amd64 -f hosting/single/Dockerfile -t budibase:latest .", "build:docker:single:image": "docker build -f hosting/single/Dockerfile -t budibase:latest .", @@ -95,19 +95,7 @@ }, "workspaces": { "packages": [ - "packages/backend-core", - "packages/bbui", - "packages/builder", - "packages/cli", - "packages/client", - "packages/frontend-core", - "packages/sdk", - "packages/server", - "packages/shared-core", - "packages/string-templates", - "packages/types", - "packages/worker", - "packages/pro/packages/pro" + "packages/*" ] }, "resolutions": { diff --git a/packages/backend-core/jest.config.ts b/packages/backend-core/jest.config.ts index 1e69797e71..8d64b24a2f 100644 --- a/packages/backend-core/jest.config.ts +++ b/packages/backend-core/jest.config.ts @@ -31,4 +31,6 @@ const config: Config.InitialOptions = { coverageReporters: ["lcov", "json", "clover"], } +process.env.DISABLE_PINO_LOGGER = "1" + export default config diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index f85687b007..4a1ed5c373 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -27,7 +27,7 @@ "@techpass/passport-openidconnect": "0.3.2", "aws-cloudfront-sign": "2.2.0", "aws-sdk": "2.1030.0", - "bcrypt": "5.0.1", + "bcrypt": "5.1.0", "bcryptjs": "2.4.3", "bull": "4.10.1", "correlation-id": "4.0.0", diff --git a/packages/backend-core/src/middleware/passport/datasource/google.ts b/packages/backend-core/src/middleware/passport/datasource/google.ts index 6fd4e9ff32..ae6b3b4913 100644 --- a/packages/backend-core/src/middleware/passport/datasource/google.ts +++ b/packages/backend-core/src/middleware/passport/datasource/google.ts @@ -1,10 +1,11 @@ import * as google from "../sso/google" import { Cookie } from "../../../constants" -import { clearCookie, getCookie } from "../../../utils" -import { doWithDB } from "../../../db" import * as configs from "../../../configs" -import { BBContext, Database, SSOProfile } from "@budibase/types" +import * as cache from "../../../cache" +import * as utils from "../../../utils" +import { UserCtx, SSOProfile } from "@budibase/types" import { ssoSaveUserNoOp } from "../sso/sso" + const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy type Passport = { @@ -22,7 +23,7 @@ async function fetchGoogleCreds() { export async function preAuth( passport: Passport, - ctx: BBContext, + ctx: UserCtx, next: Function ) { // get the relevant config @@ -36,8 +37,8 @@ export async function preAuth( ssoSaveUserNoOp ) - if (!ctx.query.appId || !ctx.query.datasourceId) { - ctx.throw(400, "appId and datasourceId query params not present.") + if (!ctx.query.appId) { + ctx.throw(400, "appId query param not present.") } return passport.authenticate(strategy, { @@ -49,7 +50,7 @@ export async function preAuth( export async function postAuth( passport: Passport, - ctx: BBContext, + ctx: UserCtx, next: Function ) { // get the relevant config @@ -57,7 +58,7 @@ export async function postAuth( const platformUrl = await configs.getPlatformUrl({ tenantAware: false }) let callbackUrl = `${platformUrl}/api/global/auth/datasource/google/callback` - const authStateCookie = getCookie(ctx, Cookie.DatasourceAuth) + const authStateCookie = utils.getCookie(ctx, Cookie.DatasourceAuth) return passport.authenticate( new GoogleStrategy( @@ -69,33 +70,26 @@ export async function postAuth( ( accessToken: string, refreshToken: string, - profile: SSOProfile, + _profile: SSOProfile, done: Function ) => { - clearCookie(ctx, Cookie.DatasourceAuth) + utils.clearCookie(ctx, Cookie.DatasourceAuth) done(null, { accessToken, refreshToken }) } ), { successRedirect: "/", failureRedirect: "/error" }, async (err: any, tokens: string[]) => { const baseUrl = `/builder/app/${authStateCookie.appId}/data` - // update the DB for the datasource with all the user info - await doWithDB(authStateCookie.appId, async (db: Database) => { - let datasource - try { - datasource = await db.get(authStateCookie.datasourceId) - } catch (err: any) { - if (err.status === 404) { - ctx.redirect(baseUrl) - } + + const id = utils.newid() + await cache.store( + `datasource:creation:${authStateCookie.appId}:google:${id}`, + { + tokens, } - if (!datasource.config) { - datasource.config = {} - } - datasource.config.auth = { type: "google", ...tokens } - await db.put(datasource) - ctx.redirect(`${baseUrl}/datasource/${authStateCookie.datasourceId}`) - }) + ) + + ctx.redirect(`${baseUrl}/new?continue_google_setup=${id}`) } )(ctx, next) } diff --git a/packages/backend-core/src/security/encryption.ts b/packages/backend-core/src/security/encryption.ts index f9adb68955..7a8cfaf04a 100644 --- a/packages/backend-core/src/security/encryption.ts +++ b/packages/backend-core/src/security/encryption.ts @@ -1,12 +1,17 @@ import crypto from "crypto" +import fs from "fs" +import zlib from "zlib" import env from "../environment" +import { join } from "path" const ALGO = "aes-256-ctr" const SEPARATOR = "-" const ITERATIONS = 10000 -const RANDOM_BYTES = 16 const STRETCH_LENGTH = 32 +const SALT_LENGTH = 16 +const IV_LENGTH = 16 + export enum SecretOption { API = "api", ENCRYPTION = "encryption", @@ -31,15 +36,15 @@ export function getSecret(secretOption: SecretOption): string { return secret } -function stretchString(string: string, salt: Buffer) { - return crypto.pbkdf2Sync(string, salt, ITERATIONS, STRETCH_LENGTH, "sha512") +function stretchString(secret: string, salt: Buffer) { + return crypto.pbkdf2Sync(secret, salt, ITERATIONS, STRETCH_LENGTH, "sha512") } export function encrypt( input: string, secretOption: SecretOption = SecretOption.API ) { - const salt = crypto.randomBytes(RANDOM_BYTES) + const salt = crypto.randomBytes(SALT_LENGTH) const stretched = stretchString(getSecret(secretOption), salt) const cipher = crypto.createCipheriv(ALGO, stretched, salt) const base = cipher.update(input) @@ -60,3 +65,115 @@ export function decrypt( const final = decipher.final() return Buffer.concat([base, final]).toString() } + +export async function encryptFile( + { dir, filename }: { dir: string; filename: string }, + secret: string +) { + const outputFileName = `${filename}.enc` + + const filePath = join(dir, filename) + const inputFile = fs.createReadStream(filePath) + const outputFile = fs.createWriteStream(join(dir, outputFileName)) + + const salt = crypto.randomBytes(SALT_LENGTH) + const iv = crypto.randomBytes(IV_LENGTH) + const stretched = stretchString(secret, salt) + const cipher = crypto.createCipheriv(ALGO, stretched, iv) + + outputFile.write(salt) + outputFile.write(iv) + + inputFile.pipe(zlib.createGzip()).pipe(cipher).pipe(outputFile) + + return new Promise<{ filename: string; dir: string }>(r => { + outputFile.on("finish", () => { + r({ + filename: outputFileName, + dir, + }) + }) + }) +} + +async function getSaltAndIV(path: string) { + const fileStream = fs.createReadStream(path) + + const salt = await readBytes(fileStream, SALT_LENGTH) + const iv = await readBytes(fileStream, IV_LENGTH) + fileStream.close() + return { salt, iv } +} + +export async function decryptFile( + inputPath: string, + outputPath: string, + secret: string +) { + const { salt, iv } = await getSaltAndIV(inputPath) + const inputFile = fs.createReadStream(inputPath, { + start: SALT_LENGTH + IV_LENGTH, + }) + + const outputFile = fs.createWriteStream(outputPath) + + const stretched = stretchString(secret, salt) + const decipher = crypto.createDecipheriv(ALGO, stretched, iv) + + const unzip = zlib.createGunzip() + + inputFile.pipe(decipher).pipe(unzip).pipe(outputFile) + + return new Promise((res, rej) => { + outputFile.on("finish", () => { + outputFile.close() + res() + }) + + inputFile.on("error", e => { + outputFile.close() + rej(e) + }) + + decipher.on("error", e => { + outputFile.close() + rej(e) + }) + + unzip.on("error", e => { + outputFile.close() + rej(e) + }) + + outputFile.on("error", e => { + outputFile.close() + rej(e) + }) + }) +} + +function readBytes(stream: fs.ReadStream, length: number) { + return new Promise((resolve, reject) => { + let bytesRead = 0 + const data: Buffer[] = [] + + stream.on("readable", () => { + let chunk + + while ((chunk = stream.read(length - bytesRead)) !== null) { + data.push(chunk) + bytesRead += chunk.length + } + + resolve(Buffer.concat(data)) + }) + + stream.on("end", () => { + reject(new Error("Insufficient data in the stream.")) + }) + + stream.on("error", error => { + reject(error) + }) + }) +} diff --git a/packages/backend-core/src/security/roles.ts b/packages/backend-core/src/security/roles.ts index bdf7a38726..e8a3c76c0a 100644 --- a/packages/backend-core/src/security/roles.ts +++ b/packages/backend-core/src/security/roles.ts @@ -140,9 +140,13 @@ export function lowerBuiltinRoleID(roleId1?: string, roleId2?: string): string { * Gets the role object, this is mainly useful for two purposes, to check if the level exists and * to check if the role inherits any others. * @param {string|null} roleId The level ID to lookup. + * @param {object|null} opts options for the function, like whether to halt errors, instead return public. * @returns {Promise} The role object, which may contain an "inherits" property. */ -export async function getRole(roleId?: string): Promise { +export async function getRole( + roleId?: string, + opts?: { defaultPublic?: boolean } +): Promise { if (!roleId) { return undefined } @@ -161,6 +165,9 @@ export async function getRole(roleId?: string): Promise { // finalise the ID role._id = getExternalRoleID(role._id) } catch (err) { + if (!isBuiltin(roleId) && opts?.defaultPublic) { + return cloneDeep(BUILTIN_ROLES.PUBLIC) + } // only throw an error if there is no role at all if (Object.keys(role).length === 0) { throw err diff --git a/packages/bbui/src/FancyForm/FancyCheckbox.svelte b/packages/bbui/src/FancyForm/FancyCheckbox.svelte index 191cc79485..0a2e5ac159 100644 --- a/packages/bbui/src/FancyForm/FancyCheckbox.svelte +++ b/packages/bbui/src/FancyForm/FancyCheckbox.svelte @@ -8,6 +8,8 @@ export let disabled = false export let error = null export let validate = null + export let indeterminate = false + export let compact = false const dispatch = createEventDispatcher() @@ -21,11 +23,19 @@ } - + - + -
+
{#if text} {text} {/if} @@ -47,6 +57,10 @@ line-clamp: 2; -webkit-box-orient: vertical; } + .text.compact { + font-size: 13px; + line-height: 15px; + } .text > :global(*) { font-size: inherit !important; } diff --git a/packages/bbui/src/FancyForm/FancyCheckboxGroup.svelte b/packages/bbui/src/FancyForm/FancyCheckboxGroup.svelte new file mode 100644 index 0000000000..ca3a6d937a --- /dev/null +++ b/packages/bbui/src/FancyForm/FancyCheckboxGroup.svelte @@ -0,0 +1,69 @@ + + +{#if options && Array.isArray(options)} +
+ + {#if showSelectAll} + + {/if} + {#each options as option, i} + + {/each} + +
+{/if} + + diff --git a/packages/bbui/src/FancyForm/FancyField.svelte b/packages/bbui/src/FancyForm/FancyField.svelte index 0c99394599..455f4b38fb 100644 --- a/packages/bbui/src/FancyForm/FancyField.svelte +++ b/packages/bbui/src/FancyForm/FancyField.svelte @@ -11,6 +11,7 @@ export let value export let ref export let autoHeight + export let compact = false const formContext = getContext("fancy-form") const id = Math.random() @@ -42,6 +43,7 @@ class:disabled class:focused class:clickable + class:compact class:auto-height={autoHeight} >
@@ -61,7 +63,6 @@ diff --git a/packages/builder/src/components/backend/DatasourceNavigator/_components/DatasourceCard.svelte b/packages/builder/src/components/backend/DatasourceNavigator/_components/DatasourceCard.svelte deleted file mode 100644 index 67ecf1c56c..0000000000 --- a/packages/builder/src/components/backend/DatasourceNavigator/_components/DatasourceCard.svelte +++ /dev/null @@ -1,60 +0,0 @@ - - -
dispatcher("selected", integrationType)} - class="item hoverable" -> -
- -
- {schema.friendlyName} - {#if schema.type} - {schema.type || ""} - {/if} -
-
-
- - diff --git a/packages/builder/src/components/backend/DatasourceNavigator/_components/GoogleSignIn.svelte b/packages/builder/src/components/backend/DatasourceNavigator/_components/GoogleSignIn.svelte deleted file mode 100644 index c30e8fc2ee..0000000000 --- a/packages/builder/src/components/backend/DatasourceNavigator/_components/GoogleSignIn.svelte +++ /dev/null @@ -1,145 +0,0 @@ - - - - - btn_google_dark_normal_ios - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js index 18aa361570..2486942dea 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js @@ -44,6 +44,9 @@ export default ICONS export function getIcon(integrationType, schema) { const integrationList = get(integrations) + if (!integrationList) { + return + } if (integrationList[integrationType]?.iconUrl) { return { url: integrationList[integrationType].iconUrl } } else if (schema?.custom || !ICONS[integrationType]) { diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte index 31a0d21cd8..1d84dbbe39 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte @@ -1,12 +1,19 @@ saveDatasource()} - confirmText={datasource.plus ? "Connect" : "Save and continue to query"} - cancelText="Back" - showSecondaryButton={datasource.plus} + {title} + onConfirm={() => nextStep()} + {confirmText} + cancelText={fetchTableStep ? "Cancel" : "Back"} + showSecondaryButton={datasourcePlus} size="L" disabled={!isValid} > - Connect your database to Budibase using the config below. + + {#if !fetchTableStep} + Connect your database to Budibase using the config below + {:else} + Choose what tables you want to sync with Budibase + {/if} - (isValid = e.detail)} - /> + {#if !fetchTableStep} + (isValid = e.detail)} + /> + {:else} +
+ +
+ {/if}
+ + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/GoogleDatasourceConfigModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/GoogleDatasourceConfigModal.svelte deleted file mode 100644 index 0783a9fe53..0000000000 --- a/packages/builder/src/components/backend/DatasourceNavigator/modals/GoogleDatasourceConfigModal.svelte +++ /dev/null @@ -1,43 +0,0 @@ - - - - - {#if isGoogleConfigured === true} - - Authenticate with your google account to use the {IntegrationNames[ - datasource.type - ]} integration. - - save(datasource, true)} /> - {:else if isGoogleConfigured === false} - Google authentication is not enabled, please complete Google SSO - configuration. - Configure Google SSO - {/if} - diff --git a/packages/builder/src/components/commandPalette/CommandPalette.svelte b/packages/builder/src/components/commandPalette/CommandPalette.svelte index ae946dc10c..3a369446a3 100644 --- a/packages/builder/src/components/commandPalette/CommandPalette.svelte +++ b/packages/builder/src/components/commandPalette/CommandPalette.svelte @@ -69,7 +69,7 @@ name: "App", description: "", icon: "Play", - action: () => window.open(`/${$store.appId}`), + action: () => store.update(state => ({ ...state, showPreview: true })), }, { type: "Preview", diff --git a/packages/builder/src/components/common/bindings/DrawerBindableInput.svelte b/packages/builder/src/components/common/bindings/DrawerBindableInput.svelte index 7fbbf75a30..dacb076bdb 100644 --- a/packages/builder/src/components/common/bindings/DrawerBindableInput.svelte +++ b/packages/builder/src/components/common/bindings/DrawerBindableInput.svelte @@ -21,7 +21,6 @@ export let allowHelpers = true export let updateOnChange = true export let drawerLeft - export let key const dispatch = createEventDispatcher() let bindingDrawer diff --git a/packages/builder/src/components/deploy/AppActions.svelte b/packages/builder/src/components/deploy/AppActions.svelte index 9813237317..a85eb5a154 100644 --- a/packages/builder/src/components/deploy/AppActions.svelte +++ b/packages/builder/src/components/deploy/AppActions.svelte @@ -62,7 +62,10 @@ } const previewApp = () => { - window.open(`/${application}`) + store.update(state => ({ + ...state, + showPreview: true, + })) } const viewApp = () => { diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ScrollTo.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ScrollTo.svelte new file mode 100644 index 0000000000..2198a87ecb --- /dev/null +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ScrollTo.svelte @@ -0,0 +1,50 @@ + + +
+ + ($validation.touched.name = true)} - on:change={nameToUrl($values.name)} - label="Name" - placeholder={defaultAppName} - /> - - ($validation.touched.url = true)} - on:change={tidyUrl($values.url)} - label="URL" - placeholder={$values.url - ? $values.url - : `/${resolveAppUrl(template, $values.name)}`} - /> - {#if $values.url && $values.url !== "" && !$validation.errors.url} -
- {appUrl} -
+ {#if currentStep === Step.CONFIG} + {#if template && !template?.fromFile} + {/if} -
+ {#if template?.fromFile} + { + $values.file = e.detail?.[0] + $validation.touched.file = true + }} + /> + {/if} + ($validation.touched.name = true)} + on:change={nameToUrl($values.name)} + label="Name" + placeholder={defaultAppName} + /> + + ($validation.touched.url = true)} + on:change={tidyUrl($values.url)} + label="URL" + placeholder={$values.url + ? $values.url + : `/${resolveAppUrl(template, $values.name)}`} + /> + {#if $values.url && $values.url !== "" && !$validation.errors.url} +
+ {appUrl} +
+ {/if} +
+ {/if} + {#if currentStep === Step.SET_PASSWORD} + ($encryptionValidation.touched.encryptionPassword = true)} + error={$encryptionValidation.touched.encryptionPassword && + $encryptionValidation.errors.encryptionPassword} + /> + {/if} diff --git a/packages/builder/src/pages/builder/app/[application]/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/_layout.svelte index f13aa3dab6..ee3f6e877e 100644 --- a/packages/builder/src/pages/builder/app/[application]/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/_layout.svelte @@ -24,6 +24,7 @@ import BuilderSidePanel from "./_components/BuilderSidePanel.svelte" import UserAvatars from "./_components/UserAvatars.svelte" import { TOUR_KEYS, TOURS } from "components/portal/onboarding/tours.js" + import PreviewOverlay from "./_components/PreviewOverlay.svelte" export let application @@ -140,7 +141,7 @@ {/if} -
+
{#if $store.initialised}
@@ -230,6 +231,10 @@ {/await}
+{#if $store.showPreview} + +{/if} + @@ -248,6 +253,10 @@ width: 100%; display: flex; flex-direction: column; + transition: filter 260ms ease-out; + } + .root.blur { + filter: blur(8px); } .top-nav { diff --git a/packages/builder/src/pages/builder/app/[application]/data/_components/CreateExternalDatasourceModal/DatasourceConfigEditor/ConfigInput.svelte b/packages/builder/src/pages/builder/app/[application]/data/_components/CreateExternalDatasourceModal/DatasourceConfigEditor/ConfigInput.svelte new file mode 100644 index 0000000000..37fd579594 --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/data/_components/CreateExternalDatasourceModal/DatasourceConfigEditor/ConfigInput.svelte @@ -0,0 +1,40 @@ + + + diff --git a/packages/builder/src/pages/builder/app/[application]/data/_components/CreateExternalDatasourceModal/DatasourceConfigEditor/fields/Boolean.svelte b/packages/builder/src/pages/builder/app/[application]/data/_components/CreateExternalDatasourceModal/DatasourceConfigEditor/fields/Boolean.svelte new file mode 100644 index 0000000000..daa4b7a2f9 --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/data/_components/CreateExternalDatasourceModal/DatasourceConfigEditor/fields/Boolean.svelte @@ -0,0 +1,20 @@ + + +
+ + +
+ + diff --git a/packages/builder/src/pages/builder/app/[application]/data/_components/CreateExternalDatasourceModal/DatasourceConfigEditor/fields/FieldGroup.svelte b/packages/builder/src/pages/builder/app/[application]/data/_components/CreateExternalDatasourceModal/DatasourceConfigEditor/fields/FieldGroup.svelte new file mode 100644 index 0000000000..a83929663b --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/data/_components/CreateExternalDatasourceModal/DatasourceConfigEditor/fields/FieldGroup.svelte @@ -0,0 +1,47 @@ + + + !!properties.value)} + header={name} +> + + {#each value as field} +
+ + handleChange(field.key, e.detail)} + value={field.value} + /> +
+ {/each} +
+
+ + diff --git a/packages/builder/src/pages/builder/app/[application]/data/_components/CreateExternalDatasourceModal/DatasourceConfigEditor/fields/LongForm.svelte b/packages/builder/src/pages/builder/app/[application]/data/_components/CreateExternalDatasourceModal/DatasourceConfigEditor/fields/LongForm.svelte new file mode 100644 index 0000000000..efbeb007e8 --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/data/_components/CreateExternalDatasourceModal/DatasourceConfigEditor/fields/LongForm.svelte @@ -0,0 +1,22 @@ + + +
+ +