developer and published apps usage quota migrations

This commit is contained in:
Rory Powell 2022-03-16 08:18:09 +00:00
parent 9a9b4019ea
commit 953a5f881c
7 changed files with 102 additions and 40 deletions

View File

@ -16,11 +16,11 @@
easing: easing,
})
$: if (value) $progress = value
$: if (value || value === 0) $progress = value
</script>
<div
class:spectrum-ProgressBar--indeterminate={!value}
class:spectrum-ProgressBar--indeterminate={!value && value !== 0}
class:spectrum-ProgressBar--sideLabel={sideLabel}
class="spectrum-ProgressBar spectrum-ProgressBar--size{size}"
value={$progress}
@ -37,7 +37,7 @@
<slot />
</div>
{/if}
{#if value}
{#if value || value === 0}
<div
class="spectrum-FieldLabel spectrum-ProgressBar-percentage spectrum-FieldLabel--size{size}"
>
@ -47,7 +47,7 @@
<div class="spectrum-ProgressBar-track">
<div
class="spectrum-ProgressBar-fill"
style={value ? `width: ${$progress}%` : ""}
style={value || value === 0 ? `width: ${$progress}%` : ""}
/>
</div>
<div class="spectrum-ProgressBar-label" hidden="" />

View File

@ -1,13 +1,15 @@
const { createUserBuildersView } = require("@budibase/backend-core/db")
import * as syncDevelopers from "./usageQuotas/syncDevelopers"
/**
* Date:
* March 2022
*
* Description:
* Create the builder users view.
* Create the builder users view and sync the developer count
*/
export const run = async (db: any) => {
await createUserBuildersView(db)
await syncDevelopers.run()
}

View File

@ -0,0 +1,13 @@
import * as syncPublishedApps from "./usageQuotas/syncPublishedApps"
/**
* Date:
* March 2022
*
* Description:
* Sync the published apps count
*/
export const run = async (db: any) => {
await syncPublishedApps.run()
}

View File

@ -0,0 +1,19 @@
import { getTenantId } from "@budibase/backend-core/tenancy"
import { utils } from "@budibase/backend-core"
import * as Pro from "@budibase/pro"
export const run = async () => {
// get developer count
const developerCount = await utils.getBuildersCount()
// sync developer count
const tenantId = getTenantId()
console.log(
`[Tenant: ${tenantId}] Syncing developer count: ${developerCount}`
)
await Pro.Licensing.Quotas.setUsage(
developerCount,
Pro.StaticQuotaName.DEVELOPERS,
Pro.QuotaUsageType.STATIC
)
}

View File

@ -0,0 +1,21 @@
import { getTenantId } from "@budibase/backend-core/tenancy"
import { getAllApps } from "@budibase/backend-core/db"
import * as Pro from "@budibase/pro"
export const run = async () => {
// get app count
const opts: any = { dev: false }
const prodApps = await getAllApps(opts)
const prodAppCount = prodApps ? prodApps.length : 0
// sync app count
const tenantId = getTenantId()
console.log(
`[Tenant: ${tenantId}] Syncing published app count: ${prodAppCount}`
)
await Pro.Licensing.Quotas.setUsage(
prodAppCount,
Pro.StaticQuotaName.PUBLISHED_APPS,
Pro.QuotaUsageType.STATIC
)
}

View File

@ -8,7 +8,8 @@ const {
import * as userEmailViewCasing from "./functions/userEmailViewCasing"
import * as quota1 from "./functions/quotas1"
import * as appUrls from "./functions/appUrls"
import * as userBuildersView from "./functions/userBuildersView"
import * as developerQuota from "./functions/developerQuota"
import * as publishedAppsQuota from "./functions/publishedAppsQuota"
export interface Migration {
type: string
@ -52,8 +53,13 @@ export const MIGRATIONS: Migration[] = [
},
{
type: MIGRATION_TYPES.GLOBAL,
name: "user_builders_view",
fn: userBuildersView.run,
name: "developer_quota",
fn: developerQuota.run,
},
{
type: MIGRATION_TYPES.GLOBAL,
name: "published_apps_quota",
fn: publishedAppsQuota.run,
},
]

View File

@ -1,7 +1,6 @@
const {
getGlobalUserParams,
StaticDatabases,
generateNewUsageQuotaDoc,
} = require("@budibase/backend-core/db")
const {
hash,
@ -9,9 +8,9 @@ const {
saveUser,
platformLogout,
} = require("@budibase/backend-core/utils")
const { EmailTemplatePurpose } = require("../../../constants")
const { checkInviteCode } = require("../../../utilities/redis")
const { sendEmail } = require("../../../utilities/email")
import { EmailTemplatePurpose } from "../../../constants"
import { checkInviteCode } from "../../../utilities/redis"
import { sendEmail } from "../../../utilities/email"
const { user: userCache } = require("@budibase/backend-core/cache")
const { invalidateSessions } = require("@budibase/backend-core/sessions")
const accounts = require("@budibase/backend-core/accounts")
@ -22,36 +21,37 @@ const {
doesTenantExist,
} = require("@budibase/backend-core/tenancy")
const { removeUserFromInfoDB } = require("@budibase/backend-core/deprovision")
const env = require("../../../environment")
const { syncUserInApps } = require("../../../utilities/appService")
import env from "../../../environment"
import { syncUserInApps } from "../../../utilities/appService"
const { errors } = require("@budibase/backend-core")
import * as Pro from "@budibase/pro"
async function allUsers() {
const allUsers = async () => {
const db = getGlobalDB()
const response = await db.allDocs(
getGlobalUserParams(null, {
include_docs: true,
})
)
return response.rows.map(row => row.doc)
return response.rows.map((row: any) => row.doc)
}
exports.save = async ctx => {
export const save = async (ctx: any) => {
try {
const user = await saveUser(ctx.request.body, getTenantId())
// let server know to sync user
await syncUserInApps(user._id)
ctx.body = user
} catch (err) {
} catch (err: any) {
ctx.throw(err.status || 400, err)
}
}
const parseBooleanParam = param => {
const parseBooleanParam = (param: any) => {
return !(param && param === "false")
}
exports.adminUser = async ctx => {
export const adminUser = async (ctx: any) => {
const { email, password, tenantId } = ctx.request.body
// account portal sends a pre-hashed password - honour param to prevent double hashing
@ -81,10 +81,10 @@ exports.adminUser = async ctx => {
} catch (err) {
// don't worry about errors
}
await db.put(generateNewUsageQuotaDoc())
await db.put(Pro.Licensing.Quotas.generateNewQuotaUsage())
}
if (response.rows.some(row => row.doc.admin)) {
if (response.rows.some((row: any) => row.doc.admin)) {
ctx.throw(
403,
"You cannot initialise once an global user has been created."
@ -106,12 +106,12 @@ exports.adminUser = async ctx => {
}
try {
ctx.body = await saveUser(user, tenantId, hashPassword, requirePassword)
} catch (err) {
} catch (err: any) {
ctx.throw(err.status || 400, err)
}
}
exports.destroy = async ctx => {
export const destroy = async (ctx: any) => {
const db = getGlobalDB()
const dbUser = await db.get(ctx.params.id)
@ -139,10 +139,10 @@ exports.destroy = async ctx => {
}
}
exports.removeAppRole = async ctx => {
export const removeAppRole = async (ctx: any) => {
const { appId } = ctx.params
const db = getGlobalDB()
const users = await allUsers(ctx)
const users = await allUsers()
const bulk = []
const cacheInvalidations = []
for (let user of users) {
@ -162,7 +162,7 @@ exports.removeAppRole = async ctx => {
/**
* Add the attributes that are session based to the current user.
*/
const addSessionAttributesToUser = ctx => {
const addSessionAttributesToUser = (ctx: any) => {
ctx.body.account = ctx.user.account
ctx.body.license = ctx.user.license
ctx.body.budibaseAccess = ctx.user.budibaseAccess
@ -174,7 +174,7 @@ const addSessionAttributesToUser = ctx => {
* Remove the attributes that are session based from the current user,
* so that stale values are not written to the db
*/
const removeSessionAttributesFromUser = ctx => {
const removeSessionAttributesFromUser = (ctx: any) => {
delete ctx.request.body.csrfToken
delete ctx.request.body.account
delete ctx.request.body.accountPortalAccess
@ -182,7 +182,7 @@ const removeSessionAttributesFromUser = ctx => {
delete ctx.request.body.license
}
exports.getSelf = async ctx => {
export const getSelf = async (ctx: any) => {
if (!ctx.user) {
ctx.throw(403, "User not logged in")
}
@ -194,7 +194,7 @@ exports.getSelf = async ctx => {
addSessionAttributesToUser(ctx)
}
exports.updateSelf = async ctx => {
export const updateSelf = async (ctx: any) => {
const db = getGlobalDB()
const user = await db.get(ctx.user._id)
if (ctx.request.body.password) {
@ -224,8 +224,8 @@ exports.updateSelf = async ctx => {
}
// called internally by app server user fetch
exports.fetch = async ctx => {
const users = await allUsers(ctx)
export const fetch = async (ctx: any) => {
const users = await allUsers()
// user hashed password shouldn't ever be returned
for (let user of users) {
if (user) {
@ -236,7 +236,7 @@ exports.fetch = async ctx => {
}
// called internally by app server user find
exports.find = async ctx => {
export const find = async (ctx: any) => {
const db = getGlobalDB()
let user
try {
@ -251,7 +251,7 @@ exports.find = async ctx => {
ctx.body = user
}
exports.tenantUserLookup = async ctx => {
export const tenantUserLookup = async (ctx: any) => {
const id = ctx.params.id
const user = await getTenantUser(id)
if (user) {
@ -261,7 +261,7 @@ exports.tenantUserLookup = async ctx => {
}
}
exports.invite = async ctx => {
export const invite = async (ctx: any) => {
let { email, userInfo } = ctx.request.body
const existing = await getGlobalUserByEmail(email)
if (existing) {
@ -271,20 +271,21 @@ exports.invite = async ctx => {
userInfo = {}
}
userInfo.tenantId = getTenantId()
await sendEmail(email, EmailTemplatePurpose.INVITATION, {
const opts: any = {
subject: "{{ company }} platform invitation",
info: userInfo,
})
}
await sendEmail(email, EmailTemplatePurpose.INVITATION, opts)
ctx.body = {
message: "Invitation has been sent.",
}
}
exports.inviteAccept = async ctx => {
export const inviteAccept = async (ctx: any) => {
const { inviteCode, password, firstName, lastName } = ctx.request.body
try {
// info is an extension of the user object that was stored by global
const { email, info } = await checkInviteCode(inviteCode)
const { email, info }: any = await checkInviteCode(inviteCode)
ctx.body = await saveUser(
{
firstName,
@ -295,7 +296,7 @@ exports.inviteAccept = async ctx => {
},
info.tenantId
)
} catch (err) {
} catch (err: any) {
if (err.code === errors.codes.USAGE_LIMIT_EXCEEDED) {
// explicitly re-throw limit exceeded errors
ctx.throw(400, err)