diff --git a/lerna.json b/lerna.json index 5605642877..ce3128165f 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.13.5", + "version": "2.13.6", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/src/cache/appMetadata.ts b/packages/backend-core/src/cache/appMetadata.ts index bd3efc20db..d442511fb8 100644 --- a/packages/backend-core/src/cache/appMetadata.ts +++ b/packages/backend-core/src/cache/appMetadata.ts @@ -19,7 +19,7 @@ async function populateFromDB(appId: string) { return doWithDB( appId, (db: Database) => { - return db.get(DocumentType.APP_METADATA) + return db.get(DocumentType.APP_METADATA) }, { skip_setup: true } ) diff --git a/packages/backend-core/src/context/Context.ts b/packages/backend-core/src/context/Context.ts index d29b6935a8..a59f5c6503 100644 --- a/packages/backend-core/src/context/Context.ts +++ b/packages/backend-core/src/context/Context.ts @@ -4,7 +4,7 @@ import { ContextMap } from "./types" export default class Context { static storage = new AsyncLocalStorage() - static run(context: ContextMap, func: any) { + static run(context: ContextMap, func: () => T) { return Context.storage.run(context, () => func()) } diff --git a/packages/backend-core/src/context/mainContext.ts b/packages/backend-core/src/context/mainContext.ts index 609c18abb5..d2259cfcab 100644 --- a/packages/backend-core/src/context/mainContext.ts +++ b/packages/backend-core/src/context/mainContext.ts @@ -98,17 +98,17 @@ function updateContext(updates: ContextMap): ContextMap { return context } -async function newContext(updates: ContextMap, task: any) { +async function newContext(updates: ContextMap, task: () => T) { // see if there already is a context setup let context: ContextMap = updateContext(updates) return Context.run(context, task) } -export async function doInAutomationContext(params: { +export async function doInAutomationContext(params: { appId: string automationId: string - task: any -}): Promise { + task: () => T +}): Promise { const tenantId = getTenantIDFromAppID(params.appId) return newContext( { @@ -144,10 +144,10 @@ export async function doInTenant( return newContext(updates, task) } -export async function doInAppContext( +export async function doInAppContext( appId: string | null, - task: any -): Promise { + task: () => T +): Promise { if (!appId && !env.isTest()) { throw new Error("appId is required") } @@ -165,10 +165,10 @@ export async function doInAppContext( return newContext(updates, task) } -export async function doInIdentityContext( +export async function doInIdentityContext( identity: IdentityContext, - task: any -): Promise { + task: () => T +): Promise { if (!identity) { throw new Error("identity is required") } @@ -276,6 +276,9 @@ export function getAuditLogsDB(): Database { */ export function getAppDB(opts?: any): Database { const appId = getAppId() + if (!appId) { + throw new Error("Unable to retrieve app DB - no app ID.") + } return getDB(appId, opts) } diff --git a/packages/backend-core/src/db/couch/DatabaseImpl.ts b/packages/backend-core/src/db/couch/DatabaseImpl.ts index 330b15e680..1ace60ed5b 100644 --- a/packages/backend-core/src/db/couch/DatabaseImpl.ts +++ b/packages/backend-core/src/db/couch/DatabaseImpl.ts @@ -48,10 +48,7 @@ export class DatabaseImpl implements Database { private readonly couchInfo = getCouchInfo() - constructor(dbName?: string, opts?: DatabaseOpts, connection?: string) { - if (dbName == null) { - throw new Error("Database name cannot be undefined.") - } + constructor(dbName: string, opts?: DatabaseOpts, connection?: string) { this.name = dbName this.pouchOpts = opts || {} if (connection) { diff --git a/packages/backend-core/src/db/db.ts b/packages/backend-core/src/db/db.ts index 9aae64b892..3e69d49f0e 100644 --- a/packages/backend-core/src/db/db.ts +++ b/packages/backend-core/src/db/db.ts @@ -1,10 +1,7 @@ -import env from "../environment" import { directCouchQuery, DatabaseImpl } from "./couch" -import { CouchFindOptions, Database } from "@budibase/types" +import { CouchFindOptions, Database, DatabaseOpts } from "@budibase/types" -const dbList = new Set() - -export function getDB(dbName?: string, opts?: any): Database { +export function getDB(dbName: string, opts?: DatabaseOpts): Database { return new DatabaseImpl(dbName, opts) } @@ -14,7 +11,7 @@ export function getDB(dbName?: string, opts?: any): Database { export async function doWithDB( dbName: string, cb: (db: Database) => Promise, - opts = {} + opts?: DatabaseOpts ) { const db = getDB(dbName, opts) // need this to be async so that we can correctly close DB after all @@ -22,13 +19,6 @@ export async function doWithDB( return await cb(db) } -export function allDbs() { - if (!env.isTest()) { - throw new Error("Cannot be used outside test environment.") - } - return [...dbList] -} - export async function directCouchAllDbs(queryString?: string) { let couchPath = "/_all_dbs" if (queryString) { diff --git a/packages/pro b/packages/pro index ad9a0085be..e202f415d9 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit ad9a0085bee0c4f3184acd86cadd872ea9917e88 +Subproject commit e202f415d9fa540d08cc2ba6e27394fbc22f357b diff --git a/packages/server/src/api/controllers/datasource.ts b/packages/server/src/api/controllers/datasource.ts index 5d024d51b6..39bc612b32 100644 --- a/packages/server/src/api/controllers/datasource.ts +++ b/packages/server/src/api/controllers/datasource.ts @@ -337,7 +337,7 @@ export async function destroy(ctx: UserCtx) { if (datasource.type === dbCore.BUDIBASE_DATASOURCE_TYPE) { await destroyInternalTablesBySourceId(datasourceId) } else { - const queries = await db.allDocs(getQueryParams(datasourceId, null)) + const queries = await db.allDocs(getQueryParams(datasourceId)) await db.bulkDocs( queries.rows.map((row: any) => ({ _id: row.id, diff --git a/packages/server/src/api/routes/public/index.ts b/packages/server/src/api/routes/public/index.ts index b37ed931fc..f27f3f8857 100644 --- a/packages/server/src/api/routes/public/index.ts +++ b/packages/server/src/api/routes/public/index.ts @@ -27,51 +27,59 @@ interface KoaRateLimitOptions { } const PREFIX = "/api/public/v1" -// allow a lot more requests when in test -const DEFAULT_API_REQ_LIMIT_PER_SEC = env.isTest() ? 100 : 10 -function getApiLimitPerSecond(): number { - if (!env.API_REQ_LIMIT_PER_SEC) { - return DEFAULT_API_REQ_LIMIT_PER_SEC - } - return parseInt(env.API_REQ_LIMIT_PER_SEC) -} +// type can't be known - untyped libraries +let limiter: any, rateLimitStore: any +if (!env.DISABLE_RATE_LIMITING) { + // allow a lot more requests when in test + const DEFAULT_API_REQ_LIMIT_PER_SEC = env.isTest() ? 100 : 10 -let rateLimitStore: any = null -if (!env.isTest()) { - const { password, host, port } = redis.utils.getRedisConnectionDetails() - let options: KoaRateLimitOptions = { - socket: { - host: host, - port: port, - }, + function getApiLimitPerSecond(): number { + if (!env.API_REQ_LIMIT_PER_SEC) { + return DEFAULT_API_REQ_LIMIT_PER_SEC + } + return parseInt(env.API_REQ_LIMIT_PER_SEC) } - if (password) { - options.password = password - } + if (!env.isTest()) { + const { password, host, port } = redis.utils.getRedisConnectionDetails() + let options: KoaRateLimitOptions = { + socket: { + host: host, + port: port, + }, + } - if (!env.REDIS_CLUSTERED) { - // Can't set direct redis db in clustered env - options.database = SelectableDatabase.RATE_LIMITING + if (password) { + options.password = password + } + + if (!env.REDIS_CLUSTERED) { + // Can't set direct redis db in clustered env + options.database = SelectableDatabase.RATE_LIMITING + } + rateLimitStore = new Stores.Redis(options) + RateLimit.defaultOptions({ + store: rateLimitStore, + }) } - rateLimitStore = new Stores.Redis(options) - RateLimit.defaultOptions({ - store: rateLimitStore, + // rate limiting, allows for 2 requests per second + limiter = RateLimit.middleware({ + interval: { sec: 1 }, + // per ip, per interval + max: getApiLimitPerSecond(), }) +} else { + console.log("**** PUBLIC API RATE LIMITING DISABLED ****") } -// rate limiting, allows for 2 requests per second -const limiter = RateLimit.middleware({ - interval: { sec: 1 }, - // per ip, per interval - max: getApiLimitPerSecond(), -}) const publicRouter = new Router({ prefix: PREFIX, }) -publicRouter.use(limiter) +if (limiter) { + publicRouter.use(limiter) +} function addMiddleware( endpoints: any, diff --git a/packages/server/src/automations/triggers.ts b/packages/server/src/automations/triggers.ts index f0eca759f5..ac977bbefb 100644 --- a/packages/server/src/automations/triggers.ts +++ b/packages/server/src/automations/triggers.ts @@ -94,7 +94,7 @@ export async function externalTrigger( automation: Automation, params: { fields: Record; timeout?: number }, { getResponses }: { getResponses?: boolean } = {} -) { +): Promise { if ( automation.definition != null && automation.definition.trigger != null && diff --git a/packages/server/src/db/utils.ts b/packages/server/src/db/utils.ts index d532d8a8b2..715db552c9 100644 --- a/packages/server/src/db/utils.ts +++ b/packages/server/src/db/utils.ts @@ -6,6 +6,7 @@ import { RelationshipFieldMetadata, VirtualDocumentType, INTERNAL_TABLE_SOURCE_ID, + DatabaseQueryOpts, } from "@budibase/types" import { FieldTypes } from "../constants" export { DocumentType, VirtualDocumentType } from "@budibase/types" @@ -229,7 +230,10 @@ export function getAutomationMetadataParams(otherProps: any = {}) { /** * Gets parameters for retrieving a query, this is a utility function for the getDocParams function. */ -export function getQueryParams(datasourceId?: Optional, otherProps: any = {}) { +export function getQueryParams( + datasourceId?: Optional, + otherProps: Partial = {} +) { if (datasourceId == null) { return getDocParams(DocumentType.QUERY, null, otherProps) } @@ -256,7 +260,7 @@ export function generateMetadataID(type: string, entityId: string) { export function getMetadataParams( type: string, entityId?: Optional, - otherProps: any = {} + otherProps: Partial = {} ) { let docId = `${type}${SEPARATOR}` if (entityId != null) { @@ -269,7 +273,9 @@ export function generateMemoryViewID(viewName: string) { return `${DocumentType.MEM_VIEW}${SEPARATOR}${viewName}` } -export function getMemoryViewParams(otherProps: any = {}) { +export function getMemoryViewParams( + otherProps: Partial = {} +) { return getDocParams(DocumentType.MEM_VIEW, null, otherProps) } diff --git a/packages/server/src/environment.ts b/packages/server/src/environment.ts index 91424113ac..c126a61c22 100644 --- a/packages/server/src/environment.ts +++ b/packages/server/src/environment.ts @@ -61,6 +61,7 @@ const environment = { ALLOW_DEV_AUTOMATIONS: process.env.ALLOW_DEV_AUTOMATIONS, DISABLE_THREADING: process.env.DISABLE_THREADING, DISABLE_AUTOMATION_LOGS: process.env.DISABLE_AUTOMATION_LOGS, + DISABLE_RATE_LIMITING: process.env.DISABLE_RATE_LIMITING, MULTI_TENANCY: process.env.MULTI_TENANCY, ENABLE_ANALYTICS: process.env.ENABLE_ANALYTICS, SELF_HOSTED: process.env.SELF_HOSTED, diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 6877561fcb..3a14a87d2a 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -510,13 +510,14 @@ class TestConfiguration { // create dev app // clear any old app this.appId = null - await context.doInAppContext(null, async () => { - this.app = await this._req( + this.app = await context.doInAppContext(null, async () => { + const app = await this._req( { name: appName }, null, controllers.app.create ) - this.appId = this.app?.appId! + this.appId = app.appId! + return app }) return await context.doInAppContext(this.appId, async () => { // create production app @@ -525,7 +526,7 @@ class TestConfiguration { this.allApps.push(this.prodApp) this.allApps.push(this.app) - return this.app + return this.app! }) } @@ -537,7 +538,7 @@ class TestConfiguration { return context.doInAppContext(prodAppId, async () => { const db = context.getProdAppDB() - return await db.get(dbCore.DocumentType.APP_METADATA) + return await db.get(dbCore.DocumentType.APP_METADATA) }) } diff --git a/packages/server/src/threads/automation.ts b/packages/server/src/threads/automation.ts index 9241289e86..d1fcc2be72 100644 --- a/packages/server/src/threads/automation.ts +++ b/packages/server/src/threads/automation.ts @@ -241,7 +241,7 @@ class Orchestrator { }) } - async execute() { + async execute(): Promise { // this will retrieve from context created at start of thread this._context.env = await sdkUtils.getEnvironmentVariables() let automation = this._automation diff --git a/packages/types/src/sdk/licensing/plan.ts b/packages/types/src/sdk/licensing/plan.ts index 1604dfb8af..5ac8b1c9f6 100644 --- a/packages/types/src/sdk/licensing/plan.ts +++ b/packages/types/src/sdk/licensing/plan.ts @@ -7,7 +7,9 @@ export enum PlanType { /** @deprecated */ PREMIUM = "premium", PREMIUM_PLUS = "premium_plus", + /** @deprecated */ BUSINESS = "business", + ENTERPRISE_BASIC = "enterprise_basic", ENTERPRISE = "enterprise", } diff --git a/qa-core/package.json b/qa-core/package.json index cfccd5e650..87172d2ed9 100644 --- a/qa-core/package.json +++ b/qa-core/package.json @@ -20,7 +20,7 @@ "test:self:ci": "yarn run test --testPathIgnorePatterns=\\.integration\\. \\.cloud\\. \\.licensing\\.", "serve:test:self:ci": "start-server-and-test dev:built http://localhost:4001/health test:self:ci", "serve": "start-server-and-test dev:built http://localhost:4001/health", - "dev:built": "cd ../ && yarn dev:built" + "dev:built": "cd ../ && DISABLE_RATE_LIMITING=1 yarn dev:built" }, "devDependencies": { "@budibase/types": "^2.3.17",