1
0
Fork 0
mirror of synced 2024-08-17 19:11:52 +12:00

Adds account locking if user limit is exceeded

This commit is contained in:
jvcalderon 2023-05-11 08:20:52 +02:00
parent d4d1bc03b3
commit 58878ac57c
9 changed files with 89 additions and 42 deletions

View file

@ -0,0 +1,31 @@
<script>
import { Modal, ModalContent, Body } from "@budibase/bbui"
let modal
export let onConfirm
export function show() {
modal.show()
}
export function hide() {
modal.hide()
}
</script>
<Modal bind:this={modal} on:hide={modal}>
<ModalContent
title="Your account is currently locked"
size="S"
showCancelButton={true}
showCloseIcon={false}
confirmText={"View plans"}
{onConfirm}
>
<Body size="S"
>Due to the free plan user limit being exceeded, your account has been
de-activated. Upgrade your plan to re-activate your account.</Body
>
</ModalContent>
</Modal>

View file

@ -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,

View file

@ -6,6 +6,8 @@
export let app
export let lockedAction
const handleDefaultClick = () => {
if (window.innerWidth < 640) {
goToOverview()
@ -29,7 +31,7 @@
}
</script>
<div class="app-row" on:click={handleDefaultClick}>
<div class="app-row" on:click={lockedAction || handleDefaultClick}>
<div class="title">
<div class="app-icon">
<Icon size="L" name={app.icon?.name || "Apps"} color={app.icon?.color} />
@ -58,8 +60,11 @@
<div class="app-row-actions">
<AppLockModal {app} buttonSize="M" />
<Button size="S" secondary on:click={goToOverview}>Manage</Button>
<Button size="S" primary on:click={goToBuilder}>Edit</Button>
<Button size="S" secondary on:click={lockedAction || goToOverview}
>Manage</Button
>
<Button size="S" primary on:click={lockedAction || goToBuilder}>Edit</Button
>
</div>
</div>

View file

@ -133,7 +133,7 @@
</Body>
</Layout>
<Divider />
{#if $licensing.usageMetrics?.dayPasses >= 100}
{#if $licensing.usageMetrics?.dayPasses >= 100 || $licensing.errUserLimit}
<div>
<Layout gap="S" justifyItems="center">
<img class="spaceman" alt="spaceman" src={Spaceman} />

View file

@ -14,6 +14,7 @@
import Spinner from "components/common/Spinner.svelte"
import CreateAppModal from "components/start/CreateAppModal.svelte"
import AppLimitModal from "components/portal/licensing/AppLimitModal.svelte"
import AccountLockedModal from "components/portal/licensing/AccountLockedModal.svelte"
import { store, automationStore } from "builderStore"
import { API } from "api"
@ -28,6 +29,7 @@
let template
let creationModal
let appLimitModal
let accountLockedModal
let creatingApp = false
let searchTerm = ""
let creatingFromTemplate = false
@ -49,6 +51,10 @@
)
$: automationErrors = getAutomationErrors(enrichedApps)
const usersLimitLockAction = $licensing?.errUserLimit
? () => accountLockedModal.show()
: null
const enrichApps = (apps, user, sortBy) => {
const enrichedApps = apps.map(app => ({
...app,
@ -189,6 +195,9 @@
creatingFromTemplate = true
createAppFromTemplateUrl(initInfo.init_template)
}
if (usersLimitLockAction) {
usersLimitLockAction()
}
} catch (error) {
notifications.error("Error getting init info")
}
@ -230,20 +239,30 @@
<Layout noPadding gap="L">
<div class="title">
<div class="buttons">
<Button size="M" cta on:click={initiateAppCreation}>
<Button
size="M"
cta
on:click={usersLimitLockAction || initiateAppCreation}
>
Create new app
</Button>
{#if $apps?.length > 0}
<Button
size="M"
secondary
on:click={$goto("/builder/portal/apps/templates")}
on:click={usersLimitLockAction ||
$goto("/builder/portal/apps/templates")}
>
View templates
</Button>
{/if}
{#if !$apps?.length}
<Button size="L" quiet secondary on:click={initiateAppImport}>
<Button
size="L"
quiet
secondary
on:click={usersLimitLockAction || initiateAppImport}
>
Import app
</Button>
{/if}
@ -267,7 +286,7 @@
<div class="app-table">
{#each filteredApps as app (app.appId)}
<AppRow {app} />
<AppRow {app} lockedAction={usersLimitLockAction} />
{/each}
</div>
</Layout>
@ -294,6 +313,7 @@
</Modal>
<AppLimitModal bind:this={appLimitModal} />
<AccountLockedModal bind:this={accountLockedModal} />
<style>
.title {

View file

@ -30,8 +30,8 @@
$: hasError = userData.find(x => x.error != null)
$: userCount = $licensing.userCount + userData.length
$: willReach = licensing.willReachUserLimit(userCount)
$: willExceed = licensing.willExceedUserLimit(userCount)
$: willReach = licensing.usersLimitReached(userCount)
$: willExceed = licensing.usersLimitExceeded(userCount)
function removeInput(idx) {
userData = userData.filter((e, i) => i !== idx)

View file

@ -25,10 +25,10 @@
$: invalidEmails = []
$: userCount = $licensing.userCount + userEmails.length
$: willExceed = licensing.willExceedUserLimit(userCount)
$: exceed = licensing.usersLimitExceeded(userCount)
$: importDisabled =
!userEmails.length || !validEmails(userEmails) || !usersRole || willExceed
!userEmails.length || !validEmails(userEmails) || !usersRole || exceed
const validEmails = userEmails => {
if ($admin.cloud && userEmails.length > MAX_USERS_UPLOAD_LIMIT) {
@ -93,7 +93,7 @@
</label>
</div>
{#if willExceed}
{#if exceed}
<div class="user-notification">
<Icon name="Info" />
{capitalise($licensing.license.plan.type)} plan is limited to {$licensing.userLimit}

View file

@ -246,7 +246,7 @@
<Body>Add users and control who gets access to your published apps</Body>
</Layout>
<Divider />
{#if $licensing.warnUserLimit}
{#if $licensing.errUserLimit}
<InlineAlert
type="error"
onConfirm={() => {
@ -258,13 +258,9 @@
}}
buttonText={isOwner ? "Upgrade" : "View plans"}
cta
header={`Users will soon be limited to ${staticUserLimit}`}
message={`Our free plan is going to be limited to ${staticUserLimit} users in ${$licensing.userLimitDays}.
This means any users exceeding the limit will be de-activated.
De-activated users will not able to access the builder or any published apps until you upgrade to one of our paid plans.
`}
header="Account de-activated"
message="Due to the free plan user limit being exceeded, your account has been de-activated.
Upgrade your plan to re-activate your account."
/>
{/if}
<div class="controls">

View file

@ -39,21 +39,21 @@ export const createLicensingStore = () => {
userLimit: undefined,
userLimitDays: undefined,
userLimitReached: false,
warnUserLimit: false,
errUserLimit: false,
}
const oneDayInMilliseconds = 86400000
const store = writable(DEFAULT)
function willReachUserLimit(userCount, userLimit) {
function usersLimitReached(userCount, userLimit) {
if (userLimit === UNLIMITED) {
return false
}
return userCount >= userLimit
}
function willExceedUserLimit(userCount, userLimit) {
function usersLimitExceeded(userCount, userLimit) {
if (userLimit === UNLIMITED) {
return false
}
@ -130,11 +130,11 @@ export const createLicensingStore = () => {
})
actions.setUsageMetrics()
},
willReachUserLimit: userCount => {
return willReachUserLimit(userCount, get(store).userLimit)
usersLimitReached: userCount => {
return usersLimitReached(userCount, get(store).userLimit)
},
willExceedUserLimit(userCount) {
return willExceedUserLimit(userCount, get(store).userLimit)
usersLimitExceeded(userCount) {
return usersLimitExceeded(userCount, get(store).userLimit)
},
setUsageMetrics: () => {
if (isEnabled(TENANT_FEATURE_FLAGS.LICENSING)) {
@ -198,11 +198,11 @@ export const createLicensingStore = () => {
const userQuota = license.quotas.usage.static.users
const userLimit = userQuota?.value
const userCount = usage.usageQuota.users
const userLimitReached = willReachUserLimit(userCount, userLimit)
const userLimitExceeded = willExceedUserLimit(userCount, userLimit)
const userLimitReached = usersLimitReached(userCount, userLimit)
const userLimitExceeded = usersLimitExceeded(userCount, userLimit)
const days = dayjs(userQuota?.startDate).diff(dayjs(), "day")
const userLimitDays = days > 1 ? `${days} days` : "1 day"
const warnUserLimit = userQuota?.startDate && userLimitExceeded
const errUserLimit = userQuota?.startDate && userLimitExceeded
store.update(state => {
return {
@ -219,7 +219,7 @@ export const createLicensingStore = () => {
userLimit,
userLimitDays,
userLimitReached,
warnUserLimit,
errUserLimit,
}
})
}