diff --git a/.github/workflows/release-singleimage.yml b/.github/workflows/release-singleimage.yml index 4d35916f4d..a3444d5e7a 100644 --- a/.github/workflows/release-singleimage.yml +++ b/.github/workflows/release-singleimage.yml @@ -66,14 +66,21 @@ jobs: context: . push: true platforms: linux/amd64,linux/arm64 + build-args: BUDIBASE_VERSION=$BUDIBASE_VERSION tags: budibase/budibase,budibase/budibase:${{ env.RELEASE_VERSION }} file: ./hosting/single/Dockerfile.v2 + env: + BUDIBASE_VERSION: ${{ env.RELEASE_VERSION }} - name: Tag and release Budibase Azure App Service docker image uses: docker/build-push-action@v2 with: context: . push: true platforms: linux/amd64 - build-args: TARGETBUILD=aas + build-args: | + TARGETBUILD=aas + BUDIBASE_VERSION=$BUDIBASE_VERSION tags: budibase/budibase-aas,budibase/budibase-aas:${{ env.RELEASE_VERSION }} file: ./hosting/single/Dockerfile.v2 + env: + BUDIBASE_VERSION: ${{ env.RELEASE_VERSION }} diff --git a/hosting/scripts/install-minio.sh b/hosting/scripts/install-minio.sh deleted file mode 100755 index 8297593599..0000000000 --- a/hosting/scripts/install-minio.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -if [[ $TARGETARCH == arm* ]] ; -then - echo "INSTALLING ARM64 MINIO" - wget https://dl.min.io/server/minio/release/linux-arm64/minio -else - echo "INSTALLING AMD64 MINIO" - wget https://dl.min.io/server/minio/release/linux-amd64/minio -fi -chmod +x minio diff --git a/hosting/single/Dockerfile.v2 b/hosting/single/Dockerfile.v2 index 5b07a51b27..ec03a1b5a2 100644 --- a/hosting/single/Dockerfile.v2 +++ b/hosting/single/Dockerfile.v2 @@ -42,6 +42,7 @@ COPY packages/string-templates packages/string-templates FROM budibase/couchdb as runner ARG TARGETARCH ENV TARGETARCH $TARGETARCH +ENV NODE_MAJOR 18 #TARGETBUILD can be set to single (for single docker image) or aas (for azure app service) # e.g. docker build --build-arg TARGETBUILD=aas .... ARG TARGETBUILD=single @@ -49,10 +50,10 @@ ENV TARGETBUILD $TARGETBUILD # install base dependencies RUN apt-get update && \ - apt-get install -y --no-install-recommends software-properties-common nginx uuid-runtime redis-server + apt-get install -y --no-install-recommends software-properties-common nginx uuid-runtime redis-server libaio1 # Install postgres client for pg_dump utils -RUN apt install software-properties-common apt-transport-https gpg -y \ +RUN apt install -y software-properties-common apt-transport-https ca-certificates gnupg \ && curl -fsSl https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/postgresql.gpg > /dev/null \ && echo deb [arch=amd64,arm64,ppc64el signed-by=/usr/share/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main | tee /etc/apt/sources.list.d/postgresql.list \ && apt update -y \ @@ -61,10 +62,8 @@ RUN apt install software-properties-common apt-transport-https gpg -y \ # install other dependencies, nodejs, oracle requirements, jdk8, redis, nginx WORKDIR /nodejs -RUN curl -sL https://deb.nodesource.com/setup_18.x -o /tmp/nodesource_setup.sh && \ - bash /tmp/nodesource_setup.sh && \ - apt-get install -y --no-install-recommends libaio1 nodejs && \ - npm install --global yarn pm2 +COPY scripts/install-node.sh ./install.sh +RUN chmod +x install.sh && ./install.sh # setup nginx COPY hosting/single/nginx/nginx.conf /etc/nginx diff --git a/hosting/single/runner.sh b/hosting/single/runner.sh index 9dc7aa25d8..770b23eec1 100644 --- a/hosting/single/runner.sh +++ b/hosting/single/runner.sh @@ -77,7 +77,7 @@ mkdir -p ${DATA_DIR}/minio chown -R couchdb:couchdb ${DATA_DIR}/couch redis-server --requirepass $REDIS_PASSWORD > /dev/stdout 2>&1 & /bbcouch-runner.sh & -/minio/minio server --console-address ":9001" ${DATA_DIR}/minio > /dev/stdout 2>&1 & +minio server --console-address ":9001" ${DATA_DIR}/minio > /dev/stdout 2>&1 & /etc/init.d/nginx restart if [[ ! -z "${CUSTOM_DOMAIN}" ]]; then # Add monthly cron job to renew certbot certificate diff --git a/lerna.json b/lerna.json index 6df4a4c4cd..f0f51242d1 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.12.1", + "version": "2.12.4", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/__mocks__/aws-sdk.ts b/packages/backend-core/__mocks__/aws-sdk.ts index b8d91dbaa9..e3be511d08 100644 --- a/packages/backend-core/__mocks__/aws-sdk.ts +++ b/packages/backend-core/__mocks__/aws-sdk.ts @@ -3,6 +3,7 @@ const mockS3 = { deleteObject: jest.fn().mockReturnThis(), deleteObjects: jest.fn().mockReturnThis(), createBucket: jest.fn().mockReturnThis(), + getObject: jest.fn().mockReturnThis(), listObject: jest.fn().mockReturnThis(), getSignedUrl: jest.fn((operation: string, params: any) => { return `http://s3.example.com/${params.Bucket}/${params.Key}` diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index b23cd8e5b1..dc8d71b52c 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -21,7 +21,7 @@ "test:watch": "jest --watchAll" }, "dependencies": { - "@budibase/nano": "10.1.2", + "@budibase/nano": "10.1.3", "@budibase/pouchdb-replication-stream": "1.2.10", "@budibase/shared-core": "0.0.0", "@budibase/types": "0.0.0", diff --git a/packages/backend-core/src/db/constants.ts b/packages/backend-core/src/db/constants.ts index aea485e3e3..bfa7595d62 100644 --- a/packages/backend-core/src/db/constants.ts +++ b/packages/backend-core/src/db/constants.ts @@ -8,3 +8,7 @@ export const CONSTANT_INTERNAL_ROW_COLS = [ ] as const export const CONSTANT_EXTERNAL_ROW_COLS = ["_id", "_rev", "tableId"] as const + +export function isInternalColumnName(name: string): boolean { + return (CONSTANT_INTERNAL_ROW_COLS as readonly string[]).includes(name) +} diff --git a/packages/backend-core/src/docIds/params.ts b/packages/backend-core/src/docIds/params.ts index 36fd75622b..d9baee3dc6 100644 --- a/packages/backend-core/src/docIds/params.ts +++ b/packages/backend-core/src/docIds/params.ts @@ -6,6 +6,7 @@ import { ViewName, } from "../constants" import { getProdAppID } from "./conversions" +import { DatabaseQueryOpts } from "@budibase/types" /** * If creating DB allDocs/query params with only a single top level ID this can be used, this @@ -22,8 +23,8 @@ import { getProdAppID } from "./conversions" export function getDocParams( docType: string, docId?: string | null, - otherProps: any = {} -) { + otherProps: Partial = {} +): DatabaseQueryOpts { if (docId == null) { docId = "" } @@ -45,8 +46,8 @@ export function getDocParams( export function getRowParams( tableId?: string | null, rowId?: string | null, - otherProps = {} -) { + otherProps: Partial = {} +): DatabaseQueryOpts { if (tableId == null) { return getDocParams(DocumentType.ROW, null, otherProps) } @@ -88,7 +89,10 @@ export const isDatasourceId = (id: string) => { /** * Gets parameters for retrieving workspaces. */ -export function getWorkspaceParams(id = "", otherProps = {}) { +export function getWorkspaceParams( + id = "", + otherProps: Partial = {} +): DatabaseQueryOpts { return { ...otherProps, startkey: `${DocumentType.WORKSPACE}${SEPARATOR}${id}`, @@ -99,7 +103,10 @@ export function getWorkspaceParams(id = "", otherProps = {}) { /** * Gets parameters for retrieving users. */ -export function getGlobalUserParams(globalId: any, otherProps: any = {}) { +export function getGlobalUserParams( + globalId: any, + otherProps: Partial = {} +): DatabaseQueryOpts { if (!globalId) { globalId = "" } @@ -117,11 +124,17 @@ export function getGlobalUserParams(globalId: any, otherProps: any = {}) { /** * Gets parameters for retrieving users, this is a utility function for the getDocParams function. */ -export function getUserMetadataParams(userId?: string | null, otherProps = {}) { +export function getUserMetadataParams( + userId?: string | null, + otherProps: Partial = {} +): DatabaseQueryOpts { return getRowParams(InternalTable.USER_METADATA, userId, otherProps) } -export function getUsersByAppParams(appId: any, otherProps: any = {}) { +export function getUsersByAppParams( + appId: any, + otherProps: Partial = {} +): DatabaseQueryOpts { const prodAppId = getProdAppID(appId) return { ...otherProps, diff --git a/packages/backend-core/src/users/db.ts b/packages/backend-core/src/users/db.ts index daa09bee6f..c071064713 100644 --- a/packages/backend-core/src/users/db.ts +++ b/packages/backend-core/src/users/db.ts @@ -165,13 +165,9 @@ export class UserDB { } static async getUsersByAppAccess(opts: { appId?: string; limit?: number }) { - const params: any = { - include_docs: true, - limit: opts.limit || 50, - } let response: User[] = await usersCore.searchGlobalUsersByAppAccess( opts.appId, - params + { limit: opts.limit || 50 } ) return response } diff --git a/packages/backend-core/src/users/users.ts b/packages/backend-core/src/users/users.ts index bad108ab84..6dc8750b62 100644 --- a/packages/backend-core/src/users/users.ts +++ b/packages/backend-core/src/users/users.ts @@ -20,6 +20,7 @@ import { User, ContextUser, DatabaseQueryOpts, + CouchFindOptions, } from "@budibase/types" import { getGlobalDB } from "../context" import * as context from "../context" @@ -140,7 +141,7 @@ export const getGlobalUserByEmail = async ( export const searchGlobalUsersByApp = async ( appId: any, - opts: any, + opts: DatabaseQueryOpts, getOpts?: GetOpts ) => { if (typeof appId !== "string") { @@ -166,7 +167,10 @@ export const searchGlobalUsersByApp = async ( Return any user who potentially has access to the application Admins, developers and app users with the explicitly role. */ -export const searchGlobalUsersByAppAccess = async (appId: any, opts: any) => { +export const searchGlobalUsersByAppAccess = async ( + appId: any, + opts?: { limit?: number } +) => { const roleSelector = `roles.${appId}` let orQuery: any[] = [ @@ -187,7 +191,7 @@ export const searchGlobalUsersByAppAccess = async (appId: any, opts: any) => { orQuery.push(roleCheck) } - let searchOptions = { + let searchOptions: CouchFindOptions = { selector: { $or: orQuery, _id: { @@ -198,7 +202,7 @@ export const searchGlobalUsersByAppAccess = async (appId: any, opts: any) => { } const resp = await directCouchFind(context.getGlobalDBName(), searchOptions) - return resp?.rows + return resp.rows } export const getGlobalUserByAppPage = (appId: string, user: User) => { @@ -245,7 +249,8 @@ export const paginatedUsers = async ({ limit, }: SearchUsersRequest = {}) => { const db = getGlobalDB() - const pageLimit = limit ? limit + 1 : PAGE_LIMIT + 1 + const pageSize = limit ?? PAGE_LIMIT + const pageLimit = pageSize + 1 // get one extra document, to have the next page const opts: DatabaseQueryOpts = { include_docs: true, @@ -272,7 +277,7 @@ export const paginatedUsers = async ({ const response = await db.allDocs(getGlobalUserParams(null, opts)) userList = response.rows.map((row: any) => row.doc) } - return pagination(userList, pageLimit, { + return pagination(userList, pageSize, { paginate: true, property, getKey, diff --git a/packages/backend-core/tests/core/utilities/mocks/date.ts b/packages/backend-core/tests/core/utilities/mocks/date.ts index f580b68349..1e6d105d93 100644 --- a/packages/backend-core/tests/core/utilities/mocks/date.ts +++ b/packages/backend-core/tests/core/utilities/mocks/date.ts @@ -1,2 +1,3 @@ export const MOCK_DATE = new Date("2020-01-01T00:00:00.000Z") + export const MOCK_DATE_TIMESTAMP = 1577836800000 diff --git a/packages/builder/.gitignore b/packages/builder/.gitignore index e5c961d509..acd1a70579 100644 --- a/packages/builder/.gitignore +++ b/packages/builder/.gitignore @@ -5,4 +5,4 @@ package-lock.json release/ dist/ routify -.routify/ \ No newline at end of file +.routify/ diff --git a/packages/builder/src/components/backend/DataTable/RelationshipDataTable.svelte b/packages/builder/src/components/backend/DataTable/RelationshipDataTable.svelte index 8ef870caca..4e67a92443 100644 --- a/packages/builder/src/components/backend/DataTable/RelationshipDataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/RelationshipDataTable.svelte @@ -16,7 +16,6 @@ $: linkedTable = $tables.list.find(table => table._id === linkedTableId) $: schema = linkedTable?.schema $: table = $tables.list.find(table => table._id === tableId) - $: type = table?.type $: fetchData(tableId, rowId) $: { let rowLabel = row?.[table?.primaryDisplay] @@ -41,5 +40,5 @@ {#if row && row._id === rowId} - +
{/if} diff --git a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte index d1e56afa4e..1ff1b34a69 100644 --- a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte @@ -16,6 +16,7 @@ import GridRelationshipButton from "components/backend/DataTable/buttons/grid/GridRelationshipButton.svelte" import GridEditColumnModal from "components/backend/DataTable/modals/grid/GridEditColumnModal.svelte" import GridUsersTableButton from "components/backend/DataTable/modals/grid/GridUsersTableButton.svelte" + import { DB_TYPE_EXTERNAL } from "constants/backend" const userSchemaOverrides = { firstName: { displayName: "First name", disabled: true }, @@ -27,7 +28,7 @@ $: id = $tables.selected?._id $: isUsersTable = id === TableNames.USERS - $: isInternal = $tables.selected?.type !== "external" + $: isInternal = $tables.selected?.sourceType !== DB_TYPE_EXTERNAL $: gridDatasource = { type: "table", tableId: id, @@ -46,10 +47,7 @@ tables.replaceTable(id, e.detail) // We need to refresh datasources when an external table changes. - // Type "external" may exist - sometimes type is "table" and sometimes it - // is "external" - it has different meanings in different endpoints. - // If we check both these then we hopefully catch all external tables. - if (e.detail?.type === "external" || e.detail?.sql) { + if (e.detail?.sourceType === DB_TYPE_EXTERNAL) { await datasources.fetch() } } diff --git a/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte b/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte index 6068439dbd..6fcba8d418 100644 --- a/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte @@ -17,7 +17,6 @@ let hideAutocolumns = true let data = [] let loading = false - let type = "internal" $: name = view.name $: schema = view.schema @@ -66,7 +65,6 @@ tableId={view.tableId} {data} {loading} - {type} rowCount={10} allowEditing={false} bind:hideAutocolumns diff --git a/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte index 71d971891c..74e255cf7e 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte @@ -10,6 +10,6 @@ diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index e5cf335382..9ebcf2a54c 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -26,6 +26,7 @@ ALLOWABLE_NUMBER_TYPES, SWITCHABLE_TYPES, PrettyRelationshipDefinitions, + DB_TYPE_EXTERNAL, } from "constants/backend" import { getAutoColumnInformation, buildAutoColumn } from "builder/utils" import ConfirmDialog from "components/common/ConfirmDialog.svelte" @@ -254,10 +255,11 @@ !uneditable && editableColumn?.type !== AUTO_TYPE && !editableColumn.autocolumn - $: external = table.type === "external" + $: externalTable = table.sourceType === DB_TYPE_EXTERNAL // in the case of internal tables the sourceId will just be undefined $: tableOptions = $tables.list.filter( - opt => opt.type === table.type && table.sourceId === opt.sourceId + opt => + opt.sourceType === table.sourceType && table.sourceId === opt.sourceId ) $: typeEnabled = !originalName || @@ -409,7 +411,7 @@ editableColumn.type === FieldType.BB_REFERENCE && editableColumn.subtype === FieldSubtype.USERS - if (!external) { + if (!externalTable) { return [ FIELDS.STRING, FIELDS.BARCODEQR, @@ -441,7 +443,7 @@ isUsers ? FIELDS.USERS : FIELDS.USER, ] // no-sql or a spreadsheet - if (!external || table.sql) { + if (!externalTable || table.sql) { fields = [...fields, FIELDS.LINK, FIELDS.ARRAY] } return fields @@ -486,7 +488,7 @@ }) } const newError = {} - if (!external && fieldInfo.name?.startsWith("_")) { + if (!externalTable && fieldInfo.name?.startsWith("_")) { newError.name = `Column name cannot start with an underscore.` } else if (fieldInfo.name && !fieldInfo.name.match(ValidColumnNameRegex)) { newError.name = `Illegal character; must be alpha-numeric.` @@ -498,7 +500,7 @@ newError.name = `Column name already in use.` } - if (fieldInfo.type == "auto" && !fieldInfo.subtype) { + if (fieldInfo.type === "auto" && !fieldInfo.subtype) { newError.subtype = `Auto Column requires a type` } diff --git a/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte index 43751ad944..eb1e7bc7ff 100644 --- a/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte @@ -1,6 +1,6 @@
-
+