Self Host <-> Licensing integration
This commit is contained in:
parent
eefe4ea2ad
commit
7698e9293c
|
@ -22,25 +22,3 @@ exports.getAccount = async email => {
|
|||
|
||||
return json[0]
|
||||
}
|
||||
|
||||
// TODO: Replace with licensing key
|
||||
exports.getLicense = async tenantId => {
|
||||
const response = await api.get(`/api/license/${tenantId}`, {
|
||||
headers: {
|
||||
[Headers.API_KEY]: env.ACCOUNT_PORTAL_API_KEY,
|
||||
},
|
||||
})
|
||||
|
||||
if (response.status === 404) {
|
||||
// no license for the tenant
|
||||
return
|
||||
}
|
||||
|
||||
if (response.status !== 200) {
|
||||
const text = await response.text()
|
||||
console.error("Error getting license: ", text)
|
||||
throw new Error(`Error getting license for tenant ${tenantId}`)
|
||||
}
|
||||
|
||||
return response.json()
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ exports.Cookies = {
|
|||
|
||||
exports.Headers = {
|
||||
API_KEY: "x-budibase-api-key",
|
||||
LICENSE_KEY: "x-budibase-license-key",
|
||||
API_VER: "x-budibase-api-version",
|
||||
APP_ID: "x-budibase-app-id",
|
||||
TYPE: "x-budibase-type",
|
||||
|
|
|
@ -22,6 +22,7 @@ exports.StaticDatabases = {
|
|||
docs: {
|
||||
apiKeys: "apikeys",
|
||||
usageQuota: "usage_quota",
|
||||
licenseInfo: "license_info",
|
||||
},
|
||||
},
|
||||
// contains information about tenancy and so on
|
||||
|
|
|
@ -55,6 +55,10 @@
|
|||
title: "Updates",
|
||||
href: "/builder/portal/settings/update",
|
||||
},
|
||||
{
|
||||
title: "Upgrade",
|
||||
href: "/builder/portal/settings/upgrade",
|
||||
},
|
||||
])
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -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>
|
|
@ -21,6 +21,7 @@ import { buildTableEndpoints } from "./tables"
|
|||
import { buildTemplateEndpoints } from "./templates"
|
||||
import { buildUserEndpoints } from "./user"
|
||||
import { buildViewEndpoints } from "./views"
|
||||
import { buildLicensingEndpoints } from "./licensing"
|
||||
|
||||
const defaultAPIClientConfig = {
|
||||
/**
|
||||
|
@ -231,5 +232,6 @@ export const createAPIClient = config => {
|
|||
...buildTemplateEndpoints(API),
|
||||
...buildUserEndpoints(API),
|
||||
...buildViewEndpoints(API),
|
||||
...buildLicensingEndpoints(API),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
})
|
||||
},
|
||||
})
|
|
@ -148,6 +148,7 @@
|
|||
"@types/jest": "^26.0.23",
|
||||
"@types/koa": "^2.13.3",
|
||||
"@types/koa-router": "^7.4.2",
|
||||
"@types/lodash": "^4.14.179",
|
||||
"@types/node": "^15.12.4",
|
||||
"@types/oracledb": "^5.2.1",
|
||||
"@typescript-eslint/parser": "5.12.0",
|
||||
|
|
|
@ -52,7 +52,7 @@ interface RunConfig {
|
|||
|
||||
module External {
|
||||
function buildFilters(
|
||||
id: string | undefined,
|
||||
id: string | undefined | string[],
|
||||
filters: SearchFilters,
|
||||
table: Table
|
||||
) {
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
const linkRows = require("../../../db/linkedRows")
|
||||
const { getRowParams, generateTableID } = require("../../../db/utils")
|
||||
const { FieldTypes } = require("../../../constants")
|
||||
const {
|
||||
import { updateLinks, EventType } from "../../../db/linkedRows"
|
||||
import { getRowParams, generateTableID } from "../../../db/utils"
|
||||
import { FieldTypes } from "../../../constants"
|
||||
import {
|
||||
TableSaveFunctions,
|
||||
hasTypeChanged,
|
||||
getTable,
|
||||
handleDataImport,
|
||||
} = require("./utils")
|
||||
const { quotas, StaticQuotaName, QuotaUsageType } = require("@budibase/pro")
|
||||
} from "./utils"
|
||||
const { getAppDB } = require("@budibase/backend-core/context")
|
||||
const env = require("../../../environment")
|
||||
const { cleanupAttachments } = require("../../../utilities/rowProcessor")
|
||||
const { runStaticFormulaChecks } = require("./bulkFormula")
|
||||
import { isTest } from "../../../environment"
|
||||
import { cleanupAttachments } from "../../../utilities/rowProcessor"
|
||||
import { runStaticFormulaChecks } from "./bulkFormula"
|
||||
import * as Pro from "@budibase/pro"
|
||||
|
||||
exports.save = async function (ctx) {
|
||||
export async function save(ctx: any) {
|
||||
const db = getAppDB()
|
||||
const { dataImport, ...rest } = ctx.request.body
|
||||
let tableToSave = {
|
||||
|
@ -80,10 +80,8 @@ exports.save = async function (ctx) {
|
|||
|
||||
// update linked rows
|
||||
try {
|
||||
const linkResp = await linkRows.updateLinks({
|
||||
eventType: oldTable
|
||||
? linkRows.EventType.TABLE_UPDATED
|
||||
: linkRows.EventType.TABLE_SAVE,
|
||||
const linkResp: any = await updateLinks({
|
||||
eventType: oldTable ? EventType.TABLE_UPDATED : EventType.TABLE_SAVE,
|
||||
table: tableToSave,
|
||||
oldTable: oldTable,
|
||||
})
|
||||
|
@ -105,11 +103,11 @@ exports.save = async function (ctx) {
|
|||
|
||||
tableToSave = await tableSaveFunctions.after(tableToSave)
|
||||
// has to run after, make sure it has _id
|
||||
await runStaticFormulaChecks(tableToSave, { oldTable })
|
||||
await runStaticFormulaChecks(tableToSave, { oldTable, deletion: null })
|
||||
return tableToSave
|
||||
}
|
||||
|
||||
exports.destroy = async function (ctx) {
|
||||
export async function destroy(ctx: any) {
|
||||
const db = getAppDB()
|
||||
const tableToDelete = await db.get(ctx.params.tableId)
|
||||
|
||||
|
@ -119,16 +117,18 @@ exports.destroy = async function (ctx) {
|
|||
include_docs: true,
|
||||
})
|
||||
)
|
||||
await db.bulkDocs(rows.rows.map(row => ({ ...row.doc, _deleted: true })))
|
||||
await quotas.updateUsage(
|
||||
await db.bulkDocs(
|
||||
rows.rows.map((row: any) => ({ ...row.doc, _deleted: true }))
|
||||
)
|
||||
await Pro.Licensing.Quotas.updateUsage(
|
||||
-rows.rows.length,
|
||||
StaticQuotaName.ROWS,
|
||||
QuotaUsageType.STATIC
|
||||
Pro.StaticQuotaName.ROWS,
|
||||
Pro.QuotaUsageType.STATIC
|
||||
)
|
||||
|
||||
// update linked rows
|
||||
await linkRows.updateLinks({
|
||||
eventType: linkRows.EventType.TABLE_DELETE,
|
||||
await updateLinks({
|
||||
eventType: EventType.TABLE_DELETE,
|
||||
table: tableToDelete,
|
||||
})
|
||||
|
||||
|
@ -136,10 +136,10 @@ exports.destroy = async function (ctx) {
|
|||
await db.remove(tableToDelete)
|
||||
|
||||
// remove table search index
|
||||
if (!env.isTest()) {
|
||||
if (!isTest()) {
|
||||
const currentIndexes = await db.getIndexes()
|
||||
const existingIndex = currentIndexes.indexes.find(
|
||||
existing => existing.name === `search:${ctx.params.tableId}`
|
||||
(existing: any) => existing.name === `search:${ctx.params.tableId}`
|
||||
)
|
||||
if (existingIndex) {
|
||||
await db.deleteIndex(existingIndex)
|
||||
|
@ -147,12 +147,15 @@ exports.destroy = async function (ctx) {
|
|||
}
|
||||
|
||||
// has to run after, make sure it has _id
|
||||
await runStaticFormulaChecks(tableToDelete, { deletion: true })
|
||||
await runStaticFormulaChecks(tableToDelete, {
|
||||
oldTable: null,
|
||||
deletion: true,
|
||||
})
|
||||
await cleanupAttachments(tableToDelete, { rows })
|
||||
return tableToDelete
|
||||
}
|
||||
|
||||
exports.bulkImport = async function (ctx) {
|
||||
export async function bulkImport(ctx: any) {
|
||||
const table = await getTable(ctx.params.tableId)
|
||||
const { dataImport } = ctx.request.body
|
||||
await handleDataImport(ctx.user, table, dataImport)
|
|
@ -1,34 +1,34 @@
|
|||
const csvParser = require("../../../utilities/csvParser")
|
||||
const {
|
||||
import { transform } from "../../../utilities/csvParser"
|
||||
import {
|
||||
getRowParams,
|
||||
generateRowID,
|
||||
InternalTables,
|
||||
getTableParams,
|
||||
BudibaseInternalDB,
|
||||
} = require("../../../db/utils")
|
||||
const { isEqual } = require("lodash")
|
||||
const { AutoFieldSubTypes, FieldTypes } = require("../../../constants")
|
||||
const {
|
||||
} from "../../../db/utils"
|
||||
import { isEqual } from "lodash"
|
||||
import { AutoFieldSubTypes, FieldTypes } from "../../../constants"
|
||||
import {
|
||||
inputProcessing,
|
||||
cleanupAttachments,
|
||||
} = require("../../../utilities/rowProcessor")
|
||||
const {
|
||||
} from "../../../utilities/rowProcessor"
|
||||
import {
|
||||
USERS_TABLE_SCHEMA,
|
||||
SwitchableTypes,
|
||||
CanSwitchTypes,
|
||||
} = require("../../../constants")
|
||||
const {
|
||||
} from "../../../constants"
|
||||
import {
|
||||
isExternalTable,
|
||||
breakExternalTableId,
|
||||
isSQL,
|
||||
} = require("../../../integrations/utils")
|
||||
const { getViews, saveView } = require("../view/utils")
|
||||
const viewTemplate = require("../view/viewBuilder")
|
||||
const { quotas, StaticQuotaName, QuotaUsageType } = require("@budibase/pro")
|
||||
} from "../../../integrations/utils"
|
||||
import { getViews, saveView } from "../view/utils"
|
||||
import viewTemplate from "../view/viewBuilder"
|
||||
const { getAppDB } = require("@budibase/backend-core/context")
|
||||
const { cloneDeep } = require("lodash/fp")
|
||||
import { cloneDeep } from "lodash/fp"
|
||||
import * as Pro from "@budibase/pro"
|
||||
|
||||
exports.clearColumns = async (table, columnNames) => {
|
||||
export async function clearColumns(table: any, columnNames: any) {
|
||||
const db = getAppDB()
|
||||
const rows = await db.allDocs(
|
||||
getRowParams(table._id, null, {
|
||||
|
@ -36,18 +36,18 @@ exports.clearColumns = async (table, columnNames) => {
|
|||
})
|
||||
)
|
||||
return db.bulkDocs(
|
||||
rows.rows.map(({ doc }) => {
|
||||
columnNames.forEach(colName => delete doc[colName])
|
||||
rows.rows.map(({ doc }: any) => {
|
||||
columnNames.forEach((colName: any) => delete doc[colName])
|
||||
return doc
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
exports.checkForColumnUpdates = async (oldTable, updatedTable) => {
|
||||
export async function checkForColumnUpdates(oldTable: any, updatedTable: any) {
|
||||
const db = getAppDB()
|
||||
let updatedRows = []
|
||||
const rename = updatedTable._rename
|
||||
let deletedColumns = []
|
||||
let deletedColumns: any = []
|
||||
if (oldTable && oldTable.schema && updatedTable.schema) {
|
||||
deletedColumns = Object.keys(oldTable.schema).filter(
|
||||
colName => updatedTable.schema[colName] == null
|
||||
|
@ -61,14 +61,14 @@ exports.checkForColumnUpdates = async (oldTable, updatedTable) => {
|
|||
include_docs: true,
|
||||
})
|
||||
)
|
||||
const rawRows = rows.rows.map(({ doc }) => doc)
|
||||
updatedRows = rawRows.map(row => {
|
||||
const rawRows = rows.rows.map(({ doc }: any) => doc)
|
||||
updatedRows = rawRows.map((row: any) => {
|
||||
row = cloneDeep(row)
|
||||
if (rename) {
|
||||
row[rename.updated] = row[rename.old]
|
||||
delete row[rename.old]
|
||||
} else if (deletedColumns.length !== 0) {
|
||||
deletedColumns.forEach(colName => delete row[colName])
|
||||
deletedColumns.forEach((colName: any) => delete row[colName])
|
||||
}
|
||||
return row
|
||||
})
|
||||
|
@ -76,14 +76,14 @@ exports.checkForColumnUpdates = async (oldTable, updatedTable) => {
|
|||
// cleanup any attachments from object storage for deleted attachment columns
|
||||
await cleanupAttachments(updatedTable, { oldTable, rows: rawRows })
|
||||
// Update views
|
||||
await exports.checkForViewUpdates(updatedTable, rename, deletedColumns)
|
||||
await checkForViewUpdates(updatedTable, rename, deletedColumns)
|
||||
delete updatedTable._rename
|
||||
}
|
||||
return { rows: updatedRows, table: updatedTable }
|
||||
}
|
||||
|
||||
// makes sure the passed in table isn't going to reset the auto ID
|
||||
exports.makeSureTableUpToDate = (table, tableToSave) => {
|
||||
export function makeSureTableUpToDate(table: any, tableToSave: any) {
|
||||
if (!table) {
|
||||
return tableToSave
|
||||
}
|
||||
|
@ -91,7 +91,9 @@ exports.makeSureTableUpToDate = (table, tableToSave) => {
|
|||
tableToSave._rev = table._rev
|
||||
// make sure auto IDs are always updated - these are internal
|
||||
// so the client may not know they have changed
|
||||
for (let [field, column] of Object.entries(table.schema)) {
|
||||
let field: any
|
||||
let column: any
|
||||
for ([field, column] of Object.entries(table.schema)) {
|
||||
if (
|
||||
column.autocolumn &&
|
||||
column.subtype === AutoFieldSubTypes.AUTO_ID &&
|
||||
|
@ -103,14 +105,14 @@ exports.makeSureTableUpToDate = (table, tableToSave) => {
|
|||
return tableToSave
|
||||
}
|
||||
|
||||
exports.handleDataImport = async (user, table, dataImport) => {
|
||||
export async function handleDataImport(user: any, table: any, dataImport: any) {
|
||||
if (!dataImport || !dataImport.csvString) {
|
||||
return table
|
||||
}
|
||||
|
||||
const db = getAppDB()
|
||||
// Populate the table with rows imported from CSV in a bulk update
|
||||
const data = await csvParser.transform({
|
||||
const data = await transform({
|
||||
...dataImport,
|
||||
existingTable: table,
|
||||
})
|
||||
|
@ -120,13 +122,15 @@ exports.handleDataImport = async (user, table, dataImport) => {
|
|||
let row = data[i]
|
||||
row._id = generateRowID(table._id)
|
||||
row.tableId = table._id
|
||||
const processed = inputProcessing(user, table, row, {
|
||||
const processed: any = inputProcessing(user, table, row, {
|
||||
noAutoRelationships: true,
|
||||
})
|
||||
table = processed.table
|
||||
row = processed.row
|
||||
|
||||
for (let [fieldName, schema] of Object.entries(table.schema)) {
|
||||
let fieldName: any
|
||||
let schema: any
|
||||
for ([fieldName, schema] of Object.entries(table.schema)) {
|
||||
// check whether the options need to be updated for inclusion as part of the data import
|
||||
if (
|
||||
schema.type === FieldTypes.OPTIONS &&
|
||||
|
@ -143,26 +147,26 @@ exports.handleDataImport = async (user, table, dataImport) => {
|
|||
finalData.push(row)
|
||||
}
|
||||
|
||||
await quotas.updateUsage(
|
||||
await Pro.Licensing.Quotas.updateUsage(
|
||||
finalData.length,
|
||||
StaticQuotaName.ROWS,
|
||||
QuotaUsageType.STATIC,
|
||||
Pro.StaticQuotaName.ROWS,
|
||||
Pro.QuotaUsageType.STATIC,
|
||||
{
|
||||
dryRun: true,
|
||||
}
|
||||
)
|
||||
await db.bulkDocs(finalData)
|
||||
await quotas.updateUsage(
|
||||
await Pro.Licensing.Quotas.updateUsage(
|
||||
finalData.length,
|
||||
StaticQuotaName.ROWS,
|
||||
QuotaUsageType.STATIC
|
||||
Pro.StaticQuotaName.ROWS,
|
||||
Pro.QuotaUsageType.STATIC
|
||||
)
|
||||
let response = await db.put(table)
|
||||
table._rev = response._rev
|
||||
return table
|
||||
}
|
||||
|
||||
exports.handleSearchIndexes = async table => {
|
||||
export async function handleSearchIndexes(table: any) {
|
||||
const db = getAppDB()
|
||||
// create relevant search indexes
|
||||
if (table.indexes && table.indexes.length > 0) {
|
||||
|
@ -170,12 +174,12 @@ exports.handleSearchIndexes = async table => {
|
|||
const indexName = `search:${table._id}`
|
||||
|
||||
const existingIndex = currentIndexes.indexes.find(
|
||||
existing => existing.name === indexName
|
||||
(existing: any) => existing.name === indexName
|
||||
)
|
||||
|
||||
if (existingIndex) {
|
||||
const currentFields = existingIndex.def.fields.map(
|
||||
field => Object.keys(field)[0]
|
||||
(field: any) => Object.keys(field)[0]
|
||||
)
|
||||
|
||||
// if index fields have changed, delete the original index
|
||||
|
@ -206,7 +210,7 @@ exports.handleSearchIndexes = async table => {
|
|||
return table
|
||||
}
|
||||
|
||||
exports.checkStaticTables = table => {
|
||||
export function checkStaticTables(table: any) {
|
||||
// check user schema has all required elements
|
||||
if (table._id === InternalTables.USER_METADATA) {
|
||||
for (let [key, schema] of Object.entries(USERS_TABLE_SCHEMA.schema)) {
|
||||
|
@ -220,7 +224,13 @@ exports.checkStaticTables = table => {
|
|||
}
|
||||
|
||||
class TableSaveFunctions {
|
||||
constructor({ user, oldTable, dataImport }) {
|
||||
db: any
|
||||
user: any
|
||||
oldTable: any
|
||||
dataImport: any
|
||||
rows: any
|
||||
|
||||
constructor({ user, oldTable, dataImport }: any) {
|
||||
this.db = getAppDB()
|
||||
this.user = user
|
||||
this.oldTable = oldTable
|
||||
|
@ -230,25 +240,25 @@ class TableSaveFunctions {
|
|||
}
|
||||
|
||||
// before anything is done
|
||||
async before(table) {
|
||||
async before(table: any) {
|
||||
if (this.oldTable) {
|
||||
table = exports.makeSureTableUpToDate(this.oldTable, table)
|
||||
table = makeSureTableUpToDate(this.oldTable, table)
|
||||
}
|
||||
table = exports.checkStaticTables(table)
|
||||
table = checkStaticTables(table)
|
||||
return table
|
||||
}
|
||||
|
||||
// when confirmed valid
|
||||
async mid(table) {
|
||||
let response = await exports.checkForColumnUpdates(this.oldTable, table)
|
||||
async mid(table: any) {
|
||||
let response = await checkForColumnUpdates(this.oldTable, table)
|
||||
this.rows = this.rows.concat(response.rows)
|
||||
return table
|
||||
}
|
||||
|
||||
// after saving
|
||||
async after(table) {
|
||||
table = await exports.handleSearchIndexes(table)
|
||||
table = await exports.handleDataImport(this.user, table, this.dataImport)
|
||||
async after(table: any) {
|
||||
table = await handleSearchIndexes(table)
|
||||
table = await handleDataImport(this.user, table, this.dataImport)
|
||||
return table
|
||||
}
|
||||
|
||||
|
@ -257,21 +267,21 @@ class TableSaveFunctions {
|
|||
}
|
||||
}
|
||||
|
||||
exports.getAllInternalTables = async () => {
|
||||
export async function getAllInternalTables() {
|
||||
const db = getAppDB()
|
||||
const internalTables = await db.allDocs(
|
||||
getTableParams(null, {
|
||||
include_docs: true,
|
||||
})
|
||||
)
|
||||
return internalTables.rows.map(tableDoc => ({
|
||||
return internalTables.rows.map((tableDoc: any) => ({
|
||||
...tableDoc.doc,
|
||||
type: "internal",
|
||||
sourceId: BudibaseInternalDB._id,
|
||||
}))
|
||||
}
|
||||
|
||||
exports.getAllExternalTables = async datasourceId => {
|
||||
export async function getAllExternalTables(datasourceId: any) {
|
||||
const db = getAppDB()
|
||||
const datasource = await db.get(datasourceId)
|
||||
if (!datasource || !datasource.entities) {
|
||||
|
@ -280,24 +290,28 @@ exports.getAllExternalTables = async datasourceId => {
|
|||
return datasource.entities
|
||||
}
|
||||
|
||||
exports.getExternalTable = async (datasourceId, tableName) => {
|
||||
const entities = await exports.getAllExternalTables(datasourceId)
|
||||
export async function getExternalTable(datasourceId: any, tableName: any) {
|
||||
const entities = await getAllExternalTables(datasourceId)
|
||||
return entities[tableName]
|
||||
}
|
||||
|
||||
exports.getTable = async tableId => {
|
||||
export async function getTable(tableId: any) {
|
||||
const db = getAppDB()
|
||||
if (isExternalTable(tableId)) {
|
||||
let { datasourceId, tableName } = breakExternalTableId(tableId)
|
||||
const datasource = await db.get(datasourceId)
|
||||
const table = await exports.getExternalTable(datasourceId, tableName)
|
||||
const table = await getExternalTable(datasourceId, tableName)
|
||||
return { ...table, sql: isSQL(datasource) }
|
||||
} else {
|
||||
return db.get(tableId)
|
||||
}
|
||||
}
|
||||
|
||||
exports.checkForViewUpdates = async (table, rename, deletedColumns) => {
|
||||
export async function checkForViewUpdates(
|
||||
table: any,
|
||||
rename: any,
|
||||
deletedColumns: any
|
||||
) {
|
||||
const views = await getViews()
|
||||
const tableViews = views.filter(view => view.meta.tableId === table._id)
|
||||
|
||||
|
@ -321,7 +335,7 @@ exports.checkForViewUpdates = async (table, rename, deletedColumns) => {
|
|||
|
||||
// Update filters if required
|
||||
if (view.meta.filters) {
|
||||
view.meta.filters.forEach(filter => {
|
||||
view.meta.filters.forEach((filter: any) => {
|
||||
if (filter.key === rename.old) {
|
||||
filter.key = rename.updated
|
||||
needsUpdated = true
|
||||
|
@ -329,7 +343,7 @@ exports.checkForViewUpdates = async (table, rename, deletedColumns) => {
|
|||
})
|
||||
}
|
||||
} else if (deletedColumns) {
|
||||
deletedColumns.forEach(column => {
|
||||
deletedColumns.forEach((column: any) => {
|
||||
// Remove calculation statement if required
|
||||
if (view.meta.field === column) {
|
||||
delete view.meta.field
|
||||
|
@ -347,7 +361,7 @@ exports.checkForViewUpdates = async (table, rename, deletedColumns) => {
|
|||
// Remove filters referencing deleted field if required
|
||||
if (view.meta.filters && view.meta.filters.length) {
|
||||
const initialLength = view.meta.filters.length
|
||||
view.meta.filters = view.meta.filters.filter(filter => {
|
||||
view.meta.filters = view.meta.filters.filter((filter: any) => {
|
||||
return filter.key !== column
|
||||
})
|
||||
if (initialLength !== view.meta.filters.length) {
|
||||
|
@ -369,16 +383,20 @@ exports.checkForViewUpdates = async (table, rename, deletedColumns) => {
|
|||
}
|
||||
}
|
||||
|
||||
exports.generateForeignKey = (column, relatedTable) => {
|
||||
export function generateForeignKey(column: any, relatedTable: any) {
|
||||
return `fk_${relatedTable.name}_${column.fieldName}`
|
||||
}
|
||||
|
||||
exports.generateJunctionTableName = (column, table, relatedTable) => {
|
||||
export function generateJunctionTableName(
|
||||
column: any,
|
||||
table: any,
|
||||
relatedTable: any
|
||||
) {
|
||||
return `jt_${table.name}_${relatedTable.name}_${column.name}_${column.fieldName}`
|
||||
}
|
||||
|
||||
exports.foreignKeyStructure = (keyName, meta = null) => {
|
||||
const structure = {
|
||||
export function foreignKeyStructure(keyName: any, meta = null) {
|
||||
const structure: any = {
|
||||
type: FieldTypes.NUMBER,
|
||||
constraints: {},
|
||||
name: keyName,
|
||||
|
@ -389,7 +407,7 @@ exports.foreignKeyStructure = (keyName, meta = null) => {
|
|||
return structure
|
||||
}
|
||||
|
||||
exports.areSwitchableTypes = (type1, type2) => {
|
||||
export function areSwitchableTypes(type1: any, type2: any) {
|
||||
if (
|
||||
SwitchableTypes.indexOf(type1) === -1 &&
|
||||
SwitchableTypes.indexOf(type2) === -1
|
||||
|
@ -406,21 +424,24 @@ exports.areSwitchableTypes = (type1, type2) => {
|
|||
return false
|
||||
}
|
||||
|
||||
exports.hasTypeChanged = (table, oldTable) => {
|
||||
export function hasTypeChanged(table: any, oldTable: any) {
|
||||
if (!oldTable) {
|
||||
return false
|
||||
}
|
||||
for (let [key, field] of Object.entries(oldTable.schema)) {
|
||||
let key: any
|
||||
let field: any
|
||||
for ([key, field] of Object.entries(oldTable.schema)) {
|
||||
const oldType = field.type
|
||||
if (!table.schema[key]) {
|
||||
continue
|
||||
}
|
||||
const newType = table.schema[key].type
|
||||
if (oldType !== newType && !exports.areSwitchableTypes(oldType, newType)) {
|
||||
if (oldType !== newType && !areSwitchableTypes(oldType, newType)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
exports.TableSaveFunctions = TableSaveFunctions
|
||||
const _TableSaveFunctions = TableSaveFunctions
|
||||
export { _TableSaveFunctions as TableSaveFunctions }
|
|
@ -11,7 +11,7 @@ const zlib = require("zlib")
|
|||
const { mainRoutes, staticRoutes } = require("./routes")
|
||||
const pkg = require("../../package.json")
|
||||
const env = require("../environment")
|
||||
const { middleware: licensing } = require("@budibase/pro")
|
||||
const Pro = require("@budibase/pro")
|
||||
|
||||
const router = new Router()
|
||||
|
||||
|
@ -55,7 +55,7 @@ router
|
|||
.use(currentApp)
|
||||
// this middleware will try to use the app ID to determine the tenancy
|
||||
.use(buildAppTenancyMiddleware())
|
||||
.use(licensing())
|
||||
.use(Pro.Middleware.Licensing())
|
||||
.use(auditLog)
|
||||
|
||||
// error handling middleware
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
const rowController = require("../../api/controllers/row")
|
||||
const automationUtils = require("../automationUtils")
|
||||
const { quotas, StaticQuotaName, QuotaUsageType } = require("@budibase/pro")
|
||||
const { buildCtx } = require("./utils")
|
||||
import { save } from "../../api/controllers/row"
|
||||
import { cleanUpRow, getError } from "../automationUtils"
|
||||
import * as Pro from "@budibase/pro"
|
||||
import { buildCtx } from "./utils"
|
||||
|
||||
exports.definition = {
|
||||
export const definition = {
|
||||
name: "Create Row",
|
||||
tagline: "Create a {{inputs.enriched.table.name}} row",
|
||||
icon: "TableRowAddBottom",
|
||||
|
@ -59,7 +59,7 @@ exports.definition = {
|
|||
},
|
||||
}
|
||||
|
||||
exports.run = async function ({ inputs, appId, emitter }) {
|
||||
export async function run({ inputs, appId, emitter }: any) {
|
||||
if (inputs.row == null || inputs.row.tableId == null) {
|
||||
return {
|
||||
success: false,
|
||||
|
@ -69,7 +69,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
|||
}
|
||||
}
|
||||
// have to clean up the row, remove the table from it
|
||||
const ctx = buildCtx(appId, emitter, {
|
||||
const ctx: any = buildCtx(appId, emitter, {
|
||||
body: inputs.row,
|
||||
params: {
|
||||
tableId: inputs.row.tableId,
|
||||
|
@ -77,15 +77,21 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
|||
})
|
||||
|
||||
try {
|
||||
inputs.row = await automationUtils.cleanUpRow(
|
||||
inputs.row.tableId,
|
||||
inputs.row
|
||||
)
|
||||
await quotas.updateUsage(1, StaticQuotaName.ROWS, QuotaUsageType.STATIC, {
|
||||
inputs.row = await cleanUpRow(inputs.row.tableId, inputs.row)
|
||||
await Pro.Licensing.Quotas.updateUsage(
|
||||
1,
|
||||
Pro.StaticQuotaName.ROWS,
|
||||
Pro.QuotaUsageType.STATIC,
|
||||
{
|
||||
dryRun: true,
|
||||
})
|
||||
await rowController.save(ctx)
|
||||
await quotas.updateUsage(1, StaticQuotaName.ROWS, QuotaUsageType.STATIC)
|
||||
}
|
||||
)
|
||||
await save(ctx)
|
||||
await Pro.Licensing.Quotas.updateUsage(
|
||||
1,
|
||||
Pro.StaticQuotaName.ROWS,
|
||||
Pro.QuotaUsageType.STATIC
|
||||
)
|
||||
return {
|
||||
row: inputs.row,
|
||||
response: ctx.body,
|
||||
|
@ -96,7 +102,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
|||
} catch (err) {
|
||||
return {
|
||||
success: false,
|
||||
response: automationUtils.getError(err),
|
||||
response: getError(err),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
const rowController = require("../../api/controllers/row")
|
||||
const { quotas, StaticQuotaName, QuotaUsageType } = require("@budibase/pro")
|
||||
const { buildCtx } = require("./utils")
|
||||
const automationUtils = require("../automationUtils")
|
||||
import { destroy } from "../../api/controllers/row"
|
||||
import * as Pro from "@budibase/pro"
|
||||
import { buildCtx } from "./utils"
|
||||
import { getError } from "../automationUtils"
|
||||
|
||||
exports.definition = {
|
||||
export const definition = {
|
||||
description: "Delete a row from your database",
|
||||
icon: "TableRowRemoveCenter",
|
||||
name: "Delete Row",
|
||||
|
@ -52,7 +52,7 @@ exports.definition = {
|
|||
},
|
||||
}
|
||||
|
||||
exports.run = async function ({ inputs, appId, emitter }) {
|
||||
export async function run({ inputs, appId, emitter }: any) {
|
||||
if (inputs.id == null || inputs.revision == null) {
|
||||
return {
|
||||
success: false,
|
||||
|
@ -62,7 +62,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
|||
}
|
||||
}
|
||||
|
||||
let ctx = buildCtx(appId, emitter, {
|
||||
let ctx: any = buildCtx(appId, emitter, {
|
||||
body: {
|
||||
_id: inputs.id,
|
||||
_rev: inputs.revision,
|
||||
|
@ -73,8 +73,12 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
|||
})
|
||||
|
||||
try {
|
||||
await quotas.updateUsage(-1, StaticQuotaName.ROWS, QuotaUsageType.STATIC)
|
||||
await rowController.destroy(ctx)
|
||||
await Pro.Licensing.Quotas.updateUsage(
|
||||
-1,
|
||||
Pro.StaticQuotaName.ROWS,
|
||||
Pro.QuotaUsageType.STATIC
|
||||
)
|
||||
await destroy(ctx)
|
||||
return {
|
||||
response: ctx.body,
|
||||
row: ctx.row,
|
||||
|
@ -83,7 +87,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
|||
} catch (err) {
|
||||
return {
|
||||
success: false,
|
||||
response: automationUtils.getError(err),
|
||||
response: getError(err),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { quotas, StaticQuotaName, QuotaUsageType } from "@budibase/pro"
|
||||
import * as Pro from "@budibase/pro"
|
||||
const { getUniqueRows } = require("../utilities/usageQuota/rows")
|
||||
const {
|
||||
isExternalTable,
|
||||
|
@ -14,12 +14,12 @@ const METHOD_MAP: any = {
|
|||
|
||||
const DOMAIN_MAP: any = {
|
||||
rows: {
|
||||
name: StaticQuotaName.ROWS,
|
||||
type: QuotaUsageType.STATIC,
|
||||
name: Pro.StaticQuotaName.ROWS,
|
||||
type: Pro.QuotaUsageType.STATIC,
|
||||
},
|
||||
applications: {
|
||||
name: StaticQuotaName.APPS,
|
||||
type: QuotaUsageType.STATIC,
|
||||
name: Pro.StaticQuotaName.APPS,
|
||||
type: Pro.QuotaUsageType.STATIC,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ function getQuotaInfo(url: string) {
|
|||
}
|
||||
|
||||
module.exports = async (ctx: any, next: any) => {
|
||||
if (!quotas.useQuotas()) {
|
||||
if (!Pro.Licensing.Quotas.useQuotas()) {
|
||||
return next()
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ const performRequest = async (
|
|||
const usageContext = {
|
||||
skipNext: false,
|
||||
skipUsage: false,
|
||||
[StaticQuotaName.APPS]: {},
|
||||
[Pro.StaticQuotaName.APPS]: {},
|
||||
}
|
||||
|
||||
const quotaName = quotaInfo.name
|
||||
|
@ -96,7 +96,9 @@ const performRequest = async (
|
|||
|
||||
// run the request
|
||||
if (!usageContext.skipNext) {
|
||||
await quotas.updateUsage(usage, quotaName, quotaInfo.type, { dryRun: true })
|
||||
await Pro.Licensing.Quotas.updateUsage(usage, quotaName, quotaInfo.type, {
|
||||
dryRun: true,
|
||||
})
|
||||
await next()
|
||||
}
|
||||
|
||||
|
@ -112,7 +114,7 @@ const performRequest = async (
|
|||
|
||||
// update the usage
|
||||
if (!usageContext.skipUsage) {
|
||||
await quotas.updateUsage(usage, quotaName, quotaInfo.type)
|
||||
await Pro.Licensing.Quotas.updateUsage(usage, quotaName, quotaInfo.type)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,18 +128,18 @@ const appPreDelete = async (ctx: any, usageContext: any) => {
|
|||
// store the row count to delete
|
||||
const rows = await getUniqueRows([ctx.appId])
|
||||
if (rows.length) {
|
||||
usageContext[StaticQuotaName.APPS] = { rowCount: rows.length }
|
||||
usageContext[Pro.StaticQuotaName.APPS] = { rowCount: rows.length }
|
||||
}
|
||||
}
|
||||
|
||||
const appPostDelete = async (ctx: any, usageContext: any) => {
|
||||
// delete the app rows from usage
|
||||
const rowCount = usageContext[StaticQuotaName.ROWS].rowCount
|
||||
const rowCount = usageContext[Pro.StaticQuotaName.ROWS].rowCount
|
||||
if (rowCount) {
|
||||
await quotas.updateUsage(
|
||||
await Pro.Licensing.Quotas.updateUsage(
|
||||
-rowCount,
|
||||
StaticQuotaName.ROWS,
|
||||
QuotaUsageType.STATIC
|
||||
Pro.StaticQuotaName.ROWS,
|
||||
Pro.QuotaUsageType.STATIC
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -147,24 +149,24 @@ const appPostCreate = async (ctx: any) => {
|
|||
if (ctx.request.body.useTemplate === "true") {
|
||||
const rows = await getUniqueRows([ctx.response.body.appId])
|
||||
const rowCount = rows ? rows.length : 0
|
||||
await quotas.updateUsage(
|
||||
await Pro.Licensing.Quotas.updateUsage(
|
||||
rowCount,
|
||||
StaticQuotaName.ROWS,
|
||||
QuotaUsageType.STATIC
|
||||
Pro.StaticQuotaName.ROWS,
|
||||
Pro.QuotaUsageType.STATIC
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const PRE_DELETE: any = {
|
||||
[StaticQuotaName.APPS]: appPreDelete,
|
||||
[Pro.StaticQuotaName.APPS]: appPreDelete,
|
||||
}
|
||||
|
||||
const POST_DELETE: any = {
|
||||
[StaticQuotaName.APPS]: appPostDelete,
|
||||
[Pro.StaticQuotaName.APPS]: appPostDelete,
|
||||
}
|
||||
|
||||
const PRE_CREATE: any = {}
|
||||
|
||||
const POST_CREATE: any = {
|
||||
[StaticQuotaName.APPS]: appPostCreate,
|
||||
[Pro.StaticQuotaName.APPS]: appPostCreate,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { quotas } from "@budibase/pro"
|
||||
import * as Pro from "@budibase/pro"
|
||||
|
||||
export const runQuotaMigration = async (migration: Function) => {
|
||||
if (!quotas.useQuotas()) {
|
||||
if (!Pro.Licensing.Quotas.useQuotas()) {
|
||||
return
|
||||
}
|
||||
await migration()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { getTenantId } from "@budibase/backend-core/tenancy"
|
||||
import { getAllApps } from "@budibase/backend-core/db"
|
||||
import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
|
||||
import * as Pro from "@budibase/pro"
|
||||
|
||||
export const run = async () => {
|
||||
// get app count
|
||||
|
@ -11,5 +11,9 @@ export const run = async () => {
|
|||
// sync app count
|
||||
const tenantId = getTenantId()
|
||||
console.log(`[Tenant: ${tenantId}] Syncing app count: ${appCount}`)
|
||||
await quotas.setUsage(appCount, StaticQuotaName.APPS, QuotaUsageType.STATIC)
|
||||
await Pro.Licensing.Quotas.setUsage(
|
||||
appCount,
|
||||
Pro.StaticQuotaName.APPS,
|
||||
Pro.QuotaUsageType.STATIC
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { getTenantId } from "@budibase/backend-core/tenancy"
|
||||
import { getAllApps } from "@budibase/backend-core/db"
|
||||
import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
|
||||
import * as Pro from "@budibase/pro"
|
||||
import { getUniqueRows } from "../../../utilities/usageQuota/rows"
|
||||
|
||||
export const run = async () => {
|
||||
|
@ -15,5 +15,9 @@ export const run = async () => {
|
|||
// sync row count
|
||||
const tenantId = getTenantId()
|
||||
console.log(`[Tenant: ${tenantId}] Syncing row count: ${rowCount}`)
|
||||
await quotas.setUsage(rowCount, StaticQuotaName.ROWS, QuotaUsageType.STATIC)
|
||||
await Pro.Licensing.Quotas.setUsage(
|
||||
rowCount,
|
||||
Pro.StaticQuotaName.ROWS,
|
||||
Pro.QuotaUsageType.STATIC
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2438,6 +2438,11 @@
|
|||
"@types/koa-compose" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/lodash@^4.14.179":
|
||||
version "4.14.179"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.179.tgz#490ec3288088c91295780237d2497a3aa9dfb5c5"
|
||||
integrity sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==
|
||||
|
||||
"@types/mime@^1":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"watch": ["src", "../backend-core"],
|
||||
"watch": ["src", "../backend-core", "../../../budibase-pro/packages/pro"],
|
||||
"ext": "js,ts,json",
|
||||
"ignore": ["src/**/*.spec.ts", "src/**/*.spec.js"],
|
||||
"exec": "ts-node src/index.ts"
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
"@types/jest": "^26.0.23",
|
||||
"@types/koa": "^2.13.3",
|
||||
"@types/koa-router": "^7.4.2",
|
||||
"@types/koa__router": "^8.0.11",
|
||||
"@types/node": "^15.12.4",
|
||||
"@typescript-eslint/parser": "5.12.0",
|
||||
"copyfiles": "^2.4.1",
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -8,7 +8,7 @@ const {
|
|||
buildTenancyMiddleware,
|
||||
buildCsrfMiddleware,
|
||||
} = require("@budibase/backend-core/auth")
|
||||
const { middleware: licensing } = require("@budibase/pro")
|
||||
const Pro = require("@budibase/pro")
|
||||
const { errors } = require("@budibase/backend-core")
|
||||
|
||||
const PUBLIC_ENDPOINTS = [
|
||||
|
@ -93,7 +93,7 @@ router
|
|||
.use(buildAuthMiddleware(PUBLIC_ENDPOINTS))
|
||||
.use(buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS))
|
||||
.use(buildCsrfMiddleware({ noCsrfPatterns: NO_CSRF_ENDPOINTS }))
|
||||
.use(licensing())
|
||||
.use(Pro.Middleware.Licensing())
|
||||
// for now no public access is allowed to worker (bar health check)
|
||||
.use((ctx, next) => {
|
||||
if (ctx.publicEndpoint) {
|
||||
|
|
|
@ -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
|
|
@ -8,6 +8,7 @@ const roleRoutes = require("./global/roles")
|
|||
const sessionRoutes = require("./global/sessions")
|
||||
const environmentRoutes = require("./system/environment")
|
||||
const tenantsRoutes = require("./system/tenants")
|
||||
const licenseRoutes = require("./global/license")
|
||||
|
||||
exports.routes = [
|
||||
configRoutes,
|
||||
|
@ -20,4 +21,5 @@ exports.routes = [
|
|||
sessionRoutes,
|
||||
roleRoutes,
|
||||
environmentRoutes,
|
||||
licenseRoutes,
|
||||
]
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue