1
0
Fork 0
mirror of synced 2024-07-04 22:11:23 +12:00

Merge remote-tracking branch 'origin/develop' into feature/whitelabelling

This commit is contained in:
Dean 2023-03-15 09:13:36 +00:00
commit f3b58923cf
69 changed files with 685 additions and 496 deletions

View file

@ -1,6 +1,10 @@
name: "deploy-preprod" name: "deploy-preprod"
on: on:
workflow_dispatch: workflow_dispatch:
inputs:
version:
description: Budibase release version. For example - 1.0.0
required: false
workflow_call: workflow_call:
jobs: jobs:
@ -8,10 +12,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: 'Get Previous tag'
id: previoustag
uses: "WyriHaximus/github-action-get-previous-tag@v1"
- name: Get the latest budibase release version
id: version
run: |
if [ -z "${{ github.event.inputs.version }}" ]; then
release_version=$(cat lerna.json | jq -r '.version')
else
release_version=${{ github.event.inputs.version }}
fi
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
- name: Configure AWS Credentials - name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1 uses: aws-actions/configure-aws-credentials@v1
with: with:
@ -26,7 +36,6 @@ jobs:
-o values.preprod.yaml \ -o values.preprod.yaml \
-L https://api.github.com/repos/budibase/budibase-infra/contents/kubernetes/budibase-preprod/values.yaml -L https://api.github.com/repos/budibase/budibase-infra/contents/kubernetes/budibase-preprod/values.yaml
wc -l values.preprod.yaml wc -l values.preprod.yaml
- name: Deploy to Preprod Environment - name: Deploy to Preprod Environment
uses: budibase/helm@v1.8.0 uses: budibase/helm@v1.8.0
with: with:
@ -37,7 +46,7 @@ jobs:
helm: helm3 helm: helm3
values: | values: |
globals: globals:
appVersion: ${{ steps.previoustag.outputs.tag }} appVersion: v${{ env.RELEASE_VERSION }}
ingress: ingress:
enabled: true enabled: true
nginx: true nginx: true
@ -52,5 +61,5 @@ jobs:
uses: tsickert/discord-webhook@v4.0.0 uses: tsickert/discord-webhook@v4.0.0
with: with:
webhook-url: ${{ secrets.PROD_DEPLOY_WEBHOOK_URL }} webhook-url: ${{ secrets.PROD_DEPLOY_WEBHOOK_URL }}
content: "Preprod Deployment Complete: ${{ steps.previoustag.outputs.tag }} deployed to Budibase Pre-prod." content: "Preprod Deployment Complete: ${{ env.RELEASE_VERSION }} deployed to Budibase Pre-prod."
embed-title: ${{ steps.previoustag.outputs.tag }} embed-title: ${{ env.RELEASE_VERSION }}

View file

@ -91,9 +91,11 @@ jobs:
uses: azure/setup-helm@v1 uses: azure/setup-helm@v1
id: helm-install id: helm-install
- name: 'Get Previous tag' - name: Get the latest budibase release version
id: previoustag id: version
uses: "WyriHaximus/github-action-get-previous-tag@v1" run: |
release_version=$(cat lerna.json | jq -r '.version')
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
# due to helm repo index issue: https://github.com/helm/helm/issues/7363 # due to helm repo index issue: https://github.com/helm/helm/issues/7363
# we need to create new package in a different dir, merge the index and move the package back # we need to create new package in a different dir, merge the index and move the package back
@ -116,8 +118,6 @@ jobs:
git add -A git add -A
git commit -m "Helm Release: ${{ env.RELEASE_VERSION }}" git commit -m "Helm Release: ${{ env.RELEASE_VERSION }}"
git push git push
env:
RELEASE_VERSION: ${{ steps.previoustag.outputs.tag }}
deploy-to-legacy-preprod-env: deploy-to-legacy-preprod-env:
needs: [release-images] needs: [release-images]
@ -130,13 +130,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: 'Get Previous tag'
id: previoustag - name: Get the latest budibase release version
uses: "WyriHaximus/github-action-get-previous-tag@v1" id: version
run: |
release_version=$(cat lerna.json | jq -r '.version')
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
- uses: passeidireto/trigger-external-workflow-action@main - uses: passeidireto/trigger-external-workflow-action@main
env: env:
PAYLOAD_VERSION: ${{ steps.previoustag.outputs.tag }} PAYLOAD_VERSION: ${{ env.RELEASE_VERSION }}
with: with:
repository: budibase/budibase-deploys repository: budibase/budibase-deploys
event: budicloud-preprod-deploy event: budicloud-preprod-deploy

View file

@ -62,16 +62,22 @@ spec:
{{ end }} {{ end }}
- name: ENABLE_ANALYTICS - name: ENABLE_ANALYTICS
value: {{ .Values.globals.enableAnalytics | quote }} value: {{ .Values.globals.enableAnalytics | quote }}
- name: API_ENCRYPTION_KEY
value: {{ .Values.globals.apiEncryptionKey | quote }}
- name: INTERNAL_API_KEY - name: INTERNAL_API_KEY
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: {{ template "budibase.fullname" . }} name: {{ template "budibase.fullname" . }}
key: internalApiKey key: internalApiKey
- name: INTERNAL_API_KEY_FALLBACK
value: {{ .Values.globals.internalApiKeyFallback | quote }}
- name: JWT_SECRET - name: JWT_SECRET
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: {{ template "budibase.fullname" . }} name: {{ template "budibase.fullname" . }}
key: jwtSecret key: jwtSecret
- name: JWT_SECRET_FALLBACK
value: {{ .Values.globals.jwtSecretFallback | quote }}
{{ if .Values.services.objectStore.region }} {{ if .Values.services.objectStore.region }}
- name: AWS_REGION - name: AWS_REGION
value: {{ .Values.services.objectStore.region }} value: {{ .Values.services.objectStore.region }}
@ -125,9 +131,9 @@ spec:
- name: SELF_HOSTED - name: SELF_HOSTED
value: {{ .Values.globals.selfHosted | quote }} value: {{ .Values.globals.selfHosted | quote }}
- name: SENTRY_DSN - name: SENTRY_DSN
value: {{ .Values.globals.sentryDSN }} value: {{ .Values.globals.sentryDSN | quote }}
- name: POSTHOG_TOKEN - name: POSTHOG_TOKEN
value: {{ .Values.globals.posthogToken }} value: {{ .Values.globals.posthogToken | quote }}
- name: WORKER_URL - name: WORKER_URL
value: http://worker-service:{{ .Values.services.worker.port }} value: http://worker-service:{{ .Values.services.worker.port }}
- name: PLATFORM_URL - name: PLATFORM_URL
@ -198,8 +204,6 @@ spec:
- name: GLOBAL_AGENT_NO_PROXY - name: GLOBAL_AGENT_NO_PROXY
value: {{ .Values.globals.globalAgentNoProxy | quote }} value: {{ .Values.globals.globalAgentNoProxy | quote }}
{{ end }} {{ end }}
- name: CDN_URL
value: {{ .Values.globals.cdnUrl }}
{{ if .Values.services.tlsRejectUnauthorized }} {{ if .Values.services.tlsRejectUnauthorized }}
- name: NODE_TLS_REJECT_UNAUTHORIZED - name: NODE_TLS_REJECT_UNAUTHORIZED
value: {{ .Values.services.tlsRejectUnauthorized }} value: {{ .Values.services.tlsRejectUnauthorized }}

View file

@ -62,16 +62,22 @@ spec:
{{ else }} {{ else }}
value: http://{{ .Release.Name }}-svc-couchdb:{{ .Values.services.couchdb.port }} value: http://{{ .Release.Name }}-svc-couchdb:{{ .Values.services.couchdb.port }}
{{ end }} {{ end }}
- name: API_ENCRYPTION_KEY
value: {{ .Values.globals.apiEncryptionKey | quote }}
- name: INTERNAL_API_KEY - name: INTERNAL_API_KEY
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: {{ template "budibase.fullname" . }} name: {{ template "budibase.fullname" . }}
key: internalApiKey key: internalApiKey
- name: INTERNAL_API_KEY_FALLBACK
value: {{ .Values.globals.internalApiKeyFallback | quote }}
- name: JWT_SECRET - name: JWT_SECRET
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: {{ template "budibase.fullname" . }} name: {{ template "budibase.fullname" . }}
key: jwtSecret key: jwtSecret
- name: JWT_SECRET_FALLBACK
value: {{ .Values.globals.jwtSecretFallback | quote }}
{{ if .Values.services.objectStore.region }} {{ if .Values.services.objectStore.region }}
- name: AWS_REGION - name: AWS_REGION
value: {{ .Values.services.objectStore.region }} value: {{ .Values.services.objectStore.region }}
@ -188,8 +194,6 @@ spec:
- name: GLOBAL_AGENT_NO_PROXY - name: GLOBAL_AGENT_NO_PROXY
value: {{ .Values.globals.globalAgentNoProxy | quote }} value: {{ .Values.globals.globalAgentNoProxy | quote }}
{{ end }} {{ end }}
- name: CDN_URL
value: {{ .Values.globals.cdnUrl }}
{{ if .Values.services.tlsRejectUnauthorized }} {{ if .Values.services.tlsRejectUnauthorized }}
- name: NODE_TLS_REJECT_UNAUTHORIZED - name: NODE_TLS_REJECT_UNAUTHORIZED
value: {{ .Values.services.tlsRejectUnauthorized }} value: {{ .Values.services.tlsRejectUnauthorized }}

View file

@ -96,9 +96,13 @@ globals:
createSecrets: true # creates an internal API key, JWT secrets and redis password for you createSecrets: true # creates an internal API key, JWT secrets and redis password for you
# if createSecrets is set to false, you can hard-code your secrets here # if createSecrets is set to false, you can hard-code your secrets here
apiEncryptionKey: ""
internalApiKey: "" internalApiKey: ""
jwtSecret: "" jwtSecret: ""
cdnUrl: "" cdnUrl: ""
# fallback values used during live rotation
internalApiKeyFallback: ""
jwtSecretFallback: ""
smtp: smtp:
enabled: false enabled: false

View file

@ -3,6 +3,7 @@ MAIN_PORT=10000
# This section contains all secrets pertaining to the system # This section contains all secrets pertaining to the system
# These should be updated # These should be updated
API_ENCRYPTION_KEY=testsecret
JWT_SECRET=testsecret JWT_SECRET=testsecret
MINIO_ACCESS_KEY=budibase MINIO_ACCESS_KEY=budibase
MINIO_SECRET_KEY=budibase MINIO_SECRET_KEY=budibase

View file

@ -17,6 +17,7 @@ services:
INTERNAL_API_KEY: ${INTERNAL_API_KEY} INTERNAL_API_KEY: ${INTERNAL_API_KEY}
BUDIBASE_ENVIRONMENT: ${BUDIBASE_ENVIRONMENT} BUDIBASE_ENVIRONMENT: ${BUDIBASE_ENVIRONMENT}
PORT: 4002 PORT: 4002
API_ENCRYPTION_KEY: ${API_ENCRYPTION_KEY}
JWT_SECRET: ${JWT_SECRET} JWT_SECRET: ${JWT_SECRET}
LOG_LEVEL: info LOG_LEVEL: info
SENTRY_DSN: https://a34ae347621946bf8acded18e5b7d4b8@o420233.ingest.sentry.io/5338131 SENTRY_DSN: https://a34ae347621946bf8acded18e5b7d4b8@o420233.ingest.sentry.io/5338131
@ -40,6 +41,7 @@ services:
SELF_HOSTED: 1 SELF_HOSTED: 1
PORT: 4003 PORT: 4003
CLUSTER_PORT: ${MAIN_PORT} CLUSTER_PORT: ${MAIN_PORT}
API_ENCRYPTION_KEY: ${API_ENCRYPTION_KEY}
JWT_SECRET: ${JWT_SECRET} JWT_SECRET: ${JWT_SECRET}
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY} MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY} MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}

View file

@ -3,6 +3,7 @@ MAIN_PORT=10000
# This section contains all secrets pertaining to the system # This section contains all secrets pertaining to the system
# These should be updated # These should be updated
API_ENCRYPTION_KEY=testsecret
JWT_SECRET=testsecret JWT_SECRET=testsecret
MINIO_ACCESS_KEY=budibase MINIO_ACCESS_KEY=budibase
MINIO_SECRET_KEY=budibase MINIO_SECRET_KEY=budibase

View file

@ -1,5 +1,5 @@
{ {
"version": "2.4.12-alpha.5", "version": "2.4.26",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/backend-core", "name": "@budibase/backend-core",
"version": "2.4.12-alpha.5", "version": "2.4.26",
"description": "Budibase backend core libraries used in server and worker", "description": "Budibase backend core libraries used in server and worker",
"main": "dist/src/index.js", "main": "dist/src/index.js",
"types": "dist/src/index.d.ts", "types": "dist/src/index.d.ts",
@ -24,7 +24,7 @@
"dependencies": { "dependencies": {
"@budibase/nano": "10.1.2", "@budibase/nano": "10.1.2",
"@budibase/pouchdb-replication-stream": "1.2.10", "@budibase/pouchdb-replication-stream": "1.2.10",
"@budibase/types": "2.4.12-alpha.5", "@budibase/types": "^2.4.26",
"@shopify/jest-koa-mocks": "5.0.1", "@shopify/jest-koa-mocks": "5.0.1",
"@techpass/passport-openidconnect": "0.3.2", "@techpass/passport-openidconnect": "0.3.2",
"aws-cloudfront-sign": "2.2.0", "aws-cloudfront-sign": "2.2.0",

View file

@ -1,6 +1,5 @@
const _passport = require("koa-passport") const _passport = require("koa-passport")
const LocalStrategy = require("passport-local").Strategy const LocalStrategy = require("passport-local").Strategy
const JwtStrategy = require("passport-jwt").Strategy
import { getGlobalDB } from "../context" import { getGlobalDB } from "../context"
import { Cookie } from "../constants" import { Cookie } from "../constants"
import { getSessionsForUser, invalidateSessions } from "../security/sessions" import { getSessionsForUser, invalidateSessions } from "../security/sessions"
@ -8,7 +7,6 @@ import {
authenticated, authenticated,
csrf, csrf,
google, google,
jwt as jwtPassport,
local, local,
oidc, oidc,
tenancy, tenancy,
@ -21,14 +19,11 @@ import {
OIDCInnerConfig, OIDCInnerConfig,
PlatformLogoutOpts, PlatformLogoutOpts,
SSOProviderType, SSOProviderType,
User,
} from "@budibase/types" } from "@budibase/types"
import { logAlert } from "../logging"
import * as events from "../events" import * as events from "../events"
import * as configs from "../configs" import * as configs from "../configs"
import { clearCookie, getCookie } from "../utils" import { clearCookie, getCookie } from "../utils"
import { ssoSaveUserNoOp } from "../middleware/passport/sso/sso" import { ssoSaveUserNoOp } from "../middleware/passport/sso/sso"
import env from "../environment"
const refresh = require("passport-oauth2-refresh") const refresh = require("passport-oauth2-refresh")
export { export {
@ -51,25 +46,6 @@ export const jwt = require("jsonwebtoken")
// Strategies // Strategies
_passport.use(new LocalStrategy(local.options, local.authenticate)) _passport.use(new LocalStrategy(local.options, local.authenticate))
if (jwtPassport.options.secretOrKey) {
_passport.use(new JwtStrategy(jwtPassport.options, jwtPassport.authenticate))
} else if (!env.DISABLE_JWT_WARNING) {
logAlert("No JWT Secret supplied, cannot configure JWT strategy")
}
_passport.serializeUser((user: User, done: any) => done(null, user))
_passport.deserializeUser(async (user: User, done: any) => {
const db = getGlobalDB()
try {
const dbUser = await db.get(user._id)
return done(null, dbUser)
} catch (err) {
console.error(`User not found`, err)
return done(null, false, { message: "User not found" })
}
})
async function refreshOIDCAccessToken( async function refreshOIDCAccessToken(
chosenConfig: OIDCInnerConfig, chosenConfig: OIDCInnerConfig,

View file

@ -30,6 +30,12 @@ const DefaultBucketName = {
const selfHosted = !!parseInt(process.env.SELF_HOSTED || "") const selfHosted = !!parseInt(process.env.SELF_HOSTED || "")
function getAPIEncryptionKey() {
return process.env.API_ENCRYPTION_KEY
? process.env.API_ENCRYPTION_KEY
: process.env.JWT_SECRET // fallback to the JWT_SECRET used historically
}
const environment = { const environment = {
isTest, isTest,
isJest, isJest,
@ -39,7 +45,9 @@ const environment = {
}, },
JS_BCRYPT: process.env.JS_BCRYPT, JS_BCRYPT: process.env.JS_BCRYPT,
JWT_SECRET: process.env.JWT_SECRET, JWT_SECRET: process.env.JWT_SECRET,
JWT_SECRET_FALLBACK: process.env.JWT_SECRET_FALLBACK,
ENCRYPTION_KEY: process.env.ENCRYPTION_KEY, ENCRYPTION_KEY: process.env.ENCRYPTION_KEY,
API_ENCRYPTION_KEY: getAPIEncryptionKey(),
COUCH_DB_URL: process.env.COUCH_DB_URL || "http://localhost:4005", COUCH_DB_URL: process.env.COUCH_DB_URL || "http://localhost:4005",
COUCH_DB_USERNAME: process.env.COUCH_DB_USER, COUCH_DB_USERNAME: process.env.COUCH_DB_USER,
COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD, COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD,
@ -55,6 +63,7 @@ const environment = {
MINIO_URL: process.env.MINIO_URL, MINIO_URL: process.env.MINIO_URL,
MINIO_ENABLED: process.env.MINIO_ENABLED || 1, MINIO_ENABLED: process.env.MINIO_ENABLED || 1,
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY, INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
INTERNAL_API_KEY_FALLBACK: process.env.INTERNAL_API_KEY_FALLBACK,
MULTI_TENANCY: process.env.MULTI_TENANCY, MULTI_TENANCY: process.env.MULTI_TENANCY,
ACCOUNT_PORTAL_URL: ACCOUNT_PORTAL_URL:
process.env.ACCOUNT_PORTAL_URL || "https://account.budibase.app", process.env.ACCOUNT_PORTAL_URL || "https://account.budibase.app",

View file

@ -1,5 +1,10 @@
import { Cookie, Header } from "../constants" import { Cookie, Header } from "../constants"
import { getCookie, clearCookie, openJwt } from "../utils" import {
getCookie,
clearCookie,
openJwt,
isValidInternalAPIKey,
} from "../utils"
import { getUser } from "../cache/user" import { getUser } from "../cache/user"
import { getSession, updateSessionTTL } from "../security/sessions" import { getSession, updateSessionTTL } from "../security/sessions"
import { buildMatcherRegex, matches } from "./matchers" import { buildMatcherRegex, matches } from "./matchers"
@ -35,7 +40,9 @@ function finalise(ctx: any, opts: FinaliseOpts = {}) {
} }
async function checkApiKey(apiKey: string, populateUser?: Function) { async function checkApiKey(apiKey: string, populateUser?: Function) {
if (apiKey === env.INTERNAL_API_KEY) { // check both the primary and the fallback internal api keys
// this allows for rotation
if (isValidInternalAPIKey(apiKey)) {
return { valid: true } return { valid: true }
} }
const decrypted = decrypt(apiKey) const decrypted = decrypt(apiKey)

View file

@ -1,4 +1,3 @@
export * as jwt from "./passport/jwt"
export * as local from "./passport/local" export * as local from "./passport/local"
export * as google from "./passport/sso/google" export * as google from "./passport/sso/google"
export * as oidc from "./passport/sso/oidc" export * as oidc from "./passport/sso/oidc"

View file

@ -1,13 +1,21 @@
import env from "../environment"
import { Header } from "../constants" import { Header } from "../constants"
import { BBContext } from "@budibase/types" import { BBContext } from "@budibase/types"
import { isValidInternalAPIKey } from "../utils"
/** /**
* API Key only endpoint. * API Key only endpoint.
*/ */
export default async (ctx: BBContext, next: any) => { export default async (ctx: BBContext, next: any) => {
const apiKey = ctx.request.headers[Header.API_KEY] const apiKey = ctx.request.headers[Header.API_KEY]
if (apiKey !== env.INTERNAL_API_KEY) { if (!apiKey) {
ctx.throw(403, "Unauthorized")
}
if (Array.isArray(apiKey)) {
ctx.throw(403, "Unauthorized")
}
if (!isValidInternalAPIKey(apiKey)) {
ctx.throw(403, "Unauthorized") ctx.throw(403, "Unauthorized")
} }

View file

@ -1,19 +0,0 @@
import { Cookie } from "../../constants"
import env from "../../environment"
import { authError } from "./utils"
import { BBContext } from "@budibase/types"
export const options = {
secretOrKey: env.JWT_SECRET,
jwtFromRequest: function (ctx: BBContext) {
return ctx.cookies.get(Cookie.Auth)
},
}
export async function authenticate(jwt: Function, done: Function) {
try {
return done(null, jwt)
} catch (err) {
return authError(done, "JWT invalid", err)
}
}

View file

@ -8,7 +8,7 @@ const RANDOM_BYTES = 16
const STRETCH_LENGTH = 32 const STRETCH_LENGTH = 32
export enum SecretOption { export enum SecretOption {
JWT = "jwt", API = "api",
ENCRYPTION = "encryption", ENCRYPTION = "encryption",
} }
@ -19,10 +19,10 @@ function getSecret(secretOption: SecretOption): string {
secret = env.ENCRYPTION_KEY secret = env.ENCRYPTION_KEY
secretName = "ENCRYPTION_KEY" secretName = "ENCRYPTION_KEY"
break break
case SecretOption.JWT: case SecretOption.API:
default: default:
secret = env.JWT_SECRET secret = env.API_ENCRYPTION_KEY
secretName = "JWT_SECRET" secretName = "API_ENCRYPTION_KEY"
break break
} }
if (!secret) { if (!secret) {
@ -37,7 +37,7 @@ function stretchString(string: string, salt: Buffer) {
export function encrypt( export function encrypt(
input: string, input: string,
secretOption: SecretOption = SecretOption.JWT secretOption: SecretOption = SecretOption.API
) { ) {
const salt = crypto.randomBytes(RANDOM_BYTES) const salt = crypto.randomBytes(RANDOM_BYTES)
const stretched = stretchString(getSecret(secretOption), salt) const stretched = stretchString(getSecret(secretOption), salt)
@ -50,7 +50,7 @@ export function encrypt(
export function decrypt( export function decrypt(
input: string, input: string,
secretOption: SecretOption = SecretOption.JWT secretOption: SecretOption = SecretOption.API
) { ) {
const [salt, encrypted] = input.split(SEPARATOR) const [salt, encrypted] = input.split(SEPARATOR)
const saltBuffer = Buffer.from(salt, "hex") const saltBuffer = Buffer.from(salt, "hex")

View file

@ -1,5 +1,4 @@
import { getAllApps, queryGlobalView } from "../db" import { getAllApps, queryGlobalView } from "../db"
import { options } from "../middleware/passport/jwt"
import { import {
Header, Header,
MAX_VALID_DATE, MAX_VALID_DATE,
@ -133,7 +132,30 @@ export function openJwt(token: string) {
if (!token) { if (!token) {
return token return token
} }
return jwt.verify(token, options.secretOrKey) try {
return jwt.verify(token, env.JWT_SECRET)
} catch (e) {
if (env.JWT_SECRET_FALLBACK) {
// fallback to enable rotation
return jwt.verify(token, env.JWT_SECRET_FALLBACK)
} else {
throw e
}
}
}
export function isValidInternalAPIKey(apiKey: string) {
if (env.INTERNAL_API_KEY && env.INTERNAL_API_KEY === apiKey) {
return true
}
// fallback to enable rotation
if (
env.INTERNAL_API_KEY_FALLBACK &&
env.INTERNAL_API_KEY_FALLBACK === apiKey
) {
return true
}
return false
} }
/** /**
@ -165,7 +187,7 @@ export function setCookie(
opts = { sign: true } opts = { sign: true }
) { ) {
if (value && opts && opts.sign) { if (value && opts && opts.sign) {
value = jwt.sign(value, options.secretOrKey) value = jwt.sign(value, env.JWT_SECRET)
} }
const config: SetOption = { const config: SetOption = {

View file

@ -1,7 +1,7 @@
{ {
"name": "@budibase/bbui", "name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.", "description": "A UI solution used in the different Budibase projects.",
"version": "2.4.12-alpha.5", "version": "2.4.26",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",
@ -38,8 +38,8 @@
], ],
"dependencies": { "dependencies": {
"@adobe/spectrum-css-workflow-icons": "1.2.1", "@adobe/spectrum-css-workflow-icons": "1.2.1",
"@budibase/shared-core": "2.4.12-alpha.5", "@budibase/shared-core": "^2.4.26",
"@budibase/string-templates": "2.4.12-alpha.5", "@budibase/string-templates": "^2.4.26",
"@spectrum-css/accordion": "3.0.24", "@spectrum-css/accordion": "3.0.24",
"@spectrum-css/actionbutton": "1.0.1", "@spectrum-css/actionbutton": "1.0.1",
"@spectrum-css/actiongroup": "1.0.1", "@spectrum-css/actiongroup": "1.0.1",

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "2.4.12-alpha.5", "version": "2.4.26",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -58,11 +58,11 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "2.4.12-alpha.5", "@budibase/bbui": "^2.4.26",
"@budibase/client": "2.4.12-alpha.5", "@budibase/client": "^2.4.26",
"@budibase/frontend-core": "2.4.12-alpha.5", "@budibase/frontend-core": "^2.4.26",
"@budibase/shared-core": "2.4.12-alpha.5", "@budibase/shared-core": "^2.4.26",
"@budibase/string-templates": "2.4.12-alpha.5", "@budibase/string-templates": "^2.4.26",
"@fortawesome/fontawesome-svg-core": "^6.2.1", "@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-brands-svg-icons": "^6.2.1", "@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1", "@fortawesome/free-solid-svg-icons": "^6.2.1",

View file

@ -74,6 +74,14 @@
} }
return capitalise(name) return capitalise(name)
} }
function getDisplayError(error, configKey) {
return error?.replace(
new RegExp(`${configKey}`, "i"),
getDisplayName(configKey)
)
}
function getFieldGroupKeys(fieldGroup) { function getFieldGroupKeys(fieldGroup) {
return Object.entries(schema[fieldGroup].fields || {}) return Object.entries(schema[fieldGroup].fields || {})
.filter(el => filter(el)) .filter(el => filter(el))
@ -147,7 +155,7 @@
type={schema[configKey].type} type={schema[configKey].type}
on:change on:change
bind:value={config[configKey]} bind:value={config[configKey]}
error={$validation.errors[configKey]} error={getDisplayError($validation.errors[configKey], configKey)}
/> />
</div> </div>
{:else if schema[configKey].type === "fieldGroup"} {:else if schema[configKey].type === "fieldGroup"}
@ -180,7 +188,7 @@
type={configKey === "port" ? "string" : schema[configKey].type} type={configKey === "port" ? "string" : schema[configKey].type}
on:change on:change
bind:value={config[configKey]} bind:value={config[configKey]}
error={$validation.errors[configKey]} error={getDisplayError($validation.errors[configKey], configKey)}
environmentVariablesEnabled={$licensing.environmentVariablesEnabled} environmentVariablesEnabled={$licensing.environmentVariablesEnabled}
{handleUpgradePanel} {handleUpgradePanel}
/> />

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "2.4.12-alpha.5", "version": "2.4.26",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "dist/index.js", "main": "dist/index.js",
"bin": { "bin": {
@ -29,9 +29,9 @@
"outputPath": "build" "outputPath": "build"
}, },
"dependencies": { "dependencies": {
"@budibase/backend-core": "2.4.12-alpha.5", "@budibase/backend-core": "^2.4.26",
"@budibase/string-templates": "2.4.12-alpha.5", "@budibase/string-templates": "^2.4.26",
"@budibase/types": "2.4.12-alpha.5", "@budibase/types": "^2.4.26",
"axios": "0.21.2", "axios": "0.21.2",
"chalk": "4.1.0", "chalk": "4.1.0",
"cli-progress": "3.11.2", "cli-progress": "3.11.2",

View file

@ -13,6 +13,7 @@ export const ENV_PATH = path.resolve("./.env")
function getSecrets(opts = { single: false }) { function getSecrets(opts = { single: false }) {
const secrets = [ const secrets = [
"API_ENCRYPTION_KEY",
"JWT_SECRET", "JWT_SECRET",
"MINIO_ACCESS_KEY", "MINIO_ACCESS_KEY",
"MINIO_SECRET_KEY", "MINIO_SECRET_KEY",

View file

@ -4,6 +4,8 @@ import {
downloadDockerCompose, downloadDockerCompose,
handleError, handleError,
getServices, getServices,
getServiceImage,
setServiceImage,
} from "./utils" } from "./utils"
import { confirmation } from "../questions" import { confirmation } from "../questions"
import compose from "docker-compose" import compose from "docker-compose"
@ -23,7 +25,11 @@ export async function update() {
!isSingle && !isSingle &&
(await confirmation("Do you wish to update you docker-compose.yaml?")) (await confirmation("Do you wish to update you docker-compose.yaml?"))
) { ) {
// get current MinIO image
const image = await getServiceImage("minio")
await downloadDockerCompose() await downloadDockerCompose()
// replace MinIO image
setServiceImage("minio", image)
} }
await handleError(async () => { await handleError(async () => {
const status = await compose.ps() const status = await compose.ps()

View file

@ -9,10 +9,44 @@ const ERROR_FILE = "docker-error.log"
const COMPOSE_URL = const COMPOSE_URL =
"https://raw.githubusercontent.com/Budibase/budibase/master/hosting/docker-compose.yaml" "https://raw.githubusercontent.com/Budibase/budibase/master/hosting/docker-compose.yaml"
export async function downloadDockerCompose() { function composeFilename() {
const fileName = COMPOSE_URL.split("/").slice(-1)[0] return COMPOSE_URL.split("/").slice(-1)[0]
}
export function getServiceImage(service: string) {
const filename = composeFilename()
try { try {
await downloadFile(COMPOSE_URL, `./${fileName}`) const { services } = getServices(filename)
const serviceKey = Object.keys(services).find(name =>
name.includes(service)
)
if (serviceKey) {
return services[serviceKey].image
} else {
return null
}
} catch (err) {
return null
}
}
export function setServiceImage(service: string, image: string) {
const filename = composeFilename()
if (!fs.existsSync(filename)) {
throw new Error(
`File ${filename} not found, cannot update ${service} image.`
)
}
const current = getServiceImage(service)!
let contents = fs.readFileSync(filename, "utf8")
contents = contents.replace(`image: ${current}`, `image: ${image}`)
fs.writeFileSync(filename, contents)
}
export async function downloadDockerCompose() {
const filename = composeFilename()
try {
await downloadFile(COMPOSE_URL, `./${filename}`)
} catch (err) { } catch (err) {
console.error(error(`Failed to retrieve compose file - ${err}`)) console.error(error(`Failed to retrieve compose file - ${err}`))
} }
@ -49,6 +83,9 @@ export async function handleError(func: Function) {
} }
export function getServices(path: string) { export function getServices(path: string) {
if (!fs.existsSync(path)) {
throw new Error(`No yaml found at path: ${path}`)
}
const dockerYaml = fs.readFileSync(path, "utf8") const dockerYaml = fs.readFileSync(path, "utf8")
const parsedYaml = yaml.parse(dockerYaml) const parsedYaml = yaml.parse(dockerYaml)
return { yaml: parsedYaml, services: parsedYaml.services } return { yaml: parsedYaml, services: parsedYaml.services }

View file

@ -17,10 +17,7 @@
"description": "This component is specific only to layouts", "description": "This component is specific only to layouts",
"icon": "Sandbox", "icon": "Sandbox",
"hasChildren": true, "hasChildren": true,
"styles": [ "styles": ["padding", "background"],
"padding",
"background"
],
"settings": [ "settings": [
{ {
"type": "text", "type": "text",
@ -36,23 +33,14 @@
"type": "select", "type": "select",
"label": "Navigation", "label": "Navigation",
"key": "navigation", "key": "navigation",
"options": [ "options": ["Top", "Left", "None"],
"Top",
"Left",
"None"
],
"defaultValue": "Top" "defaultValue": "Top"
}, },
{ {
"type": "select", "type": "select",
"label": "Width", "label": "Width",
"key": "width", "key": "width",
"options": [ "options": ["Small", "Medium", "Large", "Max"],
"Small",
"Medium",
"Large",
"Max"
],
"defaultValue": "Large" "defaultValue": "Large"
}, },
{ {
@ -89,13 +77,7 @@
"width": 400, "width": 400,
"height": 200 "height": 200
}, },
"styles": [ "styles": ["padding", "size", "background", "border", "shadow"],
"padding",
"size",
"background",
"border",
"shadow"
],
"settings": [ "settings": [
{ {
"type": "select", "type": "select",
@ -255,9 +237,7 @@
"description": "Add a section to your application", "description": "Add a section to your application",
"icon": "ColumnTwoB", "icon": "ColumnTwoB",
"hasChildren": true, "hasChildren": true,
"illegalChildren": [ "illegalChildren": ["section"],
"section"
],
"showEmptyState": false, "showEmptyState": false,
"size": { "size": {
"width": 400, "width": 400,
@ -376,9 +356,7 @@
"name": "Divider", "name": "Divider",
"description": "A basic divider", "description": "A basic divider",
"icon": "Separator", "icon": "Separator",
"illegalChildren": [ "illegalChildren": ["section"],
"section"
],
"size": { "size": {
"width": 400, "width": 400,
"height": 10 "height": 10
@ -415,9 +393,7 @@
"name": "Repeater", "name": "Repeater",
"description": "A configurable data list that attaches to your backend tables.", "description": "A configurable data list that attaches to your backend tables.",
"icon": "JourneyData", "icon": "JourneyData",
"illegalChildren": [ "illegalChildren": ["section"],
"section"
],
"hasChildren": true, "hasChildren": true,
"size": { "size": {
"width": 400, "width": 400,
@ -574,9 +550,7 @@
"name": "Stacked List", "name": "Stacked List",
"icon": "TaskList", "icon": "TaskList",
"description": "A basic card component that can contain content and actions.", "description": "A basic card component that can contain content and actions.",
"illegalChildren": [ "illegalChildren": ["section"],
"section"
],
"settings": [ "settings": [
{ {
"type": "text", "type": "text",
@ -606,9 +580,7 @@
"name": "Vertical Card", "name": "Vertical Card",
"description": "A basic card component that can contain content and actions.", "description": "A basic card component that can contain content and actions.",
"icon": "ViewColumn", "icon": "ViewColumn",
"illegalChildren": [ "illegalChildren": ["section"],
"section"
],
"settings": [ "settings": [
{ {
"type": "text", "type": "text",
@ -652,24 +624,14 @@
"type": "select", "type": "select",
"label": "Image Height", "label": "Image Height",
"key": "imageHeight", "key": "imageHeight",
"options": [ "options": ["auto", "12rem", "16rem", "20rem", "24rem"],
"auto",
"12rem",
"16rem",
"20rem",
"24rem"
],
"defaultValue": "auto" "defaultValue": "auto"
}, },
{ {
"type": "select", "type": "select",
"label": "Card Width", "label": "Card Width",
"key": "cardWidth", "key": "cardWidth",
"options": [ "options": ["16rem", "20rem", "24rem"],
"16rem",
"20rem",
"24rem"
],
"defaultValue": "20rem" "defaultValue": "20rem"
} }
] ]
@ -678,9 +640,7 @@
"name": "Paragraph", "name": "Paragraph",
"description": "A component for displaying paragraph text.", "description": "A component for displaying paragraph text.",
"icon": "TextParagraph", "icon": "TextParagraph",
"illegalChildren": [ "illegalChildren": ["section"],
"section"
],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -803,9 +763,7 @@
"name": "Headline", "name": "Headline",
"icon": "TextBold", "icon": "TextBold",
"description": "A component for displaying heading text", "description": "A component for displaying heading text",
"illegalChildren": [ "illegalChildren": ["section"],
"section"
],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -982,9 +940,7 @@
"name": "Image", "name": "Image",
"description": "A basic component for displaying images", "description": "A basic component for displaying images",
"icon": "Image", "icon": "Image",
"styles": [ "styles": ["size"],
"size"
],
"size": { "size": {
"width": 400, "width": 400,
"height": 300 "height": 300
@ -1002,9 +958,8 @@
"name": "Background Image", "name": "Background Image",
"description": "A background image", "description": "A background image",
"icon": "Images", "icon": "Images",
"styles": [ "hasChildren": true,
"size" "styles": ["size"],
],
"size": { "size": {
"width": 400, "width": 400,
"height": 300 "height": 300
@ -1162,9 +1117,7 @@
"name": "Nav Bar", "name": "Nav Bar",
"description": "A component for handling the navigation within your app.", "description": "A component for handling the navigation within your app.",
"icon": "BreadcrumbNavigation", "icon": "BreadcrumbNavigation",
"illegalChildren": [ "illegalChildren": ["section"],
"section"
],
"hasChildren": true, "hasChildren": true,
"settings": [ "settings": [
{ {
@ -1365,25 +1318,14 @@
"type": "select", "type": "select",
"label": "Image Width", "label": "Image Width",
"key": "imageWidth", "key": "imageWidth",
"options": [ "options": ["auto", "8rem", "12rem", "16rem"],
"auto",
"8rem",
"12rem",
"16rem"
],
"defaultValue": "8rem" "defaultValue": "8rem"
}, },
{ {
"type": "select", "type": "select",
"label": "Image Height", "label": "Image Height",
"key": "imageHeight", "key": "imageHeight",
"options": [ "options": ["auto", "8rem", "12rem", "16rem", "auto"],
"auto",
"8rem",
"12rem",
"16rem",
"auto"
],
"defaultValue": "auto" "defaultValue": "auto"
} }
] ]
@ -1424,9 +1366,7 @@
"name": "Embed", "name": "Embed",
"icon": "Code", "icon": "Code",
"description": "Embed content from 3rd party sources", "description": "Embed content from 3rd party sources",
"styles": [ "styles": ["size"],
"size"
],
"size": { "size": {
"width": 400, "width": 400,
"height": 100 "height": 100
@ -1478,11 +1418,7 @@
"type": "select", "type": "select",
"label": "Format", "label": "Format",
"key": "yAxisUnits", "key": "yAxisUnits",
"options": [ "options": ["Default", "Thousands", "Millions"],
"Default",
"Thousands",
"Millions"
],
"defaultValue": "Default" "defaultValue": "Default"
}, },
{ {
@ -1640,11 +1576,7 @@
"type": "select", "type": "select",
"label": "Format", "label": "Format",
"key": "yAxisUnits", "key": "yAxisUnits",
"options": [ "options": ["Default", "Thousands", "Millions"],
"Default",
"Thousands",
"Millions"
],
"defaultValue": "Default" "defaultValue": "Default"
}, },
{ {
@ -1736,11 +1668,7 @@
"type": "select", "type": "select",
"label": "Curve", "label": "Curve",
"key": "curve", "key": "curve",
"options": [ "options": ["Smooth", "Straight", "Stepline"],
"Smooth",
"Straight",
"Stepline"
],
"defaultValue": "Smooth" "defaultValue": "Smooth"
}, },
{ {
@ -1801,11 +1729,7 @@
"type": "select", "type": "select",
"label": "Format", "label": "Format",
"key": "yAxisUnits", "key": "yAxisUnits",
"options": [ "options": ["Default", "Thousands", "Millions"],
"Default",
"Thousands",
"Millions"
],
"defaultValue": "Default" "defaultValue": "Default"
}, },
{ {
@ -1897,11 +1821,7 @@
"type": "select", "type": "select",
"label": "Curve", "label": "Curve",
"key": "curve", "key": "curve",
"options": [ "options": ["Smooth", "Straight", "Stepline"],
"Smooth",
"Straight",
"Stepline"
],
"defaultValue": "Smooth" "defaultValue": "Smooth"
}, },
{ {
@ -2253,11 +2173,7 @@
"type": "select", "type": "select",
"label": "Format", "label": "Format",
"key": "yAxisUnits", "key": "yAxisUnits",
"options": [ "options": ["Default", "Thousands", "Millions"],
"Default",
"Thousands",
"Millions"
],
"defaultValue": "Default" "defaultValue": "Default"
}, },
{ {
@ -2293,19 +2209,14 @@
"name": "Form", "name": "Form",
"icon": "Form", "icon": "Form",
"hasChildren": true, "hasChildren": true,
"illegalChildren": [ "illegalChildren": ["section", "form"],
"section",
"form"
],
"actions": [ "actions": [
"ValidateForm", "ValidateForm",
"ClearForm", "ClearForm",
"ChangeFormStep", "ChangeFormStep",
"UpdateFieldValue" "UpdateFieldValue"
], ],
"styles": [ "styles": ["size"],
"size"
],
"size": { "size": {
"width": 400, "width": 400,
"height": 400 "height": 400
@ -2315,10 +2226,7 @@
"type": "select", "type": "select",
"label": "Type", "label": "Type",
"key": "actionType", "key": "actionType",
"options": [ "options": ["Create", "Update"],
"Create",
"Update"
],
"defaultValue": "Create" "defaultValue": "Create"
}, },
{ {
@ -2388,14 +2296,8 @@
"name": "Form Step", "name": "Form Step",
"icon": "AssetsAdded", "icon": "AssetsAdded",
"hasChildren": true, "hasChildren": true,
"illegalChildren": [ "illegalChildren": ["section", "form", "form step"],
"section", "styles": ["size"],
"form",
"form step"
],
"styles": [
"size"
],
"size": { "size": {
"width": 400, "width": 400,
"height": 400 "height": 400
@ -2413,12 +2315,8 @@
"fieldgroup": { "fieldgroup": {
"name": "Field Group", "name": "Field Group",
"icon": "Group", "icon": "Group",
"illegalChildren": [ "illegalChildren": ["section"],
"section" "styles": ["size"],
],
"styles": [
"size"
],
"hasChildren": true, "hasChildren": true,
"size": { "size": {
"width": 400, "width": 400,
@ -2451,9 +2349,7 @@
"skeleton": false, "skeleton": false,
"name": "Text Field", "name": "Text Field",
"icon": "Text", "icon": "Text",
"styles": [ "styles": ["size"],
"size"
],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -2543,9 +2439,7 @@
"skeleton": false, "skeleton": false,
"name": "Number Field", "name": "Number Field",
"icon": "123", "icon": "123",
"styles": [ "styles": ["size"],
"size"
],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -2601,9 +2495,7 @@
"skeleton": false, "skeleton": false,
"name": "Password Field", "name": "Password Field",
"icon": "LockClosed", "icon": "LockClosed",
"styles": [ "styles": ["size"],
"size"
],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -2659,9 +2551,7 @@
"skeleton": false, "skeleton": false,
"name": "Options Picker", "name": "Options Picker",
"icon": "Menu", "icon": "Menu",
"styles": [ "styles": ["size"],
"size"
],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -2828,9 +2718,7 @@
"skeleton": false, "skeleton": false,
"name": "Multi-select Picker", "name": "Multi-select Picker",
"icon": "ViewList", "icon": "ViewList",
"styles": [ "styles": ["size"],
"size"
],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -3070,9 +2958,7 @@
"skeleton": false, "skeleton": false,
"name": "Long Form Field", "name": "Long Form Field",
"icon": "TextAlignLeft", "icon": "TextAlignLeft",
"styles": [ "styles": ["size"],
"size"
],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -3150,9 +3036,7 @@
"skeleton": false, "skeleton": false,
"name": "Date Picker", "name": "Date Picker",
"icon": "Date", "icon": "Date",
"styles": [ "styles": ["size"],
"size"
],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -3232,9 +3116,7 @@
"skeleton": false, "skeleton": false,
"name": "Barcode/QR Scanner", "name": "Barcode/QR Scanner",
"icon": "Camera", "icon": "Camera",
"styles": [ "styles": ["size"],
"size"
],
"size": { "size": {
"width": 400, "width": 400,
"height": 50 "height": 50
@ -3283,9 +3165,7 @@
"embeddedmap": { "embeddedmap": {
"name": "Embedded Map", "name": "Embedded Map",
"icon": "Location", "icon": "Location",
"styles": [ "styles": ["size"],
"size"
],
"draggable": false, "draggable": false,
"size": { "size": {
"width": 400, "width": 400,
@ -3398,9 +3278,7 @@
"skeleton": false, "skeleton": false,
"name": "Attachment", "name": "Attachment",
"icon": "Attach", "icon": "Attach",
"styles": [ "styles": ["size"],
"size"
],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -3463,9 +3341,7 @@
"skeleton": false, "skeleton": false,
"name": "Relationship Picker", "name": "Relationship Picker",
"icon": "TaskList", "icon": "TaskList",
"styles": [ "styles": ["size"],
"size"
],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -3527,9 +3403,7 @@
"skeleton": false, "skeleton": false,
"name": "JSON Field", "name": "JSON Field",
"icon": "Brackets", "icon": "Brackets",
"styles": [ "styles": ["size"],
"size"
],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -3579,9 +3453,7 @@
"s3upload": { "s3upload": {
"name": "S3 File Upload", "name": "S3 File Upload",
"icon": "UploadToCloud", "icon": "UploadToCloud",
"styles": [ "styles": ["size"],
"size"
],
"editable": true, "editable": true,
"size": { "size": {
"width": 400, "width": 400,
@ -3642,13 +3514,9 @@
"dataprovider": { "dataprovider": {
"name": "Data Provider", "name": "Data Provider",
"icon": "Data", "icon": "Data",
"illegalChildren": [ "illegalChildren": ["section"],
"section"
],
"hasChildren": true, "hasChildren": true,
"actions": [ "actions": ["RefreshDatasource"],
"RefreshDatasource"
],
"size": { "size": {
"width": 400, "width": 400,
"height": 100 "height": 100
@ -3674,10 +3542,7 @@
"type": "select", "type": "select",
"label": "Sort Order", "label": "Sort Order",
"key": "sortOrder", "key": "sortOrder",
"options": [ "options": ["Ascending", "Descending"],
"Ascending",
"Descending"
],
"defaultValue": "Ascending" "defaultValue": "Ascending"
}, },
{ {
@ -3729,9 +3594,7 @@
"skeleton": false, "skeleton": false,
"name": "Table", "name": "Table",
"icon": "Table", "icon": "Table",
"illegalChildren": [ "illegalChildren": ["section"],
"section"
],
"hasChildren": true, "hasChildren": true,
"showEmptyState": false, "showEmptyState": false,
"size": { "size": {
@ -3815,9 +3678,7 @@
"daterangepicker": { "daterangepicker": {
"name": "Date Range", "name": "Date Range",
"icon": "Calendar", "icon": "Calendar",
"styles": [ "styles": ["size"],
"size"
],
"hasChildren": false, "hasChildren": false,
"size": { "size": {
"width": 200, "width": 200,
@ -3856,9 +3717,7 @@
"spectrumcard": { "spectrumcard": {
"name": "Card", "name": "Card",
"icon": "PersonalizationField", "icon": "PersonalizationField",
"styles": [ "styles": ["size"],
"size"
],
"size": { "size": {
"width": 300, "width": 300,
"height": 120 "height": 120
@ -4031,10 +3890,7 @@
"type": "select", "type": "select",
"label": "Sort Order", "label": "Sort Order",
"key": "sortOrder", "key": "sortOrder",
"options": [ "options": ["Ascending", "Descending"],
"Ascending",
"Descending"
],
"defaultValue": "Ascending" "defaultValue": "Ascending"
}, },
{ {
@ -4213,11 +4069,7 @@
"type": "select", "type": "select",
"label": "Format", "label": "Format",
"key": "yAxisUnits", "key": "yAxisUnits",
"options": [ "options": ["Default", "Thousands", "Millions"],
"Default",
"Thousands",
"Millions"
],
"defaultValue": "Default" "defaultValue": "Default"
}, },
{ {
@ -4271,11 +4123,7 @@
"type": "select", "type": "select",
"label": "Format", "label": "Format",
"key": "yAxisUnits", "key": "yAxisUnits",
"options": [ "options": ["Default", "Thousands", "Millions"],
"Default",
"Thousands",
"Millions"
],
"defaultValue": "Default" "defaultValue": "Default"
}, },
{ {
@ -4292,11 +4140,7 @@
"type": "select", "type": "select",
"label": "Curve", "label": "Curve",
"key": "curve", "key": "curve",
"options": [ "options": ["Smooth", "Straight", "Stepline"],
"Smooth",
"Straight",
"Stepline"
],
"defaultValue": "Smooth" "defaultValue": "Smooth"
} }
] ]
@ -4328,11 +4172,7 @@
"type": "select", "type": "select",
"label": "Format", "label": "Format",
"key": "yAxisUnits", "key": "yAxisUnits",
"options": [ "options": ["Default", "Thousands", "Millions"],
"Default",
"Thousands",
"Millions"
],
"defaultValue": "Default" "defaultValue": "Default"
}, },
{ {
@ -4349,11 +4189,7 @@
"type": "select", "type": "select",
"label": "Curve", "label": "Curve",
"key": "curve", "key": "curve",
"options": [ "options": ["Smooth", "Straight", "Stepline"],
"Smooth",
"Straight",
"Stepline"
],
"defaultValue": "Smooth" "defaultValue": "Smooth"
}, },
{ {
@ -4418,11 +4254,7 @@
"type": "select", "type": "select",
"label": "Format", "label": "Format",
"key": "yAxisUnits", "key": "yAxisUnits",
"options": [ "options": ["Default", "Thousands", "Millions"],
"Default",
"Thousands",
"Millions"
],
"defaultValue": "Default" "defaultValue": "Default"
}, },
{ {
@ -4443,9 +4275,7 @@
"block": true, "block": true,
"name": "Table block", "name": "Table block",
"icon": "Table", "icon": "Table",
"styles": [ "styles": ["size"],
"size"
],
"size": { "size": {
"width": 600, "width": 600,
"height": 400 "height": 400
@ -4483,10 +4313,7 @@
"type": "select", "type": "select",
"label": "Sort Order", "label": "Sort Order",
"key": "sortOrder", "key": "sortOrder",
"options": [ "options": ["Ascending", "Descending"],
"Ascending",
"Descending"
],
"defaultValue": "Ascending" "defaultValue": "Ascending"
}, },
{ {
@ -4638,9 +4465,7 @@
"block": true, "block": true,
"name": "Cards block", "name": "Cards block",
"icon": "PersonalizationField", "icon": "PersonalizationField",
"styles": [ "styles": ["size"],
"size"
],
"size": { "size": {
"width": 600, "width": 600,
"height": 400 "height": 400
@ -4679,10 +4504,7 @@
"type": "select", "type": "select",
"label": "Sort Order", "label": "Sort Order",
"key": "sortOrder", "key": "sortOrder",
"options": [ "options": ["Ascending", "Descending"],
"Ascending",
"Descending"
],
"defaultValue": "Descending" "defaultValue": "Descending"
}, },
{ {
@ -4816,9 +4638,7 @@
"block": true, "block": true,
"name": "Repeater block", "name": "Repeater block",
"icon": "ViewList", "icon": "ViewList",
"illegalChildren": [ "illegalChildren": ["section"],
"section"
],
"hasChildren": true, "hasChildren": true,
"size": { "size": {
"width": 400, "width": 400,
@ -4846,10 +4666,7 @@
"type": "select", "type": "select",
"label": "Sort Order", "label": "Sort Order",
"key": "sortOrder", "key": "sortOrder",
"options": [ "options": ["Ascending", "Descending"],
"Ascending",
"Descending"
],
"defaultValue": "Descending" "defaultValue": "Descending"
}, },
{ {
@ -5044,9 +4861,7 @@
"markdownviewer": { "markdownviewer": {
"name": "Markdown Viewer", "name": "Markdown Viewer",
"icon": "Preview", "icon": "Preview",
"styles": [ "styles": ["size"],
"size"
],
"size": { "size": {
"width": 400, "width": 400,
"height": 100 "height": 100
@ -5063,9 +4878,7 @@
"formblock": { "formblock": {
"name": "Form Block", "name": "Form Block",
"icon": "Form", "icon": "Form",
"styles": [ "styles": ["size"],
"size"
],
"block": true, "block": true,
"info": "Form blocks are only compatible with internal or SQL tables", "info": "Form blocks are only compatible with internal or SQL tables",
"size": { "size": {
@ -5077,11 +4890,7 @@
"type": "select", "type": "select",
"label": "Type", "label": "Type",
"key": "actionType", "key": "actionType",
"options": [ "options": ["Create", "Update", "View"],
"Create",
"Update",
"View"
],
"defaultValue": "Create" "defaultValue": "Create"
}, },
{ {
@ -5215,10 +5024,7 @@
"name": "Side Panel", "name": "Side Panel",
"icon": "RailRight", "icon": "RailRight",
"hasChildren": true, "hasChildren": true,
"illegalChildren": [ "illegalChildren": ["section", "sidepanel"],
"section",
"sidepanel"
],
"showEmptyState": false, "showEmptyState": false,
"draggable": false, "draggable": false,
"info": "Side panels are hidden by default. They will only be revealed when triggered by the 'Open Side Panel' action." "info": "Side panels are hidden by default. They will only be revealed when triggered by the 'Open Side Panel' action."
@ -5307,4 +5113,4 @@
"suffix": "repeater" "suffix": "repeater"
} }
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "2.4.12-alpha.5", "version": "2.4.26",
"license": "MPL-2.0", "license": "MPL-2.0",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
@ -19,11 +19,11 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "2.4.12-alpha.5", "@budibase/bbui": "^2.4.26",
"@budibase/frontend-core": "2.4.12-alpha.5", "@budibase/frontend-core": "^2.4.26",
"@budibase/shared-core": "2.4.12-alpha.5", "@budibase/shared-core": "^2.4.26",
"@budibase/string-templates": "2.4.12-alpha.5", "@budibase/string-templates": "^2.4.26",
"@budibase/types": "2.4.12-alpha.5", "@budibase/types": "^2.4.26",
"@spectrum-css/button": "^3.0.3", "@spectrum-css/button": "^3.0.3",
"@spectrum-css/card": "^3.0.3", "@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3", "@spectrum-css/divider": "^1.0.3",

View file

@ -21,7 +21,9 @@
{#if url} {#if url}
<div class="outer" use:styleable={$component.styles}> <div class="outer" use:styleable={$component.styles}>
<div class="inner" {style} /> <div class="inner" {style}>
<slot />
</div>
</div> </div>
{:else if $builderStore.inBuilder} {:else if $builderStore.inBuilder}
<div <div

View file

@ -1,13 +1,13 @@
{ {
"name": "@budibase/frontend-core", "name": "@budibase/frontend-core",
"version": "2.4.12-alpha.5", "version": "2.4.26",
"description": "Budibase frontend core libraries used in builder and client", "description": "Budibase frontend core libraries used in builder and client",
"author": "Budibase", "author": "Budibase",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"dependencies": { "dependencies": {
"@budibase/bbui": "2.4.12-alpha.5", "@budibase/bbui": "^2.4.26",
"@budibase/shared-core": "2.4.12-alpha.5", "@budibase/shared-core": "^2.4.26",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"svelte": "^3.46.2" "svelte": "^3.46.2"
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/sdk", "name": "@budibase/sdk",
"version": "2.4.12-alpha.5", "version": "2.4.26",
"description": "Budibase Public API SDK", "description": "Budibase Public API SDK",
"author": "Budibase", "author": "Budibase",
"license": "MPL-2.0", "license": "MPL-2.0",

View file

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "2.4.12-alpha.5", "version": "2.4.26",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -43,12 +43,12 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@apidevtools/swagger-parser": "10.0.3", "@apidevtools/swagger-parser": "10.0.3",
"@budibase/backend-core": "2.4.12-alpha.5", "@budibase/backend-core": "^2.4.26",
"@budibase/client": "2.4.12-alpha.5", "@budibase/client": "^2.4.26",
"@budibase/pro": "2.4.12-alpha.5", "@budibase/pro": "2.4.26",
"@budibase/shared-core": "2.4.12-alpha.5", "@budibase/shared-core": "^2.4.26",
"@budibase/string-templates": "2.4.12-alpha.5", "@budibase/string-templates": "^2.4.26",
"@budibase/types": "2.4.12-alpha.5", "@budibase/types": "^2.4.26",
"@bull-board/api": "3.7.0", "@bull-board/api": "3.7.0",
"@bull-board/koa": "3.9.4", "@bull-board/koa": "3.9.4",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",

View file

@ -29,13 +29,6 @@ router
br: false, br: false,
}) })
) )
.use(async (ctx, next) => {
ctx.config = {
jwtSecret: env.JWT_SECRET,
useAppRootPath: true,
}
await next()
})
// re-direct before any middlewares occur // re-direct before any middlewares occur
.redirect("/", "/builder") .redirect("/", "/builder")
.use( .use(

View file

@ -39,17 +39,14 @@ let inThread = false
const environment = { const environment = {
// important - prefer app port to generic port // important - prefer app port to generic port
PORT: process.env.APP_PORT || process.env.PORT, PORT: process.env.APP_PORT || process.env.PORT,
JWT_SECRET: process.env.JWT_SECRET,
COUCH_DB_URL: process.env.COUCH_DB_URL, COUCH_DB_URL: process.env.COUCH_DB_URL,
MINIO_URL: process.env.MINIO_URL, MINIO_URL: process.env.MINIO_URL,
WORKER_URL: process.env.WORKER_URL, WORKER_URL: process.env.WORKER_URL,
AWS_REGION: process.env.AWS_REGION, AWS_REGION: process.env.AWS_REGION,
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY, MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY, MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
CDN_URL: process.env.CDN_URL || "https://cdn.budi.live",
REDIS_URL: process.env.REDIS_URL, REDIS_URL: process.env.REDIS_URL,
REDIS_PASSWORD: process.env.REDIS_PASSWORD, REDIS_PASSWORD: process.env.REDIS_PASSWORD,
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
HTTP_MIGRATIONS: process.env.HTTP_MIGRATIONS, HTTP_MIGRATIONS: process.env.HTTP_MIGRATIONS,
API_REQ_LIMIT_PER_SEC: process.env.API_REQ_LIMIT_PER_SEC, API_REQ_LIMIT_PER_SEC: process.env.API_REQ_LIMIT_PER_SEC,
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID, GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,

View file

@ -67,6 +67,7 @@ const SCHEMA: Integration = {
database: { database: {
type: DatasourceFieldType.STRING, type: DatasourceFieldType.STRING,
required: true, required: true,
display: "Service Name",
}, },
user: { user: {
type: DatasourceFieldType.STRING, type: DatasourceFieldType.STRING,

View file

@ -107,6 +107,7 @@ const SCHEMA: Integration = {
readCsv: { readCsv: {
displayName: "Read CSV", displayName: "Read CSV",
type: QueryType.FIELDS, type: QueryType.FIELDS,
readable: true,
fields: { fields: {
bucket: { bucket: {
type: DatasourceFieldType.STRING, type: DatasourceFieldType.STRING,

View file

@ -205,7 +205,6 @@ class TestConfiguration {
request.appId = appId request.appId = appId
// fake cookies, we don't need them // fake cookies, we don't need them
request.cookies = { set: () => {}, get: () => {} } request.cookies = { set: () => {}, get: () => {} }
request.config = { jwtSecret: env.JWT_SECRET }
request.user = { appId, tenantId: this.getTenantId() } request.user = { appId, tenantId: this.getTenantId() }
request.query = {} request.query = {}
request.request = { request.request = {
@ -332,8 +331,8 @@ class TestConfiguration {
roleId: roleId, roleId: roleId,
appId, appId,
} }
const authToken = auth.jwt.sign(authObj, env.JWT_SECRET) const authToken = auth.jwt.sign(authObj, coreEnv.JWT_SECRET)
const appToken = auth.jwt.sign(app, env.JWT_SECRET) const appToken = auth.jwt.sign(app, coreEnv.JWT_SECRET)
// returning necessary request headers // returning necessary request headers
await cache.user.invalidateUser(userId) await cache.user.invalidateUser(userId)
@ -361,8 +360,8 @@ class TestConfiguration {
roleId: roles.BUILTIN_ROLE_IDS.ADMIN, roleId: roles.BUILTIN_ROLE_IDS.ADMIN,
appId: this.appId, appId: this.appId,
} }
const authToken = auth.jwt.sign(authObj, env.JWT_SECRET) const authToken = auth.jwt.sign(authObj, coreEnv.JWT_SECRET)
const appToken = auth.jwt.sign(app, env.JWT_SECRET) const appToken = auth.jwt.sign(app, coreEnv.JWT_SECRET)
const headers: any = { const headers: any = {
Accept: "application/json", Accept: "application/json",
Cookie: [ Cookie: [

View file

@ -6,6 +6,7 @@ import {
constants, constants,
tenancy, tenancy,
logging, logging,
env as coreEnv,
} from "@budibase/backend-core" } from "@budibase/backend-core"
import { updateAppRole } from "./global" import { updateAppRole } from "./global"
import { BBContext, User } from "@budibase/types" import { BBContext, User } from "@budibase/types"
@ -15,7 +16,7 @@ export function request(ctx?: BBContext, request?: any) {
request.headers = {} request.headers = {}
} }
if (!ctx) { if (!ctx) {
request.headers[constants.Header.API_KEY] = env.INTERNAL_API_KEY request.headers[constants.Header.API_KEY] = coreEnv.INTERNAL_API_KEY
if (tenancy.isTenantIdSet()) { if (tenancy.isTenantIdSet()) {
request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId() request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId()
} }

View file

@ -1278,14 +1278,14 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@2.4.12-alpha.5": "@budibase/backend-core@2.4.26":
version "2.4.12-alpha.5" version "2.4.26"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.12-alpha.5.tgz#6fc37b439e05f0806909954c5c9f01f37e99f4d8" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.26.tgz#ae9679f20e86ce1706d6d549aed78a342365a4b4"
integrity sha512-TVXjKXT/67ZWK3L6Rs1eJ1+8li4o3+zxOisVuSzgAHTepm6tbF9GLNWIVlzMoLGh5k9M9GHjCkhRKmxozMrBYw== integrity sha512-9QYJbAT9WPiOckBIR6a/CoqqbUiP9vlmc/Iy5TR5Yj2wy1JnWsf09ReTuL3CsHmh+8bCJlUHZZC4m6PUMg7+ow==
dependencies: dependencies:
"@budibase/nano" "10.1.2" "@budibase/nano" "10.1.2"
"@budibase/pouchdb-replication-stream" "1.2.10" "@budibase/pouchdb-replication-stream" "1.2.10"
"@budibase/types" "2.4.12-alpha.5" "@budibase/types" "^2.4.26"
"@shopify/jest-koa-mocks" "5.0.1" "@shopify/jest-koa-mocks" "5.0.1"
"@techpass/passport-openidconnect" "0.3.2" "@techpass/passport-openidconnect" "0.3.2"
aws-cloudfront-sign "2.2.0" aws-cloudfront-sign "2.2.0"
@ -1417,14 +1417,14 @@
pouchdb-promise "^6.0.4" pouchdb-promise "^6.0.4"
through2 "^2.0.0" through2 "^2.0.0"
"@budibase/pro@2.4.12-alpha.5": "@budibase/pro@2.4.26":
version "2.4.12-alpha.5" version "2.4.26"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.12-alpha.5.tgz#ebdaf6fe987a35c9dee00a36bbcf5acb738015de" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.26.tgz#37ca2b94f5dfc28ee4ff0ffa088e29112de5b66f"
integrity sha512-j749G4I9NHnEE+0AlFckFjBa3Hkx8M93Raw5s+C7YxaPpChws2HfN/7fCSgY33aeCCGqB0SpwCKAm48BSwbwwQ== integrity sha512-PXpsj5DFnUaSlp3AHZRZa/N4CD02HPpvVFv35/FUGkeGwGJ5AihhmzxlD54U9Q9X3Ln8miejYTFoWvEnV5Ei8w==
dependencies: dependencies:
"@budibase/backend-core" "2.4.12-alpha.5" "@budibase/backend-core" "2.4.26"
"@budibase/string-templates" "2.3.20" "@budibase/string-templates" "2.3.20"
"@budibase/types" "2.4.12-alpha.5" "@budibase/types" "2.4.26"
"@koa/router" "8.0.8" "@koa/router" "8.0.8"
bull "4.10.1" bull "4.10.1"
joi "17.6.0" joi "17.6.0"
@ -1463,10 +1463,10 @@
lodash "^4.17.20" lodash "^4.17.20"
vm2 "^3.9.4" vm2 "^3.9.4"
"@budibase/types@2.4.12-alpha.5": "@budibase/types@2.4.26", "@budibase/types@^2.4.26":
version "2.4.12-alpha.5" version "2.4.26"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.12-alpha.5.tgz#3727ddef178aebb354e43de0efe03a329b37b91f" resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.26.tgz#c4efd9286e736feee56d623c21a9f6fd7c922b94"
integrity sha512-ddtKzLjNcqdQjwYv1lNRo1t5XHdxiHRsFl+xMFsMwpB/8IY8LDw7zvkoC58sFYPUvOP4c1cBA0Wne9YNxM5IiA== integrity sha512-q2QfDXJAopmHNq6Y25udmVJoEtnoskZEtaMy5d7/hX4jePJX3QnBd9sjgnAoOeSC3NOuXDjmvcRGtqXz6ao/Ag==
"@bull-board/api@3.7.0": "@bull-board/api@3.7.0":
version "3.7.0" version "3.7.0"

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/shared-core", "name": "@budibase/shared-core",
"version": "2.4.12-alpha.5", "version": "2.4.26",
"description": "Shared data utils", "description": "Shared data utils",
"main": "dist/cjs/src/index.js", "main": "dist/cjs/src/index.js",
"types": "dist/mjs/src/index.d.ts", "types": "dist/mjs/src/index.d.ts",
@ -20,7 +20,7 @@
"dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\"" "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\""
}, },
"dependencies": { "dependencies": {
"@budibase/types": "2.4.12-alpha.5" "@budibase/types": "^2.4.26"
}, },
"devDependencies": { "devDependencies": {
"concurrently": "^7.6.0", "concurrently": "^7.6.0",

View file

@ -62,7 +62,7 @@ export const getValidOperatorsForType = (
// Only allow equal/not equal for _id in SQL tables // Only allow equal/not equal for _id in SQL tables
if (field === "_id" && externalTable) { if (field === "_id" && externalTable) {
ops = [Op.Equals, Op.NotEquals] ops = [Op.Equals, Op.NotEquals, Op.In]
} }
return ops return ops

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "2.4.12-alpha.5", "version": "2.4.26",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs", "main": "src/index.cjs",
"module": "dist/bundle.mjs", "module": "dist/bundle.mjs",

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/types", "name": "@budibase/types",
"version": "2.4.12-alpha.5", "version": "2.4.26",
"description": "Budibase types", "description": "Budibase types",
"main": "dist/cjs/index.js", "main": "dist/cjs/index.js",
"types": "dist/mjs/index.d.ts", "types": "dist/mjs/index.d.ts",

View file

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "2.4.12-alpha.5", "version": "2.4.26",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -36,10 +36,10 @@
"author": "Budibase", "author": "Budibase",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@budibase/backend-core": "2.4.12-alpha.5", "@budibase/backend-core": "^2.4.26",
"@budibase/pro": "2.4.12-alpha.5", "@budibase/pro": "2.4.26",
"@budibase/string-templates": "2.4.12-alpha.5", "@budibase/string-templates": "^2.4.26",
"@budibase/types": "2.4.12-alpha.5", "@budibase/types": "^2.4.26",
"@koa/router": "8.0.8", "@koa/router": "8.0.8",
"@sentry/node": "6.17.7", "@sentry/node": "6.17.7",
"@techpass/passport-openidconnect": "0.3.2", "@techpass/passport-openidconnect": "0.3.2",

View file

@ -295,7 +295,7 @@ export async function publicSettings(
// google // google
const googleConfig = await configs.getGoogleConfig() const googleConfig = await configs.getGoogleConfig()
const preActivated = googleConfig?.activated == null const preActivated = googleConfig && googleConfig.activated == null
const google = preActivated || !!googleConfig?.activated const google = preActivated || !!googleConfig?.activated
const _googleCallbackUrl = await googleCallbackUrl(googleConfig) const _googleCallbackUrl = await googleCallbackUrl(googleConfig)

View file

@ -288,7 +288,7 @@ describe("configs", () => {
company: "Budibase", company: "Budibase",
logoUrl: "", logoUrl: "",
analyticsEnabled: false, analyticsEnabled: false,
google: true, google: false,
googleCallbackUrl: `http://localhost:10000/api/global/auth/${config.tenantId}/google/callback`, googleCallbackUrl: `http://localhost:10000/api/global/auth/${config.tenantId}/google/callback`,
isSSOEnforced: false, isSSOEnforced: false,
oidc: false, oidc: false,

View file

@ -30,10 +30,8 @@ const environment = {
// auth // auth
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY, MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY, MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
JWT_SECRET: process.env.JWT_SECRET,
SALT_ROUNDS: process.env.SALT_ROUNDS, SALT_ROUNDS: process.env.SALT_ROUNDS,
REDIS_PASSWORD: process.env.REDIS_PASSWORD, REDIS_PASSWORD: process.env.REDIS_PASSWORD,
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
COOKIE_DOMAIN: process.env.COOKIE_DOMAIN, COOKIE_DOMAIN: process.env.COOKIE_DOMAIN,
// urls // urls
MINIO_URL: process.env.MINIO_URL, MINIO_URL: process.env.MINIO_URL,
@ -42,7 +40,6 @@ const environment = {
ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL, ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL,
PLATFORM_URL: process.env.PLATFORM_URL, PLATFORM_URL: process.env.PLATFORM_URL,
APPS_URL: process.env.APPS_URL, APPS_URL: process.env.APPS_URL,
CDN_URL: process.env.CDN_URL || "https://tenants.cdn.budi.live",
// ports // ports
// prefer worker port to generic port // prefer worker port to generic port
PORT: process.env.WORKER_PORT || process.env.PORT, PORT: process.env.WORKER_PORT || process.env.PORT,

View file

@ -1,5 +1,5 @@
import env from "../environment" import env from "../environment"
import { constants } from "@budibase/backend-core" import { constants, utils } from "@budibase/backend-core"
import { BBContext } from "@budibase/types" import { BBContext } from "@budibase/types"
/** /**
@ -9,7 +9,15 @@ import { BBContext } from "@budibase/types"
export default async (ctx: BBContext, next: any) => { export default async (ctx: BBContext, next: any) => {
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
const apiKey = ctx.request.headers[constants.Header.API_KEY] const apiKey = ctx.request.headers[constants.Header.API_KEY]
if (apiKey !== env.INTERNAL_API_KEY) { if (!apiKey) {
ctx.throw(403, "Unauthorized")
}
if (Array.isArray(apiKey)) {
ctx.throw(403, "Unauthorized")
}
if (!utils.isValidInternalAPIKey(apiKey)) {
ctx.throw(403, "Unauthorized") ctx.throw(403, "Unauthorized")
} }
} }

View file

@ -5,10 +5,10 @@ import {
sessions, sessions,
events, events,
HTTPError, HTTPError,
env as coreEnv,
} from "@budibase/backend-core" } from "@budibase/backend-core"
import { PlatformLogoutOpts, User } from "@budibase/types" import { PlatformLogoutOpts, User } from "@budibase/types"
import jwt from "jsonwebtoken" import jwt from "jsonwebtoken"
import env from "../../environment"
import * as userSdk from "../users" import * as userSdk from "../users"
import * as emails from "../../utilities/email" import * as emails from "../../utilities/email"
import * as redis from "../../utilities/redis" import * as redis from "../../utilities/redis"
@ -26,7 +26,7 @@ export async function loginUser(user: User) {
sessionId, sessionId,
tenantId, tenantId,
}, },
env.JWT_SECRET! coreEnv.JWT_SECRET!
) )
return token return token
} }

View file

@ -74,7 +74,6 @@ class TestConfiguration {
const request: any = {} const request: any = {}
// fake cookies, we don't need them // fake cookies, we don't need them
request.cookies = { set: () => {}, get: () => {} } request.cookies = { set: () => {}, get: () => {} }
request.config = { jwtSecret: env.JWT_SECRET }
request.user = { tenantId: this.getTenantId() } request.user = { tenantId: this.getTenantId() }
request.query = {} request.query = {}
request.request = { request.request = {
@ -180,7 +179,7 @@ class TestConfiguration {
sessionId: "sessionid", sessionId: "sessionid",
tenantId: user.tenantId, tenantId: user.tenantId,
} }
const authCookie = auth.jwt.sign(authToken, env.JWT_SECRET) const authCookie = auth.jwt.sign(authToken, coreEnv.JWT_SECRET)
return { return {
Accept: "application/json", Accept: "application/json",
...this.cookieHeader([`${constants.Cookie.Auth}=${authCookie}`]), ...this.cookieHeader([`${constants.Cookie.Auth}=${authCookie}`]),
@ -197,7 +196,7 @@ class TestConfiguration {
} }
internalAPIHeaders() { internalAPIHeaders() {
return { [constants.Header.API_KEY]: env.INTERNAL_API_KEY } return { [constants.Header.API_KEY]: coreEnv.INTERNAL_API_KEY }
} }
adminOnlyResponse = () => { adminOnlyResponse = () => {
@ -277,7 +276,7 @@ class TestConfiguration {
// CONFIGS - OIDC // CONFIGS - OIDC
getOIDConfigCookie(configId: string) { getOIDConfigCookie(configId: string) {
const token = auth.jwt.sign(configId, env.JWT_SECRET) const token = auth.jwt.sign(configId, coreEnv.JWT_SECRET)
return this.cookieHeader([[`${constants.Cookie.OIDC_CONFIG}=${token}`]]) return this.cookieHeader([[`${constants.Cookie.OIDC_CONFIG}=${token}`]])
} }

View file

@ -1,5 +1,10 @@
import fetch from "node-fetch" import fetch from "node-fetch"
import { constants, tenancy, logging } from "@budibase/backend-core" import {
constants,
tenancy,
logging,
env as coreEnv,
} from "@budibase/backend-core"
import { checkSlashesInUrl } from "../utilities" import { checkSlashesInUrl } from "../utilities"
import env from "../environment" import env from "../environment"
import { SyncUserRequest, User } from "@budibase/types" import { SyncUserRequest, User } from "@budibase/types"
@ -9,7 +14,7 @@ async function makeAppRequest(url: string, method: string, body: any) {
return return
} }
const request: any = { headers: {} } const request: any = { headers: {} }
request.headers[constants.Header.API_KEY] = env.INTERNAL_API_KEY request.headers[constants.Header.API_KEY] = coreEnv.INTERNAL_API_KEY
if (tenancy.isTenantIdSet()) { if (tenancy.isTenantIdSet()) {
request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId() request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId()
} }

View file

@ -475,14 +475,14 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@2.4.12-alpha.5": "@budibase/backend-core@2.4.26":
version "2.4.12-alpha.5" version "2.4.26"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.12-alpha.5.tgz#6fc37b439e05f0806909954c5c9f01f37e99f4d8" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.26.tgz#ae9679f20e86ce1706d6d549aed78a342365a4b4"
integrity sha512-TVXjKXT/67ZWK3L6Rs1eJ1+8li4o3+zxOisVuSzgAHTepm6tbF9GLNWIVlzMoLGh5k9M9GHjCkhRKmxozMrBYw== integrity sha512-9QYJbAT9WPiOckBIR6a/CoqqbUiP9vlmc/Iy5TR5Yj2wy1JnWsf09ReTuL3CsHmh+8bCJlUHZZC4m6PUMg7+ow==
dependencies: dependencies:
"@budibase/nano" "10.1.2" "@budibase/nano" "10.1.2"
"@budibase/pouchdb-replication-stream" "1.2.10" "@budibase/pouchdb-replication-stream" "1.2.10"
"@budibase/types" "2.4.12-alpha.5" "@budibase/types" "^2.4.26"
"@shopify/jest-koa-mocks" "5.0.1" "@shopify/jest-koa-mocks" "5.0.1"
"@techpass/passport-openidconnect" "0.3.2" "@techpass/passport-openidconnect" "0.3.2"
aws-cloudfront-sign "2.2.0" aws-cloudfront-sign "2.2.0"
@ -564,14 +564,14 @@
pouchdb-promise "^6.0.4" pouchdb-promise "^6.0.4"
through2 "^2.0.0" through2 "^2.0.0"
"@budibase/pro@2.4.12-alpha.5": "@budibase/pro@2.4.26":
version "2.4.12-alpha.5" version "2.4.26"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.12-alpha.5.tgz#ebdaf6fe987a35c9dee00a36bbcf5acb738015de" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.26.tgz#37ca2b94f5dfc28ee4ff0ffa088e29112de5b66f"
integrity sha512-j749G4I9NHnEE+0AlFckFjBa3Hkx8M93Raw5s+C7YxaPpChws2HfN/7fCSgY33aeCCGqB0SpwCKAm48BSwbwwQ== integrity sha512-PXpsj5DFnUaSlp3AHZRZa/N4CD02HPpvVFv35/FUGkeGwGJ5AihhmzxlD54U9Q9X3Ln8miejYTFoWvEnV5Ei8w==
dependencies: dependencies:
"@budibase/backend-core" "2.4.12-alpha.5" "@budibase/backend-core" "2.4.26"
"@budibase/string-templates" "2.3.20" "@budibase/string-templates" "2.3.20"
"@budibase/types" "2.4.12-alpha.5" "@budibase/types" "2.4.26"
"@koa/router" "8.0.8" "@koa/router" "8.0.8"
bull "4.10.1" bull "4.10.1"
joi "17.6.0" joi "17.6.0"
@ -592,10 +592,10 @@
lodash "^4.17.20" lodash "^4.17.20"
vm2 "^3.9.4" vm2 "^3.9.4"
"@budibase/types@2.4.12-alpha.5": "@budibase/types@2.4.26", "@budibase/types@^2.4.26":
version "2.4.12-alpha.5" version "2.4.26"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.12-alpha.5.tgz#3727ddef178aebb354e43de0efe03a329b37b91f" resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.26.tgz#c4efd9286e736feee56d623c21a9f6fd7c922b94"
integrity sha512-ddtKzLjNcqdQjwYv1lNRo1t5XHdxiHRsFl+xMFsMwpB/8IY8LDw7zvkoC58sFYPUvOP4c1cBA0Wne9YNxM5IiA== integrity sha512-q2QfDXJAopmHNq6Y25udmVJoEtnoskZEtaMy5d7/hX4jePJX3QnBd9sjgnAoOeSC3NOuXDjmvcRGtqXz6ao/Ag==
"@cspotcode/source-map-support@^0.8.0": "@cspotcode/source-map-support@^0.8.0":
version "0.8.1" version "0.8.1"

View file

@ -1,6 +1,5 @@
BB_ADMIN_USER_EMAIL=qa@budibase.com BB_ADMIN_USER_EMAIL=qa@budibase.com
BB_ADMIN_USER_PASSWORD=budibase BB_ADMIN_USER_PASSWORD=budibase
ENCRYPTED_TEST_PUBLIC_API_KEY=a65722f06bee5caeadc5d7ca2f543a43-d610e627344210c643bb726f
COUCH_DB_URL=http://budibase:budibase@localhost:4567 COUCH_DB_URL=http://budibase:budibase@localhost:4567
COUCH_DB_USER=budibase COUCH_DB_USER=budibase
COUCH_DB_PASSWORD=budibase COUCH_DB_PASSWORD=budibase

View file

@ -1,11 +1,3 @@
const env = require("../src/environment")
env._set("BUDIBASE_SERVER_URL", "http://localhost:4100")
env._set(
"BUDIBASE_PUBLIC_API_KEY",
"a65722f06bee5caeadc5d7ca2f543a43-d610e627344210c643bb726f"
)
// mock all dates to 2020-01-01T00:00:00.000Z // mock all dates to 2020-01-01T00:00:00.000Z
// use tk.reset() to use real dates in individual tests // use tk.reset() to use real dates in individual tests
const MOCK_DATE = new Date("2020-01-01T00:00:00.000Z") const MOCK_DATE = new Date("2020-01-01T00:00:00.000Z")

View file

@ -49,14 +49,16 @@ class InternalAPIClient {
// @ts-ignore // @ts-ignore
const response = await fetch(`https://${process.env.TENANT_ID}.${this.host}${url}`, requestOptions) const response = await fetch(`https://${process.env.TENANT_ID}.${this.host}${url}`, requestOptions)
if (response.status == 404 || response.status == 500) { if (
response.status == 404 ||
response.status == 500 ||
response.status == 403
) {
console.error("Error in apiCall") console.error("Error in apiCall")
console.error("Response:") console.error("Response:", response)
console.error(response) const json = await response.json()
console.error("Response body:") console.error("Response body:", json)
console.error(response.body) console.error("Request body:", requestOptions.body)
console.error("Request body:")
console.error(requestOptions.body)
} }
return response return response
} }

View file

@ -44,12 +44,10 @@ class AccountsAPIClient {
const response = await fetch(`${this.host}${url}`, requestOptions) const response = await fetch(`${this.host}${url}`, requestOptions)
if (response.status == 404 || response.status == 500) { if (response.status == 404 || response.status == 500) {
console.error("Error in apiCall") console.error("Error in apiCall")
console.error("Response:") console.error("Response:", response)
console.error(response) const json = await response.json()
console.error("Response body:") console.error("Response body:", json)
console.error(response.body) console.error("Request body:", requestOptions.body)
console.error("Request body:")
console.error(requestOptions.body)
} }
return response return response
} }

View file

@ -5,7 +5,8 @@ import { Hosting } from "@budibase/types"
export const generateAccount = (): Partial<NewAccount> => { export const generateAccount = (): Partial<NewAccount> => {
const randomGuid = generator.guid() const randomGuid = generator.guid()
let tenant: string = "a" + randomGuid //Needs to start with a letter
let tenant: string = "tenant" + randomGuid
tenant = tenant.replace(/-/g, "") tenant = tenant.replace(/-/g, "")
return { return {

View file

@ -11,20 +11,32 @@ interface ApiOptions {
class PublicAPIClient { class PublicAPIClient {
host: string host: string
apiKey: string apiKey?: string
tenantName?: string
appId?: string appId?: string
cookie?: string
constructor(appId?: string) { constructor(appId?: string) {
if (!env.BUDIBASE_PUBLIC_API_KEY || !env.BUDIBASE_SERVER_URL) { if (!env.BUDIBASE_HOST) {
throw new Error( throw new Error(
"Must set BUDIBASE_PUBLIC_API_KEY and BUDIBASE_SERVER_URL env vars" "Must set BUDIBASE_PUBLIC_API_KEY and BUDIBASE_SERVER_URL env vars"
) )
} }
this.host = `${env.BUDIBASE_SERVER_URL}/api/public/v1` this.host = `${env.BUDIBASE_HOST}/api/public/v1`
this.apiKey = env.BUDIBASE_PUBLIC_API_KEY
this.appId = appId this.appId = appId
} }
setTenantName(tenantName: string) {
this.tenantName = tenantName
}
setApiKey(apiKey: string) {
this.apiKey = apiKey
process.env.BUDIBASE_PUBLIC_API_KEY = apiKey
this.host = `${env.BUDIBASE_HOST}/api/public/v1`
}
apiCall = apiCall =
(method: APIMethod) => (method: APIMethod) =>
async (url = "", options: ApiOptions = {}) => { async (url = "", options: ApiOptions = {}) => {
@ -32,18 +44,27 @@ class PublicAPIClient {
method, method,
body: JSON.stringify(options.body), body: JSON.stringify(options.body),
headers: { headers: {
"x-budibase-api-key": this.apiKey, "x-budibase-api-key": this.apiKey || null,
"x-budibase-app-id": this.appId, "x-budibase-app-id": this.appId,
"Content-Type": "application/json", "Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
...options.headers, ...options.headers,
cookie: this.cookie,
redirect: "follow",
follow: 20,
}, },
} }
// prettier-ignore
// @ts-ignore // @ts-ignore
const response = await fetch(`${this.host}${url}`, requestOptions) const response = await fetch(`https://${process.env.TENANT_ID}.${this.host}${url}`, requestOptions)
if (response.status !== 200) {
console.error(response) if (response.status == 500 || response.status == 403) {
console.error("Error in apiCall")
console.error("Response:", response)
const json = await response.json()
console.error("Response body:", json)
console.error("Request body:", requestOptions.body)
} }
return response return response
} }

View file

@ -0,0 +1,38 @@
import { Response } from "node-fetch"
import { Account } from "@budibase/types"
import AccountsAPIClient from "./accountsAPIClient"
import { NewAccount } from "../fixtures/types/newAccount"
export default class AccountsApi {
api: AccountsAPIClient
constructor(AccountsAPIClient: AccountsAPIClient) {
this.api = AccountsAPIClient
}
async validateEmail(email: string): Promise<Response> {
const response = await this.api.post(`/accounts/validate/email`, {
body: { email },
})
expect(response).toHaveStatusCode(200)
return response
}
async validateTenantId(tenantId: string): Promise<Response> {
const response = await this.api.post(`/accounts/validate/tenantId`, {
body: { tenantId },
})
expect(response).toHaveStatusCode(200)
return response
}
async create(body: Partial<NewAccount>): Promise<[Response, Account]> {
const headers = {
"no-verify": "1",
}
const response = await this.api.post(`/accounts`, { body, headers })
const json = await response.json()
expect(response).toHaveStatusCode(201)
return [response, json]
}
}

View file

@ -0,0 +1,66 @@
import env from "../../../environment"
import fetch, { HeadersInit } from "node-fetch"
type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
interface ApiOptions {
method?: APIMethod
body?: object
headers?: HeadersInit | undefined
}
class AccountsAPIClient {
host: string
appId?: string
cookie?: string
constructor(appId?: string) {
if (!env.BUDIBASE_ACCOUNTS_URL) {
throw new Error("Must set BUDIBASE_SERVER_URL env var")
}
this.host = `${env.BUDIBASE_ACCOUNTS_URL}/api`
this.appId = appId
}
apiCall =
(method: APIMethod) =>
async (url = "", options: ApiOptions = {}) => {
const requestOptions = {
method,
body: JSON.stringify(options.body),
headers: {
"x-budibase-app-id": this.appId,
"Content-Type": "application/json",
Accept: "application/json",
cookie: this.cookie,
redirect: "follow",
follow: 20,
...options.headers,
},
credentials: "include",
}
// @ts-ignore
const response = await fetch(`${this.host}${url}`, requestOptions)
if (
response.status == 404 ||
response.status == 500 ||
response.status == 400
) {
console.error("Error in apiCall")
console.error("Response:", response)
const json = await response.json()
console.error("Response body:", json)
console.error("Request body:", requestOptions.body)
}
return response
}
post = this.apiCall("POST")
get = this.apiCall("GET")
patch = this.apiCall("PATCH")
del = this.apiCall("DELETE")
put = this.apiCall("PUT")
}
export default AccountsAPIClient

View file

@ -63,4 +63,15 @@ export default class AppApi {
const response = await this.api.post(`/applications/${id}/unpublish`) const response = await this.api.post(`/applications/${id}/unpublish`)
return [response] return [response]
} }
async createFirstApp() {
const body = {
name: "My first app",
url: "my-first-app",
useTemplate: false,
sampleData: true,
}
const response = await this.api.post("/applications", { body })
expect(response).toHaveStatusCode(200)
}
} }

View file

@ -0,0 +1,48 @@
import { Response } from "node-fetch"
import AccountsAPIClient from "./accountsAPIClient"
import { ApiKeyResponse } from "../fixtures/types/apiKeyResponse"
export default class AuthApi {
api: AccountsAPIClient
constructor(apiClient: AccountsAPIClient) {
this.api = apiClient
}
async loginAsAdmin(): Promise<[Response, any]> {
const response = await this.api.post(`/auth/login`, {
body: {
username: process.env.BB_ADMIN_USER_EMAIL,
password: process.env.BB_ADMIN_USER_PASSWORD,
},
})
const cookie = response.headers.get("set-cookie")
this.api.cookie = cookie as any
return [response, cookie]
}
async login(email: String, password: String): Promise<[Response, any]> {
const response = await this.api.post(`/global/auth/default/login`, {
body: {
username: email,
password: password,
},
})
expect(response).toHaveStatusCode(200)
const cookie = response.headers.get("set-cookie")
this.api.cookie = cookie as any
return [response, cookie]
}
async logout(): Promise<any> {
return this.api.post(`/global/auth/logout`)
}
async getApiKey(): Promise<ApiKeyResponse> {
const response = await this.api.get(`/global/self/api_key`)
const json = await response.json()
expect(response).toHaveStatusCode(200)
expect(json).toHaveProperty("apiKey")
return json
}
}

View file

@ -3,19 +3,82 @@ import ApplicationApi from "./applications"
import TableApi from "./tables" import TableApi from "./tables"
import UserApi from "./users" import UserApi from "./users"
import RowApi from "./rows" import RowApi from "./rows"
import AuthApi from "./auth"
import AccountsApiClient from "./accountsAPIClient"
import AccountsApi from "./accounts"
import { generateAccount } from "../fixtures/accounts"
import internalApplicationsApi from "../../internal-api/TestConfiguration/applications"
import InternalAPIClient from "../../internal-api/TestConfiguration/InternalAPIClient"
export default class TestConfiguration<T> { export default class TestConfiguration<T> {
applications: ApplicationApi applications: ApplicationApi
auth: AuthApi
users: UserApi users: UserApi
tables: TableApi tables: TableApi
rows: RowApi rows: RowApi
context: T context: T
accounts: AccountsApi
apiClient: PublicAPIClient
accountsApiClient: AccountsApiClient
internalApiClient: InternalAPIClient
internalApplicationsApi: internalApplicationsApi
constructor(apiClient: PublicAPIClient) { constructor(
apiClient: PublicAPIClient,
accountsApiClient: AccountsApiClient,
internalApiClient: InternalAPIClient
) {
this.apiClient = apiClient
this.accountsApiClient = accountsApiClient
this.internalApiClient = internalApiClient
this.auth = new AuthApi(this.internalApiClient)
this.accounts = new AccountsApi(this.accountsApiClient)
this.applications = new ApplicationApi(apiClient) this.applications = new ApplicationApi(apiClient)
this.users = new UserApi(apiClient) this.users = new UserApi(apiClient)
this.tables = new TableApi(apiClient) this.tables = new TableApi(apiClient)
this.rows = new RowApi(apiClient) this.rows = new RowApi(apiClient)
this.internalApplicationsApi = new internalApplicationsApi(
internalApiClient
)
this.context = <T>{}
}
async setupAccountAndTenant() {
// This step is required to create a new account and tenant for the tests, its part of
// the support for running tests in multiple environments.
const account = generateAccount()
await this.accounts.validateEmail(<string>account.email)
await this.accounts.validateTenantId(<string>account.tenantId)
process.env.TENANT_ID = <string>account.tenantId
await this.accounts.create(account)
await this.updateApiClients(<string>account.tenantName)
await this.auth.login(<string>account.email, <string>account.password)
const body = {
name: "My first app",
url: "my-first-app",
useTemplate: false,
sampleData: true,
}
await this.internalApplicationsApi.create(body)
}
// After the account and tenant have been created, we need to get and set the API key for the test
async setApiKey() {
const apiKeyResponse = await this.auth.getApiKey()
this.apiClient.setApiKey(apiKeyResponse.apiKey)
}
async updateApiClients(tenantName: string) {
this.apiClient.setTenantName(tenantName)
this.applications = new ApplicationApi(this.apiClient)
this.rows = new RowApi(this.apiClient)
this.internalApiClient.setTenantName(tenantName)
this.internalApplicationsApi = new internalApplicationsApi(
this.internalApiClient
)
this.auth = new AuthApi(this.internalApiClient)
this.context = <T>{} this.context = <T>{}
} }

View file

@ -0,0 +1,22 @@
import { NewAccount } from "./types/newAccount"
import generator from "../../generator"
import { Hosting } from "@budibase/types"
export const generateAccount = (): Partial<NewAccount> => {
const randomGuid = generator.guid()
//Needs to start with a letter
let tenant: string = "tenant" + randomGuid
tenant = tenant.replace(/-/g, "")
return {
email: `qa+${randomGuid}@budibase.com`,
hosting: Hosting.CLOUD,
name: `qa+${randomGuid}@budibase.com`,
password: `${randomGuid}`,
profession: "software_engineer",
size: "10+",
tenantId: `${tenant}`,
tenantName: `${tenant}`,
}
}

View file

@ -0,0 +1,6 @@
export interface ApiKeyResponse {
apiKey: string
createdAt: string
updatedAt: string
userId: string
}

View file

@ -0,0 +1,5 @@
import { Account } from "@budibase/types"
export interface NewAccount extends Account {
password: string
}

View file

@ -1,15 +1,25 @@
import TestConfiguration from "../../../config/public-api/TestConfiguration" import TestConfiguration from "../../../config/public-api/TestConfiguration"
import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient" import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient"
import AccountsAPIClient from "../../../config/public-api/TestConfiguration/accountsAPIClient"
import generateApp from "../../../config/public-api/fixtures/applications" import generateApp from "../../../config/public-api/fixtures/applications"
import { Application } from "@budibase/server/api/controllers/public/mapping/types" import { Application } from "@budibase/server/api/controllers/public/mapping/types"
import { db as dbCore } from "@budibase/backend-core" import { db as dbCore } from "@budibase/backend-core"
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
describe("Public API - /applications endpoints", () => { describe("Public API - /applications endpoints", () => {
const api = new PublicAPIClient() const api = new PublicAPIClient()
const config = new TestConfiguration<Application>(api) const accountsAPI = new AccountsAPIClient()
const internalAPI = new InternalAPIClient()
const config = new TestConfiguration<Application>(
api,
accountsAPI,
internalAPI
)
beforeAll(async () => { beforeAll(async () => {
await config.beforeAll() await config.setupAccountAndTenant()
await config.setApiKey()
const [response, app] = await config.applications.seed() const [response, app] = await config.applications.seed()
config.context = app config.context = app
}) })

View file

@ -2,14 +2,19 @@ import { Row } from "@budibase/server/api/controllers/public/mapping/types"
import { generateRow } from "../../../config/public-api/fixtures/tables" import { generateRow } from "../../../config/public-api/fixtures/tables"
import TestConfiguration from "../../../config/public-api/TestConfiguration" import TestConfiguration from "../../../config/public-api/TestConfiguration"
import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient" import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient"
import AccountsAPIClient from "../../../config/public-api/TestConfiguration/accountsAPIClient"
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
describe("Public API - /rows endpoints", () => { describe("Public API - /rows endpoints", () => {
let api = new PublicAPIClient() const api = new PublicAPIClient()
const accountsAPI = new AccountsAPIClient()
const config = new TestConfiguration<Row>(api) const internalAPI = new InternalAPIClient()
const config = new TestConfiguration<Row>(api, accountsAPI, internalAPI)
beforeAll(async () => { beforeAll(async () => {
await config.beforeAll() await config.setupAccountAndTenant()
await config.setApiKey()
const [aResp, app] = await config.applications.seed() const [aResp, app] = await config.applications.seed()
config.tables.api.appId = app._id config.tables.api.appId = app._id

View file

@ -2,13 +2,19 @@ import { Table } from "@budibase/server/api/controllers/public/mapping/types"
import { generateTable } from "../../../config/public-api/fixtures/tables" import { generateTable } from "../../../config/public-api/fixtures/tables"
import TestConfiguration from "../../../config/public-api/TestConfiguration" import TestConfiguration from "../../../config/public-api/TestConfiguration"
import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient" import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient"
import AccountsAPIClient from "../../../config/public-api/TestConfiguration/accountsAPIClient"
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
describe("Public API - /tables endpoints", () => { describe("Public API - /tables endpoints", () => {
let api = new PublicAPIClient() const api = new PublicAPIClient()
const config = new TestConfiguration<Table>(api) const accountsAPI = new AccountsAPIClient()
const internalAPI = new InternalAPIClient()
const config = new TestConfiguration<Table>(api, accountsAPI, internalAPI)
beforeAll(async () => { beforeAll(async () => {
await config.beforeAll() await config.setupAccountAndTenant()
await config.setApiKey()
const [appResp, app] = await config.applications.seed() const [appResp, app] = await config.applications.seed()
config.tables.api.appId = app._id config.tables.api.appId = app._id

View file

@ -2,13 +2,18 @@ import TestConfiguration from "../../../config/public-api/TestConfiguration"
import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient" import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient"
import generateUser from "../../../config/public-api/fixtures/users" import generateUser from "../../../config/public-api/fixtures/users"
import { User } from "@budibase/server/api/controllers/public/mapping/types" import { User } from "@budibase/server/api/controllers/public/mapping/types"
import AccountsAPIClient from "../../../config/public-api/TestConfiguration/accountsAPIClient"
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
describe("Public API - /users endpoints", () => { describe("Public API - /users endpoints", () => {
const api = new PublicAPIClient() const api = new PublicAPIClient()
const config = new TestConfiguration<User>(api) const accountsAPI = new AccountsAPIClient()
const internalAPI = new InternalAPIClient()
const config = new TestConfiguration<User>(api, accountsAPI, internalAPI)
beforeAll(async () => { beforeAll(async () => {
await config.beforeAll() await config.setupAccountAndTenant()
await config.setApiKey()
const [_, user] = await config.users.seed() const [_, user] = await config.users.seed()
config.context = user config.context = user
}) })