diff --git a/packages/backend-core/src/middleware/authenticated.ts b/packages/backend-core/src/middleware/authenticated.ts index 54e41bff57..0e1b31b9b7 100644 --- a/packages/backend-core/src/middleware/authenticated.ts +++ b/packages/backend-core/src/middleware/authenticated.ts @@ -112,11 +112,6 @@ export = ( } user.csrfToken = session.csrfToken - // check day passes for the current user - if (opts.checkDayPass) { - await opts.checkDayPass(ctx, user, session.tenantId) - } - if ( !session.lastAccessedAt || session.lastAccessedAt < timeMinusOneMinute() diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.js index ffcea09985..4cd574f557 100644 --- a/packages/server/src/api/index.js +++ b/packages/server/src/api/index.js @@ -11,7 +11,7 @@ const zlib = require("zlib") const { mainRoutes, staticRoutes, publicRoutes } = require("./routes") const pkg = require("../../package.json") const env = require("../environment") -const { middleware: pro, quotas } = require("@budibase/pro") +const { middleware: pro } = require("@budibase/pro") const { shutdown } = require("./routes/public") const router = new Router() @@ -44,7 +44,6 @@ router .use( buildAuthMiddleware(null, { publicAllowed: true, - checkDayPass: quotas.checkDayPass, }) ) // nothing in the server should allow query string tenants @@ -55,9 +54,8 @@ router noTenancyRequired: true, }) ) - .use(currentApp) .use(pro.licensing()) - .use(pro.activity()) + .use(currentApp) .use(auditLog) // error handling middleware diff --git a/packages/types/src/sdk/koa.ts b/packages/types/src/sdk/koa.ts index 5ab04a3e6f..8d419d5cf1 100644 --- a/packages/types/src/sdk/koa.ts +++ b/packages/types/src/sdk/koa.ts @@ -1,8 +1,10 @@ import { Context } from "koa" import { User } from "../documents" +import { License } from "../sdk" export interface ContextUser extends User { globalId?: string + license: License } export interface BBContext extends Context { diff --git a/packages/types/src/sdk/licensing/billing.ts b/packages/types/src/sdk/licensing/billing.ts new file mode 100644 index 0000000000..6743b9cb17 --- /dev/null +++ b/packages/types/src/sdk/licensing/billing.ts @@ -0,0 +1,20 @@ +import { PriceDuration } from "./plan" + +export interface CustomerBilling { + balance: number | null | undefined + currency: string | null | undefined +} + +export interface SubscriptionBilling { + amount: number + quantity: number + duration: PriceDuration + cancelAt: number | null | undefined + currentPeriodStart: number + currentPeriodEnd: number +} + +export interface Billing { + customer: CustomerBilling + subscription?: SubscriptionBilling +} diff --git a/packages/types/src/sdk/licensing/feature.ts b/packages/types/src/sdk/licensing/feature.ts new file mode 100644 index 0000000000..cbd1f4a50c --- /dev/null +++ b/packages/types/src/sdk/licensing/feature.ts @@ -0,0 +1,3 @@ +export enum Feature { + USER_GROUPS = "userGroups", +} diff --git a/packages/types/src/sdk/licensing/index.ts b/packages/types/src/sdk/licensing/index.ts index e6080073b7..8d15ba96fc 100644 --- a/packages/types/src/sdk/licensing/index.ts +++ b/packages/types/src/sdk/licensing/index.ts @@ -1 +1,5 @@ export * from "./license" +export * from "./plan" +export * from "./quota" +export * from "./feature" +export * from "./billing" diff --git a/packages/types/src/sdk/licensing/license.ts b/packages/types/src/sdk/licensing/license.ts index ed2e0a0245..d19a3a617d 100644 --- a/packages/types/src/sdk/licensing/license.ts +++ b/packages/types/src/sdk/licensing/license.ts @@ -1 +1,8 @@ -export interface License {} +import { AccountPlan, Quotas, Feature, Billing } from "." + +export interface License { + features: Feature[] + quotas: Quotas + plan: AccountPlan + billing?: Billing +} diff --git a/packages/types/src/sdk/licensing/plan.ts b/packages/types/src/sdk/licensing/plan.ts new file mode 100644 index 0000000000..6b226887b4 --- /dev/null +++ b/packages/types/src/sdk/licensing/plan.ts @@ -0,0 +1,25 @@ +export interface AccountPlan { + type: PlanType + price?: Price +} + +export enum PlanType { + FREE = "free", + PRO = "pro", + BUSINESS = "business", + ENTERPRISE = "enterprise", +} + +export enum PriceDuration { + MONTHLY = "monthly", + YEARLY = "yearly", +} + +export interface Price { + amount: number + amountMonthly: number + currency: string + duration: PriceDuration + priceId: string + dayPasses: number +} diff --git a/packages/types/src/sdk/licensing/quota.ts b/packages/types/src/sdk/licensing/quota.ts new file mode 100644 index 0000000000..578a5d98d0 --- /dev/null +++ b/packages/types/src/sdk/licensing/quota.ts @@ -0,0 +1,82 @@ +import { PlanType } from "." + +export enum QuotaUsageType { + STATIC = "static", + MONTHLY = "monthly", +} + +export enum QuotaType { + USAGE = "usage", + CONSTANT = "constant", +} + +export enum StaticQuotaName { + ROWS = "rows", + APPS = "apps", +} + +export enum MonthlyQuotaName { + QUERIES = "queries", + AUTOMATIONS = "automations", + DAY_PASSES = "dayPasses", +} + +export enum ConstantQuotaName { + QUERY_TIMEOUT_SECONDS = "queryTimeoutSeconds", + AUTOMATION_LOG_RETENTION_DAYS = "automationLogRetentionDays", +} + +export type QuotaName = StaticQuotaName | MonthlyQuotaName | ConstantQuotaName + +export const isStaticQuota = ( + quotaType: QuotaType, + usageType: QuotaUsageType, + name: QuotaName +): name is StaticQuotaName => { + return quotaType === QuotaType.USAGE && usageType === QuotaUsageType.STATIC +} + +export const isMonthlyQuota = ( + quotaType: QuotaType, + usageType: QuotaUsageType, + name: QuotaName +): name is MonthlyQuotaName => { + return quotaType === QuotaType.USAGE && usageType === QuotaUsageType.MONTHLY +} + +export const isConstantQuota = ( + quotaType: QuotaType, + name: QuotaName +): name is ConstantQuotaName => { + return quotaType === QuotaType.CONSTANT +} + +export type PlanQuotas = { + [PlanType.FREE]: Quotas + [PlanType.PRO]: Quotas + [PlanType.BUSINESS]: Quotas + [PlanType.ENTERPRISE]: Quotas +} + +export type Quotas = { + [QuotaType.USAGE]: { + [QuotaUsageType.MONTHLY]: { + [MonthlyQuotaName.QUERIES]: Quota + [MonthlyQuotaName.AUTOMATIONS]: Quota + [MonthlyQuotaName.DAY_PASSES]: Quota + } + [QuotaUsageType.STATIC]: { + [StaticQuotaName.ROWS]: Quota + [StaticQuotaName.APPS]: Quota + } + } + [QuotaType.CONSTANT]: { + [ConstantQuotaName.QUERY_TIMEOUT_SECONDS]: Quota + [ConstantQuotaName.AUTOMATION_LOG_RETENTION_DAYS]: Quota + } +} + +export interface Quota { + name: string + value: number +} diff --git a/packages/worker/src/api/controllers/global/self.js b/packages/worker/src/api/controllers/global/self.js index 28afa69fa0..3e073243dc 100644 --- a/packages/worker/src/api/controllers/global/self.js +++ b/packages/worker/src/api/controllers/global/self.js @@ -144,6 +144,7 @@ exports.updateSelf = async ctx => { } // remove the old password from the user before sending events + user._rev = response.rev delete user.password await events.user.updated(user) if (passwordChange) { diff --git a/packages/worker/src/api/index.ts b/packages/worker/src/api/index.ts index 5d809ba5b8..21fbf1d993 100644 --- a/packages/worker/src/api/index.ts +++ b/packages/worker/src/api/index.ts @@ -2,7 +2,7 @@ import Router from "@koa/router" const compress = require("koa-compress") const zlib = require("zlib") import { routes } from "./routes" -import { middleware as pro, quotas } from "@budibase/pro" +import { middleware as pro } from "@budibase/pro" import { errors, auth, middleware } from "@budibase/backend-core" import { APIError } from "@budibase/types" @@ -92,15 +92,10 @@ router }) ) .use("/health", ctx => (ctx.status = 200)) - .use( - auth.buildAuthMiddleware(PUBLIC_ENDPOINTS, { - checkDayPass: quotas.checkDayPass, - }) - ) + .use(auth.buildAuthMiddleware(PUBLIC_ENDPOINTS)) .use(auth.buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS)) .use(auth.buildCsrfMiddleware({ noCsrfPatterns: NO_CSRF_ENDPOINTS })) .use(pro.licensing()) - .use(pro.activity()) // for now no public access is allowed to worker (bar health check) .use((ctx, next) => { if (ctx.publicEndpoint) { diff --git a/packages/worker/src/api/routes/global/tests/self.spec.ts b/packages/worker/src/api/routes/global/tests/self.spec.ts index b3c91a9306..5640bab3ce 100644 --- a/packages/worker/src/api/routes/global/tests/self.spec.ts +++ b/packages/worker/src/api/routes/global/tests/self.spec.ts @@ -1,5 +1,5 @@ jest.mock("nodemailer") -import { TestConfiguration, API } from "../../../../tests" +import { TestConfiguration, API, mocks } from "../../../../tests" import { events } from "@budibase/backend-core" describe("/api/global/self", () => { @@ -26,6 +26,9 @@ describe("/api/global/self", () => { delete user.password const res = await api.self.updateSelf(user) + const dbUser = await config.getUser(user.email) + user._rev = dbUser._rev + user.dayPassRecordedAt = mocks.date.MOCK_DATE.toISOString() expect(res.body._id).toBe(user._id) expect(events.user.updated).toBeCalledTimes(1) expect(events.user.updated).toBeCalledWith(user) @@ -39,6 +42,9 @@ describe("/api/global/self", () => { user.password = "newPassword" const res = await api.self.updateSelf(user) + const dbUser = await config.getUser(user.email) + user._rev = dbUser._rev + user.dayPassRecordedAt = mocks.date.MOCK_DATE.toISOString() delete user.password expect(res.body._id).toBe(user._id) expect(events.user.updated).toBeCalledTimes(1)