forked from tsai/budibase
26 changed files with 1605 additions and 191 deletions
@ -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> |
||||
@ -0,0 +1,30 @@ |
|||||
|
export const buildLicensingEndpoints = API => ({ |
||||
|
/** |
||||
|
* Activates a self hosted license key |
||||
|
*/ |
||||
|
activateLicenseKey: async data => { |
||||
|
return API.post({ |
||||
|
url: `/api/global/license/activate`, |
||||
|
body: data, |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Get the license info - metadata about the license including the |
||||
|
* obfuscated license key. |
||||
|
*/ |
||||
|
getLicenseInfo: async () => { |
||||
|
return API.get({ |
||||
|
url: "/api/global/license/info", |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Refreshes the license cache |
||||
|
*/ |
||||
|
refreshLicense: async () => { |
||||
|
return API.post({ |
||||
|
url: "/api/global/license/refresh", |
||||
|
}) |
||||
|
}, |
||||
|
}) |
||||
@ -0,0 +1,25 @@ |
|||||
|
import * as Pro from "@budibase/pro" |
||||
|
|
||||
|
export const activate = async (ctx: any) => { |
||||
|
const { licenseKey } = ctx.request.body |
||||
|
if (!licenseKey) { |
||||
|
ctx.throw(400, "licenseKey is required") |
||||
|
} |
||||
|
|
||||
|
await Pro.Licensing.activateLicenseKey(licenseKey) |
||||
|
ctx.status = 200 |
||||
|
} |
||||
|
|
||||
|
export const refresh = async (ctx: any) => { |
||||
|
await Pro.Licensing.Cache.refresh() |
||||
|
ctx.status = 200 |
||||
|
} |
||||
|
|
||||
|
export const getInfo = async (ctx: any) => { |
||||
|
const licenseInfo = await Pro.Licensing.getLicenseInfo() |
||||
|
if (licenseInfo) { |
||||
|
licenseInfo.licenseKey = "*" |
||||
|
ctx.body = licenseInfo |
||||
|
} |
||||
|
ctx.status = 200 |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
import Router from "@koa/router" |
||||
|
import * as controller from "../../controllers/global/license" |
||||
|
|
||||
|
const router = new Router() |
||||
|
|
||||
|
router |
||||
|
.post("/api/global/license/activate", controller.activate) |
||||
|
.post("/api/global/license/refresh", controller.refresh) |
||||
|
.get("/api/global/license/info", controller.getInfo) |
||||
|
|
||||
|
export = router |
||||
File diff suppressed because it is too large
Loading…
Reference in new issue