mirror of https://github.com/Budibase/budibase.git
227 changed files with 6636 additions and 8371 deletions
@ -0,0 +1,11 @@ |
|||
class BudibaseError extends Error { |
|||
constructor(message, type, code) { |
|||
super(message) |
|||
this.type = type |
|||
this.code = code |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
BudibaseError, |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
const licensing = require("./licensing") |
|||
|
|||
const codes = { |
|||
...licensing.codes, |
|||
} |
|||
|
|||
const types = { |
|||
...licensing.types, |
|||
} |
|||
|
|||
const context = { |
|||
...licensing.context, |
|||
} |
|||
|
|||
const getPublicError = err => { |
|||
let error |
|||
if (err.code || err.type) { |
|||
// add generic error information
|
|||
error = { |
|||
code: err.code, |
|||
type: err.type, |
|||
} |
|||
|
|||
if (err.code && context[err.code]) { |
|||
error = { |
|||
...error, |
|||
// get any additional context from this error
|
|||
...context[err.code](err), |
|||
} |
|||
} |
|||
} |
|||
|
|||
return error |
|||
} |
|||
|
|||
module.exports = { |
|||
codes, |
|||
types, |
|||
UsageLimitError: licensing.UsageLimitError, |
|||
getPublicError, |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
const { BudibaseError } = require("./base") |
|||
|
|||
const types = { |
|||
LICENSE_ERROR: "license_error", |
|||
} |
|||
|
|||
const codes = { |
|||
USAGE_LIMIT_EXCEEDED: "usage_limit_exceeded", |
|||
} |
|||
|
|||
const context = { |
|||
[codes.USAGE_LIMIT_EXCEEDED]: err => { |
|||
return { |
|||
limitName: err.limitName, |
|||
} |
|||
}, |
|||
} |
|||
|
|||
class UsageLimitError extends BudibaseError { |
|||
constructor(message, limitName) { |
|||
super(message, types.LICENSE_ERROR, codes.USAGE_LIMIT_EXCEEDED) |
|||
this.limitName = limitName |
|||
this.status = 400 |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
types, |
|||
codes, |
|||
context, |
|||
UsageLimitError, |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
const env = require("../environment") |
|||
const tenancy = require("../tenancy") |
|||
|
|||
/** |
|||
* Read the TENANT_FEATURE_FLAGS env var and return an array of features flags for each tenant. |
|||
* The env var is formatted as: |
|||
* tenant1:feature1:feature2,tenant2:feature1 |
|||
*/ |
|||
const getFeatureFlags = () => { |
|||
if (!env.TENANT_FEATURE_FLAGS) { |
|||
return |
|||
} |
|||
|
|||
const tenantFeatureFlags = {} |
|||
|
|||
env.TENANT_FEATURE_FLAGS.split(",").forEach(tenantToFeatures => { |
|||
const [tenantId, ...features] = tenantToFeatures.split(":") |
|||
|
|||
features.forEach(feature => { |
|||
if (!tenantFeatureFlags[tenantId]) { |
|||
tenantFeatureFlags[tenantId] = [] |
|||
} |
|||
tenantFeatureFlags[tenantId].push(feature) |
|||
}) |
|||
}) |
|||
|
|||
return tenantFeatureFlags |
|||
} |
|||
|
|||
const TENANT_FEATURE_FLAGS = getFeatureFlags() |
|||
|
|||
exports.isEnabled = featureFlag => { |
|||
const tenantId = tenancy.getTenantId() |
|||
|
|||
return ( |
|||
TENANT_FEATURE_FLAGS && |
|||
TENANT_FEATURE_FLAGS[tenantId] && |
|||
TENANT_FEATURE_FLAGS[tenantId].includes(featureFlag) |
|||
) |
|||
} |
|||
|
|||
exports.getTenantFeatureFlags = tenantId => { |
|||
if (TENANT_FEATURE_FLAGS && TENANT_FEATURE_FLAGS[tenantId]) { |
|||
return TENANT_FEATURE_FLAGS[tenantId] |
|||
} |
|||
|
|||
return [] |
|||
} |
|||
|
|||
exports.FeatureFlag = { |
|||
LICENSING: "LICENSING", |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,78 @@ |
|||
<script> |
|||
import { Select, Label, Combobox } from "@budibase/bbui" |
|||
import { onMount } from "svelte" |
|||
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" |
|||
import { currentAsset, store } from "builderStore" |
|||
import { |
|||
getActionProviderComponents, |
|||
buildFormSchema, |
|||
} from "builderStore/dataBinding" |
|||
import { findComponent } from "builderStore/componentUtils" |
|||
|
|||
export let parameters |
|||
export let bindings = [] |
|||
|
|||
const typeOptions = [ |
|||
{ |
|||
label: "Set value", |
|||
value: "set", |
|||
}, |
|||
{ |
|||
label: "Reset to default value", |
|||
value: "reset", |
|||
}, |
|||
] |
|||
|
|||
$: formComponent = findComponent($currentAsset.props, parameters.componentId) |
|||
$: formSchema = buildFormSchema(formComponent) |
|||
$: fieldOptions = Object.keys(formSchema || {}) |
|||
$: actionProviders = getActionProviderComponents( |
|||
$currentAsset, |
|||
$store.selectedComponentId, |
|||
"ValidateForm" |
|||
) |
|||
|
|||
onMount(() => { |
|||
if (!parameters.type) { |
|||
parameters.type = "set" |
|||
} |
|||
}) |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<Label small>Form</Label> |
|||
<Select |
|||
bind:value={parameters.componentId} |
|||
options={actionProviders} |
|||
getOptionLabel={x => x._instanceName} |
|||
getOptionValue={x => x._id} |
|||
/> |
|||
<Label small>Type</Label> |
|||
<Select |
|||
placeholder={null} |
|||
bind:value={parameters.type} |
|||
options={typeOptions} |
|||
/> |
|||
<Label small>Field</Label> |
|||
<Combobox bind:value={parameters.field} options={fieldOptions} /> |
|||
{#if parameters.type === "set"} |
|||
<Label small>Value</Label> |
|||
<DrawerBindableInput |
|||
{bindings} |
|||
value={parameters.value} |
|||
on:change={e => (parameters.value = e.detail)} |
|||
/> |
|||
{/if} |
|||
</div> |
|||
|
|||
<style> |
|||
.root { |
|||
display: grid; |
|||
column-gap: var(--spacing-l); |
|||
row-gap: var(--spacing-s); |
|||
grid-template-columns: 60px 1fr; |
|||
align-items: center; |
|||
max-width: 400px; |
|||
margin: 0 auto; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,14 @@ |
|||
import { auth } from "../stores/portal" |
|||
import { get } from "svelte/store" |
|||
|
|||
export const FEATURE_FLAGS = { |
|||
LICENSING: "LICENSING", |
|||
} |
|||
|
|||
export const isEnabled = featureFlag => { |
|||
const user = get(auth).user |
|||
if (user?.featureFlags?.includes(featureFlag)) { |
|||
return true |
|||
} |
|||
return false |
|||
} |
|||
@ -0,0 +1,151 @@ |
|||
<script> |
|||
import { |
|||
Layout, |
|||
Heading, |
|||
Body, |
|||
Divider, |
|||
Link, |
|||
Button, |
|||
Input, |
|||
Label, |
|||
notifications, |
|||
} from "@budibase/bbui" |
|||
import { auth, admin } from "stores/portal" |
|||
import { redirect } from "@roxi/routify" |
|||
import { processStringSync } from "@budibase/string-templates" |
|||
import { API } from "api" |
|||
import { onMount } from "svelte" |
|||
|
|||
$: license = $auth.user.license |
|||
$: upgradeUrl = `${$admin.accountPortalUrl}/portal/upgrade` |
|||
|
|||
$: activateDisabled = !licenseKey || licenseKeyDisabled |
|||
|
|||
let licenseInfo |
|||
|
|||
let licenseKeyDisabled = false |
|||
let licenseKeyType = "text" |
|||
let licenseKey = "" |
|||
|
|||
// Make sure page can't be visited directly in cloud |
|||
$: { |
|||
if ($admin.cloud) { |
|||
$redirect("../../portal") |
|||
} |
|||
} |
|||
|
|||
const activate = async () => { |
|||
await API.activateLicenseKey({ licenseKey }) |
|||
await auth.getSelf() |
|||
await setLicenseInfo() |
|||
notifications.success("Successfully activated") |
|||
} |
|||
|
|||
const refresh = async () => { |
|||
try { |
|||
await API.refreshLicense() |
|||
await auth.getSelf() |
|||
notifications.success("Refreshed license") |
|||
} catch (err) { |
|||
console.error(err) |
|||
notifications.error("Error refreshing license") |
|||
} |
|||
} |
|||
|
|||
// deactivate the license key field if there is a license key set |
|||
$: { |
|||
if (licenseInfo?.licenseKey) { |
|||
licenseKey = "**********************************************" |
|||
licenseKeyType = "password" |
|||
licenseKeyDisabled = true |
|||
activateDisabled = true |
|||
} |
|||
} |
|||
|
|||
const setLicenseInfo = async () => { |
|||
licenseInfo = await API.getLicenseInfo() |
|||
} |
|||
|
|||
onMount(async () => { |
|||
await setLicenseInfo() |
|||
}) |
|||
</script> |
|||
|
|||
{#if $auth.isAdmin} |
|||
<Layout noPadding> |
|||
<Layout gap="XS" noPadding> |
|||
<Heading size="M">Upgrade</Heading> |
|||
<Body size="M"> |
|||
{#if license.plan.type === "free"} |
|||
Upgrade your budibase installation to unlock additional features. To |
|||
subscribe to a plan visit your <Link size="L" href={upgradeUrl} |
|||
>Account</Link |
|||
>. |
|||
{:else} |
|||
To manage your plan visit your <Link size="L" href={upgradeUrl} |
|||
>Account</Link |
|||
>. |
|||
{/if} |
|||
</Body> |
|||
</Layout> |
|||
<Divider size="S" /> |
|||
<Layout gap="XS" noPadding> |
|||
<Heading size="S">Activate</Heading> |
|||
<Body size="S">Enter your license key below to activate your plan</Body> |
|||
</Layout> |
|||
<Layout noPadding> |
|||
<div class="fields"> |
|||
<div class="field"> |
|||
<Label size="L">License Key</Label> |
|||
<Input |
|||
thin |
|||
bind:value={licenseKey} |
|||
type={licenseKeyType} |
|||
disabled={licenseKeyDisabled} |
|||
/> |
|||
</div> |
|||
</div> |
|||
<div> |
|||
<Button cta on:click={activate} disabled={activateDisabled} |
|||
>Activate</Button |
|||
> |
|||
</div> |
|||
</Layout> |
|||
<Divider size="S" /> |
|||
<Layout gap="L" noPadding> |
|||
<Layout gap="S" noPadding> |
|||
<Heading size="S">Plan</Heading> |
|||
<Layout noPadding gap="XXS"> |
|||
<Body size="S">You are currently on the {license.plan.type} plan</Body |
|||
> |
|||
<Body size="XS"> |
|||
{processStringSync( |
|||
"Updated {{ duration time 'millisecond' }} ago", |
|||
{ |
|||
time: |
|||
new Date().getTime() - |
|||
new Date(license.refreshedAt).getTime(), |
|||
} |
|||
)} |
|||
</Body> |
|||
</Layout> |
|||
</Layout> |
|||
<div> |
|||
<Button secondary on:click={refresh}>Refresh</Button> |
|||
</div> |
|||
</Layout> |
|||
</Layout> |
|||
{/if} |
|||
|
|||
<style> |
|||
.fields { |
|||
display: grid; |
|||
grid-gap: var(--spacing-m); |
|||
} |
|||
.field { |
|||
display: grid; |
|||
grid-template-columns: 100px 1fr; |
|||
grid-gap: var(--spacing-l); |
|||
align-items: center; |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
@ -1,14 +0,0 @@ |
|||
{ |
|||
// Use IntelliSense to learn about possible attributes. |
|||
// Hover to view descriptions of existing attributes. |
|||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 |
|||
"version": "0.2.0", |
|||
"configurations": [ |
|||
{ |
|||
"type": "node", |
|||
"request": "launch", |
|||
"name": "Publish Dev", |
|||
"program": "${workspaceFolder}/scripts/publishDev.js" |
|||
} |
|||
] |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue