diff --git a/hosting/proxy/nginx.prod.conf b/hosting/proxy/nginx.prod.conf
index dce1a71918..001a08a9a6 100644
--- a/hosting/proxy/nginx.prod.conf
+++ b/hosting/proxy/nginx.prod.conf
@@ -55,7 +55,7 @@ http {
set $csp_style "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://rsms.me https://maxcdn.bootstrapcdn.com";
set $csp_object "object-src 'none'";
set $csp_base_uri "base-uri 'self'";
- set $csp_connect "connect-src 'self' https://*.budibase.net https://api-iam.intercom.io https://api-iam.intercom.io https://api-ping.intercom.io https://app.posthog.com wss://nexus-websocket-a.intercom.io wss://nexus-websocket-b.intercom.io https://nexus-websocket-a.intercom.io https://nexus-websocket-b.intercom.io https://uploads.intercomcdn.com https://uploads.intercomusercontent.com https://*.s3.amazonaws.com https://*.s3.us-east-2.amazonaws.com https://*.s3.us-east-1.amazonaws.com https://*.s3.us-west-1.amazonaws.com https://*.s3.us-west-2.amazonaws.com https://*.s3.af-south-1.amazonaws.com https://*.s3.ap-east-1.amazonaws.com https://*.s3.ap-southeast-3.amazonaws.com https://*.s3.ap-south-1.amazonaws.com https://*.s3.ap-northeast-3.amazonaws.com https://*.s3.ap-northeast-2.amazonaws.com https://*.s3.ap-southeast-1.amazonaws.com https://*.s3.ap-southeast-2.amazonaws.com https://*.s3.ap-northeast-1.amazonaws.com https://*.s3.ca-central-1.amazonaws.com https://*.s3.cn-north-1.amazonaws.com https://*.s3.cn-northwest-1.amazonaws.com https://*.s3.eu-central-1.amazonaws.com https://*.s3.eu-west-1.amazonaws.com https://*.s3.eu-west-2.amazonaws.com https://*.s3.eu-south-1.amazonaws.com https://*.s3.eu-west-3.amazonaws.com https://*.s3.eu-north-1.amazonaws.com https://*.s3.sa-east-1.amazonaws.com https://*.s3.me-south-1.amazonaws.com https://*.s3.us-gov-east-1.amazonaws.com https://*.s3.us-gov-west-1.amazonaws.com https://api.github.com";
+ set $csp_connect "connect-src 'self' https://*.budibase.net https://api-iam.intercom.io https://api-iam.intercom.io https://api-ping.intercom.io https://app.posthog.com wss://nexus-websocket-a.intercom.io wss://nexus-websocket-b.intercom.io https://nexus-websocket-a.intercom.io https://nexus-websocket-b.intercom.io https://uploads.intercomcdn.com https://uploads.intercomusercontent.com https://*.amazonaws.com https://*.s3.amazonaws.com https://*.s3.us-east-2.amazonaws.com https://*.s3.us-east-1.amazonaws.com https://*.s3.us-west-1.amazonaws.com https://*.s3.us-west-2.amazonaws.com https://*.s3.af-south-1.amazonaws.com https://*.s3.ap-east-1.amazonaws.com https://*.s3.ap-southeast-3.amazonaws.com https://*.s3.ap-south-1.amazonaws.com https://*.s3.ap-northeast-3.amazonaws.com https://*.s3.ap-northeast-2.amazonaws.com https://*.s3.ap-southeast-1.amazonaws.com https://*.s3.ap-southeast-2.amazonaws.com https://*.s3.ap-northeast-1.amazonaws.com https://*.s3.ca-central-1.amazonaws.com https://*.s3.cn-north-1.amazonaws.com https://*.s3.cn-northwest-1.amazonaws.com https://*.s3.eu-central-1.amazonaws.com https://*.s3.eu-west-1.amazonaws.com https://*.s3.eu-west-2.amazonaws.com https://*.s3.eu-south-1.amazonaws.com https://*.s3.eu-west-3.amazonaws.com https://*.s3.eu-north-1.amazonaws.com https://*.s3.sa-east-1.amazonaws.com https://*.s3.me-south-1.amazonaws.com https://*.s3.us-gov-east-1.amazonaws.com https://*.s3.us-gov-west-1.amazonaws.com https://api.github.com";
set $csp_font "font-src 'self' data: https://cdn.jsdelivr.net https://fonts.gstatic.com https://rsms.me https://maxcdn.bootstrapcdn.com https://js.intercomcdn.com https://fonts.intercomcdn.com";
set $csp_frame "frame-src 'self' https:";
set $csp_img "img-src http: https: data: blob:";
@@ -82,6 +82,12 @@ http {
set $couchdb ${COUCHDB_UPSTREAM_URL};
set $watchtower ${WATCHTOWER_UPSTREAM_URL};
+ location /health {
+ access_log off;
+ add_header 'Content-Type' 'application/json';
+ return 200 '{ "status": "OK" }';
+ }
+
location /app {
proxy_pass $apps;
}
diff --git a/lerna.json b/lerna.json
index 664712d568..e56d307592 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "2.6.8-alpha.11",
+ "version": "2.6.16-alpha.2",
"npmClient": "yarn",
"packages": [
"packages/backend-core",
diff --git a/packages/backend-core/src/constants/db.ts b/packages/backend-core/src/constants/db.ts
index aa40f13775..be49b9f261 100644
--- a/packages/backend-core/src/constants/db.ts
+++ b/packages/backend-core/src/constants/db.ts
@@ -21,7 +21,7 @@ export enum ViewName {
AUTOMATION_LOGS = "automation_logs",
ACCOUNT_BY_EMAIL = "account_by_email",
PLATFORM_USERS_LOWERCASE = "platform_users_lowercase",
- USER_BY_GROUP = "by_group_user",
+ USER_BY_GROUP = "user_by_group",
APP_BACKUP_BY_TRIGGER = "by_trigger",
}
diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts
index 155f09e6d9..9163dfeba6 100644
--- a/packages/backend-core/src/environment.ts
+++ b/packages/backend-core/src/environment.ts
@@ -69,10 +69,10 @@ function findVersion() {
try {
const packageJsonFile = findFileInAncestors("package.json", process.cwd())
const content = readFileSync(packageJsonFile!, "utf-8")
- const version = JSON.parse(content).version
- return version
+ return JSON.parse(content).version
} catch {
- throw new Error("Cannot find a valid version in its package.json")
+ // throwing an error here is confusing/causes backend-core to be hard to import
+ return undefined
}
}
@@ -95,7 +95,7 @@ const environment = {
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
SALT_ROUNDS: process.env.SALT_ROUNDS,
REDIS_URL: process.env.REDIS_URL || "localhost:6379",
- REDIS_PASSWORD: process.env.REDIS_PASSWORD || "budibase",
+ REDIS_PASSWORD: process.env.REDIS_PASSWORD,
REDIS_CLUSTERED: process.env.REDIS_CLUSTERED,
MOCK_REDIS: process.env.MOCK_REDIS,
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
diff --git a/packages/builder/package.json b/packages/builder/package.json
index ccd3b94c01..d39fb69dd0 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -69,6 +69,7 @@
"@codemirror/state": "^6.2.0",
"@codemirror/theme-one-dark": "^6.1.2",
"@codemirror/view": "^6.11.2",
+ "@budibase/types": "0.0.1",
"@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1",
diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte
index 460a02a9b1..642f10ad35 100644
--- a/packages/builder/src/components/backend/DataTable/Table.svelte
+++ b/packages/builder/src/components/backend/DataTable/Table.svelte
@@ -23,6 +23,7 @@
export let disableSorting = false
export let customPlaceholder = false
export let allowClickRows
+ export let allowEditing = true
const dispatch = createEventDispatcher()
@@ -110,6 +111,7 @@
{rowCount}
{disableSorting}
{customPlaceholder}
+ allowEditRows={allowEditing}
showAutoColumns={!hideAutocolumns}
{allowClickRows}
on:clickrelationship={e => selectRelationship(e.detail)}
diff --git a/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte b/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte
index 056bc33994..d239cabd59 100644
--- a/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte
+++ b/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte
@@ -58,6 +58,7 @@
{loading}
{type}
rowCount={10}
+ allowEditing={false}
bind:hideAutocolumns
>
diff --git a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte
index 94b9020203..f1337e88d4 100644
--- a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte
+++ b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte
@@ -6,7 +6,8 @@
import NavItem from "components/common/NavItem.svelte"
import { goto, isActive } from "@roxi/routify"
- const alphabetical = (a, b) => a.name?.toLowerCase() > b.name?.toLowerCase()
+ const alphabetical = (a, b) =>
+ a.name?.toLowerCase() > b.name?.toLowerCase() ? 1 : -1
export let sourceId
diff --git a/packages/builder/src/components/common/NavItem.svelte b/packages/builder/src/components/common/NavItem.svelte
index 1fd43e2c70..73a8d3d647 100644
--- a/packages/builder/src/components/common/NavItem.svelte
+++ b/packages/builder/src/components/common/NavItem.svelte
@@ -17,6 +17,7 @@
export let highlighted = false
export let rightAlignIcon = false
export let id
+ export let showTooltip = false
const scrollApi = getContext("scroll")
const dispatch = createEventDispatcher()
@@ -84,7 +85,7 @@
{/if}
-
{text}
+ {text}
{#if withActions}
diff --git a/packages/builder/src/components/portal/licensing/AccountLockedModal.svelte b/packages/builder/src/components/portal/licensing/AccountLockedModal.svelte
new file mode 100644
index 0000000000..a33d982627
--- /dev/null
+++ b/packages/builder/src/components/portal/licensing/AccountLockedModal.svelte
@@ -0,0 +1,31 @@
+
+
+
+
+ Due to the free plan user limit being exceeded, your account has been
+ de-activated. Upgrade your plan to re-activate your account.
+
+
diff --git a/packages/builder/src/components/portal/licensing/licensingBanners.js b/packages/builder/src/components/portal/licensing/licensingBanners.js
index dafa8cfaed..ea4a1bb946 100644
--- a/packages/builder/src/components/portal/licensing/licensingBanners.js
+++ b/packages/builder/src/components/portal/licensing/licensingBanners.js
@@ -3,7 +3,6 @@ import { temporalStore } from "builderStore"
import { admin, auth, licensing } from "stores/portal"
import { get } from "svelte/store"
import { BANNER_TYPES } from "@budibase/bbui"
-import { capitalise } from "helpers"
const oneDayInSeconds = 86400
@@ -146,23 +145,19 @@ const buildUsersAboveLimitBanner = EXPIRY_KEY => {
const userLicensing = get(licensing)
return {
key: EXPIRY_KEY,
- type: BANNER_TYPES.WARNING,
+ type: BANNER_TYPES.NEGATIVE,
onChange: () => {
defaultCacheFn(EXPIRY_KEY)
},
criteria: () => {
- return userLicensing.warnUserLimit
+ return userLicensing.errUserLimit
},
- message: `${capitalise(
- userLicensing.license.plan.type
- )} plan changes - Users will be limited to ${
- userLicensing.userLimit
- } users in ${userLicensing.userLimitDays}`,
+ message: "Your Budibase account is de-activated. Upgrade your plan",
...{
- extraButtonText: "Find out more",
+ extraButtonText: "View plans",
extraButtonAction: () => {
defaultCacheFn(ExpiringKeys.LICENSING_USERS_ABOVE_LIMIT_BANNER)
- window.location.href = "/builder/portal/users/users"
+ window.location.href = "https://budibase.com/pricing/"
},
},
showCloseButton: true,
diff --git a/packages/builder/src/components/start/AppRow.svelte b/packages/builder/src/components/start/AppRow.svelte
index 34d083a096..194f897fdc 100644
--- a/packages/builder/src/components/start/AppRow.svelte
+++ b/packages/builder/src/components/start/AppRow.svelte
@@ -6,6 +6,8 @@
export let app
+ export let lockedAction
+
const handleDefaultClick = () => {
if (window.innerWidth < 640) {
goToOverview()
@@ -29,7 +31,7 @@
}
-
+
@@ -58,8 +60,11 @@
-
-
+
+
diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
index be8237f616..65963b828d 100644
--- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte
@@ -28,13 +28,16 @@
let inviting = false
let searchFocus = false
+ // Initially filter entities without app access
+ // Show all when false
+ let filterByAppAccess = true
+
let appInvites = []
let filteredInvites = []
let filteredUsers = []
let filteredGroups = []
let selectedGroup
let userOnboardResponse = null
-
let userLimitReachedModal
$: queryIsEmail = emailValidator(query) === true
@@ -52,15 +55,32 @@
}
const filterInvites = async query => {
- appInvites = await getInvites()
- if (!query || query == "") {
- filteredInvites = appInvites
+ if (!prodAppId) {
return
}
- filteredInvites = appInvites.filter(invite => invite.email.includes(query))
+
+ appInvites = await getInvites()
+
+ //On Focus behaviour
+ if (!filterByAppAccess && !query) {
+ filteredInvites =
+ appInvites.length > 100 ? appInvites.slice(0, 100) : [...appInvites]
+ return
+ }
+
+ filteredInvites = appInvites.filter(invite => {
+ const inviteInfo = invite.info?.apps
+ if (!query && inviteInfo && prodAppId) {
+ return Object.keys(inviteInfo).includes(prodAppId)
+ }
+ return invite.email.includes(query)
+ })
}
- $: filterInvites(query)
+ $: filterByAppAccess, prodAppId, filterInvites(query)
+ $: if (searchFocus === true) {
+ filterByAppAccess = false
+ }
const usersFetch = fetchData({
API,
@@ -79,9 +99,9 @@
}
await usersFetch.update({
query: {
- appId: query ? null : prodAppId,
+ appId: query || !filterByAppAccess ? null : prodAppId,
email: query,
- paginated: query ? null : false,
+ paginated: query || !filterByAppAccess ? null : false,
},
})
await usersFetch.refresh()
@@ -107,7 +127,12 @@
}
const debouncedUpdateFetch = Utils.debounce(searchUsers, 250)
- $: debouncedUpdateFetch(query, $store.builderSidePanel, loaded)
+ $: debouncedUpdateFetch(
+ query,
+ $store.builderSidePanel,
+ loaded,
+ filterByAppAccess
+ )
const updateAppUser = async (user, role) => {
if (!prodAppId) {
@@ -182,9 +207,10 @@
}
const searchGroups = (userGroups, query) => {
- let filterGroups = query?.length
- ? userGroups
- : getAppGroups(userGroups, prodAppId)
+ let filterGroups =
+ query?.length || !filterByAppAccess
+ ? userGroups
+ : getAppGroups(userGroups, prodAppId)
return filterGroups
.filter(group => {
if (!query?.length) {
@@ -214,7 +240,7 @@
}
// Adds the 'role' attribute and sets it to the current app.
- $: enrichedGroups = getEnrichedGroups($groups)
+ $: enrichedGroups = getEnrichedGroups($groups, filterByAppAccess)
$: filteredGroups = searchGroups(enrichedGroups, query)
$: groupUsers = buildGroupUsers(filteredGroups, filteredUsers)
$: allUsers = [...filteredUsers, ...groupUsers]
@@ -226,7 +252,7 @@
specific roles for the app.
*/
const buildGroupUsers = (userGroups, filteredUsers) => {
- if (query) {
+ if (query || !filterByAppAccess) {
return []
}
// Must exclude users who have explicit privileges
@@ -321,12 +347,12 @@
[prodAppId]: role,
},
})
- await filterInvites()
+ await filterInvites(query)
}
const onUninviteAppUser = async invite => {
await uninviteAppUser(invite)
- await filterInvites()
+ await filterInvites(query)
}
// Purge only the app from the invite or recind the invite if only 1 app remains?
@@ -351,7 +377,6 @@
onMount(() => {
rendered = true
- searchFocus = true
})
function handleKeyDown(evt) {
@@ -417,7 +442,6 @@
autocomplete="off"
disabled={inviting}
value={query}
- autofocus
on:input={e => {
query = e.target.value.trim()
}}
@@ -428,16 +452,20 @@
{
+ if (!filterByAppAccess) {
+ filterByAppAccess = true
+ }
if (!query) {
return
}
query = null
userOnboardResponse = null
+ filterByAppAccess = true
}}
>
-
+
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenListPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenListPanel.svelte
index a6fd9089b1..06f9f86eb6 100644
--- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenListPanel.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenListPanel.svelte
@@ -59,6 +59,7 @@
text={screen.routing.route}
on:click={() => store.actions.screens.select(screen._id)}
rightAlignIcon
+ showTooltip
>
diff --git a/packages/builder/src/pages/builder/apps/index.svelte b/packages/builder/src/pages/builder/apps/index.svelte
index 23f4df5bb5..4b77671345 100644
--- a/packages/builder/src/pages/builder/apps/index.svelte
+++ b/packages/builder/src/pages/builder/apps/index.svelte
@@ -133,7 +133,7 @@