mirror of https://github.com/Budibase/budibase.git
409 changed files with 13176 additions and 6345 deletions
@ -1,48 +1,79 @@ |
|||
{ |
|||
"name": "@budibase/backend-core", |
|||
"version": "1.0.192-alpha.2", |
|||
"version": "1.0.200-alpha.3", |
|||
"description": "Budibase backend core libraries used in server and worker", |
|||
"main": "src/index.js", |
|||
"main": "dist/src/index.js", |
|||
"types": "dist/src/index.d.ts", |
|||
"exports": { |
|||
".": "./dist/src/index.js", |
|||
"./tests": "./dist/tests/index.js", |
|||
"./*": "./dist/*.js" |
|||
}, |
|||
"author": "Budibase", |
|||
"license": "GPL-3.0", |
|||
"scripts": { |
|||
"prebuild": "rimraf dist/", |
|||
"build": "tsc -p tsconfig.build.json", |
|||
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", |
|||
"test": "jest", |
|||
"test:watch": "jest --watchAll" |
|||
}, |
|||
"dependencies": { |
|||
"@techpass/passport-openidconnect": "^0.3.0", |
|||
"aws-sdk": "^2.901.0", |
|||
"bcrypt": "^5.0.1", |
|||
"dotenv": "^16.0.1", |
|||
"emitter-listener": "^1.1.2", |
|||
"ioredis": "^4.27.1", |
|||
"jsonwebtoken": "^8.5.1", |
|||
"koa-passport": "^4.1.4", |
|||
"lodash": "^4.17.21", |
|||
"lodash.isarguments": "^3.1.0", |
|||
"node-fetch": "^2.6.1", |
|||
"passport-google-auth": "^1.0.2", |
|||
"passport-google-oauth": "^2.0.0", |
|||
"passport-jwt": "^4.0.0", |
|||
"passport-local": "^1.0.0", |
|||
"posthog-node": "^1.3.0", |
|||
"@techpass/passport-openidconnect": "0.3.2", |
|||
"aws-sdk": "2.1030.0", |
|||
"bcrypt": "5.0.1", |
|||
"dotenv": "16.0.1", |
|||
"emitter-listener": "1.1.2", |
|||
"ioredis": "4.28.0", |
|||
"jsonwebtoken": "8.5.1", |
|||
"koa-passport": "4.1.4", |
|||
"lodash": "4.17.21", |
|||
"lodash.isarguments": "3.1.0", |
|||
"node-fetch": "2.6.7", |
|||
"passport-google-auth": "1.0.2", |
|||
"passport-google-oauth": "2.0.0", |
|||
"passport-jwt": "4.0.0", |
|||
"passport-local": "1.0.0", |
|||
"posthog-node": "1.3.0", |
|||
"pouchdb": "7.3.0", |
|||
"pouchdb-find": "^7.2.2", |
|||
"pouchdb-replication-stream": "^1.2.9", |
|||
"sanitize-s3-objectkey": "^0.0.1", |
|||
"tar-fs": "^2.1.1", |
|||
"uuid": "^8.3.2", |
|||
"zlib": "^1.0.5" |
|||
"pouchdb-find": "7.2.2", |
|||
"pouchdb-replication-stream": "1.2.9", |
|||
"redlock": "4.2.0", |
|||
"sanitize-s3-objectkey": "0.0.1", |
|||
"semver": "7.3.7", |
|||
"tar-fs": "2.1.1", |
|||
"uuid": "8.3.2", |
|||
"zlib": "1.0.5" |
|||
}, |
|||
"jest": { |
|||
"preset": "ts-jest", |
|||
"testEnvironment": "node", |
|||
"moduleNameMapper": { |
|||
"@budibase/types": "<rootDir>/../types/src" |
|||
}, |
|||
"setupFiles": [ |
|||
"./scripts/jestSetup.js" |
|||
"./scripts/jestSetup.ts" |
|||
] |
|||
}, |
|||
"devDependencies": { |
|||
"ioredis-mock": "^5.5.5", |
|||
"jest": "^26.6.3", |
|||
"pouchdb-adapter-memory": "^7.2.2" |
|||
"@budibase/types": "^1.0.200-alpha.3", |
|||
"@shopify/jest-koa-mocks": "3.1.5", |
|||
"@types/jest": "27.5.1", |
|||
"@types/koa": "2.0.52", |
|||
"@types/node": "14.18.20", |
|||
"@types/node-fetch": "2.6.1", |
|||
"@types/redlock": "4.0.3", |
|||
"@types/semver": "7.3.7", |
|||
"@types/tar-fs": "2.0.1", |
|||
"@types/uuid": "8.3.4", |
|||
"ioredis-mock": "5.8.0", |
|||
"jest": "27.5.1", |
|||
"koa": "2.7.0", |
|||
"nodemon": "2.0.16", |
|||
"pouchdb-adapter-memory": "7.2.2", |
|||
"timekeeper": "2.2.0", |
|||
"ts-jest": "27.1.5", |
|||
"typescript": "4.7.3" |
|||
}, |
|||
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" |
|||
} |
|||
|
|||
@ -1,4 +1,5 @@ |
|||
module.exports = { |
|||
Client: require("./src/redis"), |
|||
utils: require("./src/redis/utils"), |
|||
clients: require("./src/redis/authRedis"), |
|||
} |
|||
|
|||
@ -1,6 +0,0 @@ |
|||
const env = require("../src/environment") |
|||
|
|||
env._set("SELF_HOSTED", "1") |
|||
env._set("NODE_ENV", "jest") |
|||
env._set("JWT_SECRET", "test-jwtsecret") |
|||
env._set("LOG_LEVEL", "silent") |
|||
@ -0,0 +1,12 @@ |
|||
import env from "../src/environment" |
|||
import { mocks } from "../tests/utilities" |
|||
|
|||
// mock all dates to 2020-01-01T00:00:00.000Z
|
|||
// use tk.reset() to use real dates in individual tests
|
|||
import tk from "timekeeper" |
|||
tk.freeze(mocks.date.MOCK_DATE) |
|||
|
|||
env._set("SELF_HOSTED", "1") |
|||
env._set("NODE_ENV", "jest") |
|||
env._set("JWT_SECRET", "test-jwtsecret") |
|||
env._set("LOG_LEVEL", "silent") |
|||
@ -1,39 +0,0 @@ |
|||
const API = require("./api") |
|||
const env = require("../environment") |
|||
const { Headers } = require("../constants") |
|||
|
|||
const api = new API(env.ACCOUNT_PORTAL_URL) |
|||
|
|||
exports.getAccount = async email => { |
|||
const payload = { |
|||
email, |
|||
} |
|||
const response = await api.post(`/api/accounts/search`, { |
|||
body: payload, |
|||
headers: { |
|||
[Headers.API_KEY]: env.ACCOUNT_PORTAL_API_KEY, |
|||
}, |
|||
}) |
|||
const json = await response.json() |
|||
|
|||
if (response.status !== 200) { |
|||
throw new Error(`Error getting account by email ${email}`, json) |
|||
} |
|||
|
|||
return json[0] |
|||
} |
|||
|
|||
exports.getStatus = async () => { |
|||
const response = await api.get(`/api/status`, { |
|||
headers: { |
|||
[Headers.API_KEY]: env.ACCOUNT_PORTAL_API_KEY, |
|||
}, |
|||
}) |
|||
const json = await response.json() |
|||
|
|||
if (response.status !== 200) { |
|||
throw new Error(`Error getting status`) |
|||
} |
|||
|
|||
return json |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
import API from "./api" |
|||
import env from "../environment" |
|||
import { Headers } from "../constants" |
|||
import { CloudAccount } from "@budibase/types" |
|||
|
|||
const api = new API(env.ACCOUNT_PORTAL_URL) |
|||
|
|||
export const getAccount = async ( |
|||
email: string |
|||
): Promise<CloudAccount | undefined> => { |
|||
const payload = { |
|||
email, |
|||
} |
|||
const response = await api.post(`/api/accounts/search`, { |
|||
body: payload, |
|||
headers: { |
|||
[Headers.API_KEY]: env.ACCOUNT_PORTAL_API_KEY, |
|||
}, |
|||
}) |
|||
|
|||
if (response.status !== 200) { |
|||
throw new Error(`Error getting account by email ${email}`) |
|||
} |
|||
|
|||
const json: CloudAccount[] = await response.json() |
|||
return json[0] |
|||
} |
|||
|
|||
export const getAccountByTenantId = async ( |
|||
tenantId: string |
|||
): Promise<CloudAccount | undefined> => { |
|||
const payload = { |
|||
tenantId, |
|||
} |
|||
const response = await api.post(`/api/accounts/search`, { |
|||
body: payload, |
|||
headers: { |
|||
[Headers.API_KEY]: env.ACCOUNT_PORTAL_API_KEY, |
|||
}, |
|||
}) |
|||
|
|||
if (response.status !== 200) { |
|||
throw new Error(`Error getting account by tenantId ${tenantId}`) |
|||
} |
|||
|
|||
const json: CloudAccount[] = await response.json() |
|||
return json[0] |
|||
} |
|||
|
|||
export const getStatus = async () => { |
|||
const response = await api.get(`/api/status`, { |
|||
headers: { |
|||
[Headers.API_KEY]: env.ACCOUNT_PORTAL_API_KEY, |
|||
}, |
|||
}) |
|||
const json = await response.json() |
|||
|
|||
if (response.status !== 200) { |
|||
throw new Error(`Error getting status`) |
|||
} |
|||
|
|||
return json |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
import { |
|||
IdentityContext, |
|||
IdentityType, |
|||
User, |
|||
UserContext, |
|||
isCloudAccount, |
|||
Account, |
|||
AccountUserContext, |
|||
} from "@budibase/types" |
|||
import * as context from "." |
|||
|
|||
export const getIdentity = (): IdentityContext | undefined => { |
|||
return context.getIdentity() |
|||
} |
|||
|
|||
export const doInIdentityContext = (identity: IdentityContext, task: any) => { |
|||
return context.doInIdentityContext(identity, task) |
|||
} |
|||
|
|||
export const doInUserContext = (user: User, task: any) => { |
|||
const userContext: UserContext = { |
|||
...user, |
|||
_id: user._id as string, |
|||
type: IdentityType.USER, |
|||
} |
|||
return doInIdentityContext(userContext, task) |
|||
} |
|||
|
|||
export const doInAccountContext = (account: Account, task: any) => { |
|||
const _id = getAccountUserId(account) |
|||
const tenantId = account.tenantId |
|||
const accountContext: AccountUserContext = { |
|||
_id, |
|||
type: IdentityType.USER, |
|||
tenantId, |
|||
account, |
|||
} |
|||
return doInIdentityContext(accountContext, task) |
|||
} |
|||
|
|||
export const getAccountUserId = (account: Account) => { |
|||
let userId: string |
|||
if (isCloudAccount(account)) { |
|||
userId = account.budibaseUserId |
|||
} else { |
|||
// use account id as user id for self hosting
|
|||
userId = account.accountId |
|||
} |
|||
return userId |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
require("../../../tests/utilities/TestConfiguration") |
|||
const { dangerousGetDB } = require("../") |
|||
|
|||
describe("db", () => { |
|||
|
|||
describe("getDB", () => { |
|||
it("returns a db", async () => { |
|||
const db = dangerousGetDB("test") |
|||
expect(db).toBeDefined() |
|||
expect(db._adapter).toBe("memory") |
|||
expect(db.prefix).toBe("_pouch_") |
|||
expect(db.name).toBe("test") |
|||
}) |
|||
|
|||
it("uses the custom put function", async () => { |
|||
const db = dangerousGetDB("test") |
|||
let doc = { _id: "test" } |
|||
await db.put(doc) |
|||
doc = await db.get(doc._id) |
|||
expect(doc.createdAt).toBe(new Date().toISOString()) |
|||
expect(doc.updatedAt).toBe(new Date().toISOString()) |
|||
await db.destroy() |
|||
}) |
|||
}) |
|||
}) |
|||
|
|||
@ -0,0 +1,11 @@ |
|||
const { BudibaseError } = require("./base") |
|||
|
|||
class GenericError extends BudibaseError { |
|||
constructor(message, code, type) { |
|||
super(message, code, type ? type : "generic") |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
GenericError, |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
const { GenericError } = require("./generic") |
|||
|
|||
class HTTPError extends GenericError { |
|||
constructor(message, httpStatus, code = "http", type = "generic") { |
|||
super(message, code, type) |
|||
this.status = httpStatus |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
HTTPError, |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
import env from "../environment" |
|||
import tenancy from "../tenancy" |
|||
import * as dbUtils from "../db/utils" |
|||
import { Configs } from "../constants" |
|||
import { withCache, TTL, CacheKeys } from "../cache/generic" |
|||
|
|||
export const enabled = async () => { |
|||
// cloud - always use the environment variable
|
|||
if (!env.SELF_HOSTED) { |
|||
return !!env.ENABLE_ANALYTICS |
|||
} |
|||
|
|||
// self host - prefer the settings doc
|
|||
// use cache as events have high throughput
|
|||
const enabledInDB = await withCache( |
|||
CacheKeys.ANALYTICS_ENABLED, |
|||
TTL.ONE_DAY, |
|||
async () => { |
|||
const settings = await getSettingsDoc() |
|||
|
|||
// need to do explicit checks in case the field is not set
|
|||
if (settings?.config?.analyticsEnabled === false) { |
|||
return false |
|||
} else if (settings?.config?.analyticsEnabled === true) { |
|||
return true |
|||
} |
|||
} |
|||
) |
|||
|
|||
if (enabledInDB !== undefined) { |
|||
return enabledInDB |
|||
} |
|||
|
|||
// fallback to the environment variable
|
|||
// explicitly check for 0 or false here, undefined or otherwise is treated as true
|
|||
const envEnabled: any = env.ENABLE_ANALYTICS |
|||
if (envEnabled === 0 || envEnabled === false) { |
|||
return false |
|||
} else { |
|||
return true |
|||
} |
|||
} |
|||
|
|||
const getSettingsDoc = async () => { |
|||
const db = tenancy.getGlobalDB() |
|||
let settings |
|||
try { |
|||
settings = await db.get( |
|||
dbUtils.generateConfigID({ type: Configs.SETTINGS }) |
|||
) |
|||
} catch (e: any) { |
|||
if (e.status !== 404) { |
|||
throw e |
|||
} |
|||
} |
|||
return settings |
|||
} |
|||
@ -0,0 +1,183 @@ |
|||
import { |
|||
Event, |
|||
BackfillMetadata, |
|||
CachedEvent, |
|||
SSOCreatedEvent, |
|||
AutomationCreatedEvent, |
|||
AutomationStepCreatedEvent, |
|||
DatasourceCreatedEvent, |
|||
LayoutCreatedEvent, |
|||
QueryCreatedEvent, |
|||
RoleCreatedEvent, |
|||
ScreenCreatedEvent, |
|||
TableCreatedEvent, |
|||
ViewCreatedEvent, |
|||
ViewCalculationCreatedEvent, |
|||
ViewFilterCreatedEvent, |
|||
AppPublishedEvent, |
|||
UserCreatedEvent, |
|||
RoleAssignedEvent, |
|||
UserPermissionAssignedEvent, |
|||
AppCreatedEvent, |
|||
} from "@budibase/types" |
|||
import * as context from "../context" |
|||
import { CacheKeys } from "../cache/generic" |
|||
import * as cache from "../cache/generic" |
|||
|
|||
// LIFECYCLE
|
|||
|
|||
export const start = async (events: Event[]) => { |
|||
const metadata: BackfillMetadata = { |
|||
eventWhitelist: events, |
|||
} |
|||
return saveBackfillMetadata(metadata) |
|||
} |
|||
|
|||
export const recordEvent = async (event: Event, properties: any) => { |
|||
const eventKey = getEventKey(event, properties) |
|||
// don't use a ttl - cleaned up by migration
|
|||
// don't use tenancy - already in the key
|
|||
await cache.store(eventKey, properties, undefined, { useTenancy: false }) |
|||
} |
|||
|
|||
export const end = async () => { |
|||
await deleteBackfillMetadata() |
|||
await clearEvents() |
|||
} |
|||
|
|||
// CRUD
|
|||
|
|||
const getBackfillMetadata = async (): Promise<BackfillMetadata | null> => { |
|||
return cache.get(CacheKeys.BACKFILL_METADATA) |
|||
} |
|||
|
|||
const saveBackfillMetadata = async ( |
|||
backfill: BackfillMetadata |
|||
): Promise<void> => { |
|||
// no TTL - deleted by backfill
|
|||
return cache.store(CacheKeys.BACKFILL_METADATA, backfill) |
|||
} |
|||
|
|||
const deleteBackfillMetadata = async (): Promise<void> => { |
|||
await cache.delete(CacheKeys.BACKFILL_METADATA) |
|||
} |
|||
|
|||
const clearEvents = async () => { |
|||
// wildcard
|
|||
const pattern = getEventKey() |
|||
const keys = await cache.keys(pattern) |
|||
|
|||
for (const key of keys) { |
|||
// delete each key
|
|||
// don't use tenancy, already in the key
|
|||
await cache.delete(key, { useTenancy: false }) |
|||
} |
|||
} |
|||
|
|||
// HELPERS
|
|||
|
|||
export const isBackfillingEvent = async (event: Event) => { |
|||
const backfill = await getBackfillMetadata() |
|||
const events = backfill?.eventWhitelist |
|||
if (events && events.includes(event)) { |
|||
return true |
|||
} else { |
|||
return false |
|||
} |
|||
} |
|||
|
|||
export const isAlreadySent = async (event: Event, properties: any) => { |
|||
const eventKey = getEventKey(event, properties) |
|||
const cachedEvent: CachedEvent = await cache.get(eventKey, { |
|||
useTenancy: false, |
|||
}) |
|||
return !!cachedEvent |
|||
} |
|||
|
|||
const CUSTOM_PROPERTY_SUFFIX: any = { |
|||
// APP EVENTS
|
|||
[Event.AUTOMATION_CREATED]: (properties: AutomationCreatedEvent) => { |
|||
return properties.automationId |
|||
}, |
|||
[Event.AUTOMATION_STEP_CREATED]: (properties: AutomationStepCreatedEvent) => { |
|||
return properties.stepId |
|||
}, |
|||
[Event.DATASOURCE_CREATED]: (properties: DatasourceCreatedEvent) => { |
|||
return properties.datasourceId |
|||
}, |
|||
[Event.LAYOUT_CREATED]: (properties: LayoutCreatedEvent) => { |
|||
return properties.layoutId |
|||
}, |
|||
[Event.QUERY_CREATED]: (properties: QueryCreatedEvent) => { |
|||
return properties.queryId |
|||
}, |
|||
[Event.ROLE_CREATED]: (properties: RoleCreatedEvent) => { |
|||
return properties.roleId |
|||
}, |
|||
[Event.SCREEN_CREATED]: (properties: ScreenCreatedEvent) => { |
|||
return properties.screenId |
|||
}, |
|||
[Event.TABLE_CREATED]: (properties: TableCreatedEvent) => { |
|||
return properties.tableId |
|||
}, |
|||
[Event.VIEW_CREATED]: (properties: ViewCreatedEvent) => { |
|||
return properties.tableId // best uniqueness
|
|||
}, |
|||
[Event.VIEW_CALCULATION_CREATED]: ( |
|||
properties: ViewCalculationCreatedEvent |
|||
) => { |
|||
return properties.tableId // best uniqueness
|
|||
}, |
|||
[Event.VIEW_FILTER_CREATED]: (properties: ViewFilterCreatedEvent) => { |
|||
return properties.tableId // best uniqueness
|
|||
}, |
|||
[Event.APP_CREATED]: (properties: AppCreatedEvent) => { |
|||
return properties.appId // best uniqueness
|
|||
}, |
|||
[Event.APP_PUBLISHED]: (properties: AppPublishedEvent) => { |
|||
return properties.appId // best uniqueness
|
|||
}, |
|||
// GLOBAL EVENTS
|
|||
[Event.AUTH_SSO_CREATED]: (properties: SSOCreatedEvent) => { |
|||
return properties.type |
|||
}, |
|||
[Event.AUTH_SSO_ACTIVATED]: (properties: SSOCreatedEvent) => { |
|||
return properties.type |
|||
}, |
|||
[Event.USER_CREATED]: (properties: UserCreatedEvent) => { |
|||
return properties.userId |
|||
}, |
|||
[Event.USER_PERMISSION_ADMIN_ASSIGNED]: ( |
|||
properties: UserPermissionAssignedEvent |
|||
) => { |
|||
return properties.userId |
|||
}, |
|||
[Event.USER_PERMISSION_BUILDER_ASSIGNED]: ( |
|||
properties: UserPermissionAssignedEvent |
|||
) => { |
|||
return properties.userId |
|||
}, |
|||
[Event.ROLE_ASSIGNED]: (properties: RoleAssignedEvent) => { |
|||
return `${properties.roleId}-${properties.userId}` |
|||
}, |
|||
} |
|||
|
|||
const getEventKey = (event?: Event, properties?: any) => { |
|||
let eventKey: string |
|||
|
|||
const tenantId = context.getTenantId() |
|||
if (event) { |
|||
eventKey = `${CacheKeys.EVENTS}:${tenantId}:${event}` |
|||
|
|||
// use some properties to make the key more unique
|
|||
const custom = CUSTOM_PROPERTY_SUFFIX[event] |
|||
const suffix = custom ? custom(properties) : undefined |
|||
if (suffix) { |
|||
eventKey = `${eventKey}:${suffix}` |
|||
} |
|||
} else { |
|||
eventKey = `${CacheKeys.EVENTS}:${tenantId}:*` |
|||
} |
|||
|
|||
return eventKey |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
import { Event } from "@budibase/types" |
|||
import { processors } from "./processors" |
|||
import * as identification from "./identification" |
|||
import * as backfill from "./backfill" |
|||
|
|||
export const publishEvent = async ( |
|||
event: Event, |
|||
properties: any, |
|||
timestamp?: string | number |
|||
) => { |
|||
// in future this should use async events via a distributed queue.
|
|||
const identity = await identification.getCurrentIdentity() |
|||
|
|||
const backfilling = await backfill.isBackfillingEvent(event) |
|||
// no backfill - send the event and exit
|
|||
if (!backfilling) { |
|||
await processors.processEvent(event, identity, properties, timestamp) |
|||
return |
|||
} |
|||
|
|||
// backfill active - check if the event has been sent already
|
|||
const alreadySent = await backfill.isAlreadySent(event, properties) |
|||
if (alreadySent) { |
|||
// do nothing
|
|||
return |
|||
} else { |
|||
// send and record the event
|
|||
await processors.processEvent(event, identity, properties, timestamp) |
|||
await backfill.recordEvent(event, properties) |
|||
} |
|||
} |
|||
@ -0,0 +1,267 @@ |
|||
import * as context from "../context" |
|||
import * as identityCtx from "../context/identity" |
|||
import env from "../environment" |
|||
import { |
|||
Hosting, |
|||
User, |
|||
Identity, |
|||
IdentityType, |
|||
Account, |
|||
isCloudAccount, |
|||
isSSOAccount, |
|||
TenantGroup, |
|||
SettingsConfig, |
|||
CloudAccount, |
|||
UserIdentity, |
|||
InstallationGroup, |
|||
isSelfHostAccount, |
|||
UserContext, |
|||
Group, |
|||
} from "@budibase/types" |
|||
import { processors } from "./processors" |
|||
import * as dbUtils from "../db/utils" |
|||
import { Configs } from "../constants" |
|||
import * as hashing from "../hashing" |
|||
import * as installation from "../installation" |
|||
import { withCache, TTL, CacheKeys } from "../cache/generic" |
|||
|
|||
const pkg = require("../../package.json") |
|||
|
|||
/** |
|||
* An identity can be: |
|||
* - account user (Self host) |
|||
* - budibase user |
|||
* - tenant |
|||
* - installation |
|||
*/ |
|||
export const getCurrentIdentity = async (): Promise<Identity> => { |
|||
let identityContext = identityCtx.getIdentity() |
|||
|
|||
let identityType |
|||
|
|||
if (!identityContext) { |
|||
identityType = IdentityType.TENANT |
|||
} else { |
|||
identityType = identityContext.type |
|||
} |
|||
|
|||
if (identityType === IdentityType.INSTALLATION) { |
|||
const installationId = await getInstallationId() |
|||
return { |
|||
id: formatDistinctId(installationId, identityType), |
|||
type: identityType, |
|||
installationId, |
|||
} |
|||
} else if (identityType === IdentityType.TENANT) { |
|||
const installationId = await getInstallationId() |
|||
const tenantId = await getEventTenantId(context.getTenantId()) |
|||
|
|||
return { |
|||
id: formatDistinctId(tenantId, identityType), |
|||
type: identityType, |
|||
installationId, |
|||
tenantId, |
|||
} |
|||
} else if (identityType === IdentityType.USER) { |
|||
const userContext = identityContext as UserContext |
|||
const tenantId = await getEventTenantId(context.getTenantId()) |
|||
let installationId: string | undefined |
|||
|
|||
// self host account users won't have installation
|
|||
if (!userContext.account || !isSelfHostAccount(userContext.account)) { |
|||
installationId = await getInstallationId() |
|||
} |
|||
|
|||
return { |
|||
id: userContext._id, |
|||
type: identityType, |
|||
installationId, |
|||
tenantId, |
|||
} |
|||
} else { |
|||
throw new Error("Unknown identity type") |
|||
} |
|||
} |
|||
|
|||
export const identifyInstallationGroup = async ( |
|||
installId: string, |
|||
timestamp?: string | number |
|||
): Promise<void> => { |
|||
const id = installId |
|||
const type = IdentityType.INSTALLATION |
|||
const hosting = getHostingFromEnv() |
|||
const version = pkg.version |
|||
|
|||
const group: InstallationGroup = { |
|||
id, |
|||
type, |
|||
hosting, |
|||
version, |
|||
} |
|||
|
|||
await identifyGroup(group, timestamp) |
|||
// need to create a normal identity for the group to be able to query it globally
|
|||
// match the posthog syntax to link this identity to the empty auto generated one
|
|||
await identify({ ...group, id: `$${type}_${id}` }, timestamp) |
|||
} |
|||
|
|||
export const identifyTenantGroup = async ( |
|||
tenantId: string, |
|||
account: Account | undefined, |
|||
timestamp?: string | number |
|||
): Promise<void> => { |
|||
const id = await getEventTenantId(tenantId) |
|||
const type = IdentityType.TENANT |
|||
|
|||
let hosting: Hosting |
|||
let profession: string | undefined |
|||
let companySize: string | undefined |
|||
|
|||
if (account) { |
|||
profession = account.profession |
|||
companySize = account.size |
|||
hosting = account.hosting |
|||
} else { |
|||
hosting = getHostingFromEnv() |
|||
} |
|||
|
|||
const group: TenantGroup = { |
|||
id, |
|||
type, |
|||
hosting, |
|||
profession, |
|||
companySize, |
|||
} |
|||
|
|||
await identifyGroup(group, timestamp) |
|||
// need to create a normal identity for the group to be able to query it globally
|
|||
// match the posthog syntax to link this identity to the auto generated one
|
|||
await identify({ ...group, id: `$${type}_${id}` }, timestamp) |
|||
} |
|||
|
|||
export const identifyUser = async ( |
|||
user: User, |
|||
account: CloudAccount | undefined, |
|||
timestamp?: string | number |
|||
) => { |
|||
const id = user._id as string |
|||
const tenantId = await getEventTenantId(user.tenantId) |
|||
const type = IdentityType.USER |
|||
let builder = user.builder?.global || false |
|||
let admin = user.admin?.global || false |
|||
let providerType = user.providerType |
|||
const accountHolder = account?.budibaseUserId === user._id || false |
|||
const verified = |
|||
account && account?.budibaseUserId === user._id ? account.verified : false |
|||
const installationId = await getInstallationId() |
|||
|
|||
const identity: UserIdentity = { |
|||
id, |
|||
type, |
|||
installationId, |
|||
tenantId, |
|||
verified, |
|||
accountHolder, |
|||
providerType, |
|||
builder, |
|||
admin, |
|||
} |
|||
|
|||
await identify(identity, timestamp) |
|||
} |
|||
|
|||
export const identifyAccount = async (account: Account) => { |
|||
let id = account.accountId |
|||
const tenantId = account.tenantId |
|||
let type = IdentityType.USER |
|||
let providerType = isSSOAccount(account) ? account.providerType : undefined |
|||
const verified = account.verified |
|||
const accountHolder = true |
|||
|
|||
if (isCloudAccount(account)) { |
|||
if (account.budibaseUserId) { |
|||
// use the budibase user as the id if set
|
|||
id = account.budibaseUserId |
|||
} |
|||
} |
|||
|
|||
const identity: UserIdentity = { |
|||
id, |
|||
type, |
|||
tenantId, |
|||
providerType, |
|||
verified, |
|||
accountHolder, |
|||
} |
|||
|
|||
await identify(identity) |
|||
} |
|||
|
|||
export const identify = async ( |
|||
identity: Identity, |
|||
timestamp?: string | number |
|||
) => { |
|||
await processors.identify(identity, timestamp) |
|||
} |
|||
|
|||
export const identifyGroup = async ( |
|||
group: Group, |
|||
timestamp?: string | number |
|||
) => { |
|||
await processors.identifyGroup(group, timestamp) |
|||
} |
|||
|
|||
const getHostingFromEnv = () => { |
|||
return env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD |
|||
} |
|||
|
|||
export const getInstallationId = async () => { |
|||
if (isAccountPortal()) { |
|||
return "account-portal" |
|||
} |
|||
const install = await installation.getInstall() |
|||
return install.installId |
|||
} |
|||
|
|||
const getEventTenantId = async (tenantId: string): Promise<string> => { |
|||
if (env.SELF_HOSTED) { |
|||
return getUniqueTenantId(tenantId) |
|||
} else { |
|||
// tenant id's in the cloud are already unique
|
|||
return tenantId |
|||
} |
|||
} |
|||
|
|||
const getUniqueTenantId = async (tenantId: string): Promise<string> => { |
|||
// make sure this tenantId always matches the tenantId in context
|
|||
return context.doInTenant(tenantId, () => { |
|||
return withCache(CacheKeys.UNIQUE_TENANT_ID, TTL.ONE_DAY, async () => { |
|||
const db = context.getGlobalDB() |
|||
const config: SettingsConfig = await dbUtils.getScopedFullConfig(db, { |
|||
type: Configs.SETTINGS, |
|||
}) |
|||
|
|||
let uniqueTenantId: string |
|||
if (config.config.uniqueTenantId) { |
|||
return config.config.uniqueTenantId |
|||
} else { |
|||
uniqueTenantId = `${hashing.newid()}_${tenantId}` |
|||
config.config.uniqueTenantId = uniqueTenantId |
|||
await db.put(config) |
|||
return uniqueTenantId |
|||
} |
|||
}) |
|||
}) |
|||
} |
|||
|
|||
const isAccountPortal = () => { |
|||
return env.SERVICE === "account-portal" |
|||
} |
|||
|
|||
const formatDistinctId = (id: string, type: IdentityType) => { |
|||
if (type === IdentityType.INSTALLATION || type === IdentityType.TENANT) { |
|||
return `$${type}_${id}` |
|||
} else { |
|||
return id |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
export * from "./publishers" |
|||
export * as processors from "./processors" |
|||
export * as analytics from "./analytics" |
|||
export * as identification from "./identification" |
|||
export * as backfillCache from "./backfill" |
|||
|
|||
import { processors } from "./processors" |
|||
|
|||
export const shutdown = () => { |
|||
processors.shutdown() |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
import { Event, Identity, Group, IdentityType } from "@budibase/types" |
|||
import { EventProcessor } from "./types" |
|||
import env from "../../environment" |
|||
import * as analytics from "../analytics" |
|||
import PosthogProcessor from "./PosthogProcessor" |
|||
|
|||
/** |
|||
* Events that are always captured. |
|||
*/ |
|||
const EVENT_WHITELIST = [Event.VERSION_UPGRADED, Event.VERSION_DOWNGRADED] |
|||
const IDENTITY_WHITELIST = [IdentityType.INSTALLATION, IdentityType.TENANT] |
|||
|
|||
export default class AnalyticsProcessor implements EventProcessor { |
|||
posthog: PosthogProcessor | undefined |
|||
|
|||
constructor() { |
|||
if (env.POSTHOG_TOKEN) { |
|||
this.posthog = new PosthogProcessor(env.POSTHOG_TOKEN) |
|||
} |
|||
} |
|||
|
|||
async processEvent( |
|||
event: Event, |
|||
identity: Identity, |
|||
properties: any, |
|||
timestamp?: string | number |
|||
): Promise<void> { |
|||
if (!EVENT_WHITELIST.includes(event) && !(await analytics.enabled())) { |
|||
return |
|||
} |
|||
if (this.posthog) { |
|||
this.posthog.processEvent(event, identity, properties, timestamp) |
|||
} |
|||
} |
|||
|
|||
async identify(identity: Identity, timestamp?: string | number) { |
|||
// Group indentifications (tenant and installation) always on
|
|||
if ( |
|||
!IDENTITY_WHITELIST.includes(identity.type) && |
|||
!(await analytics.enabled()) |
|||
) { |
|||
return |
|||
} |
|||
if (this.posthog) { |
|||
this.posthog.identify(identity, timestamp) |
|||
} |
|||
} |
|||
|
|||
async identifyGroup(group: Group, timestamp?: string | number) { |
|||
// Group indentifications (tenant and installation) always on
|
|||
if (this.posthog) { |
|||
this.posthog.identifyGroup(group, timestamp) |
|||
} |
|||
} |
|||
|
|||
shutdown() { |
|||
if (this.posthog) { |
|||
this.posthog.shutdown() |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
import { Event, Identity, Group } from "@budibase/types" |
|||
import { EventProcessor } from "./types" |
|||
import env from "../../environment" |
|||
|
|||
const getTimestampString = (timestamp?: string | number) => { |
|||
let timestampString = "" |
|||
if (timestamp) { |
|||
timestampString = `[timestamp=${new Date(timestamp).toISOString()}]` |
|||
} |
|||
return timestampString |
|||
} |
|||
|
|||
const skipLogging = env.SELF_HOSTED && !env.isDev() |
|||
|
|||
export default class LoggingProcessor implements EventProcessor { |
|||
async processEvent( |
|||
event: Event, |
|||
identity: Identity, |
|||
properties: any, |
|||
timestamp?: string |
|||
): Promise<void> { |
|||
if (skipLogging) { |
|||
return |
|||
} |
|||
let timestampString = getTimestampString(timestamp) |
|||
console.log( |
|||
`[audit] [tenant=${identity.tenantId}] [identityType=${identity.type}] [identity=${identity.id}] ${timestampString} ${event} ` |
|||
) |
|||
} |
|||
|
|||
async identify(identity: Identity, timestamp?: string | number) { |
|||
if (skipLogging) { |
|||
return |
|||
} |
|||
let timestampString = getTimestampString(timestamp) |
|||
console.log( |
|||
`[audit] [${JSON.stringify(identity)}] ${timestampString} identified` |
|||
) |
|||
} |
|||
|
|||
async identifyGroup(group: Group, timestamp?: string | number) { |
|||
if (skipLogging) { |
|||
return |
|||
} |
|||
let timestampString = getTimestampString(timestamp) |
|||
console.log( |
|||
`[audit] [${JSON.stringify(group)}] ${timestampString} group identified` |
|||
) |
|||
} |
|||
|
|||
shutdown(): void { |
|||
// no-op
|
|||
} |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
import PostHog from "posthog-node" |
|||
import { Event, Identity, Group, BaseEvent } from "@budibase/types" |
|||
import { EventProcessor } from "./types" |
|||
import env from "../../environment" |
|||
import context from "../../context" |
|||
const pkg = require("../../../package.json") |
|||
|
|||
export default class PosthogProcessor implements EventProcessor { |
|||
posthog: PostHog |
|||
|
|||
constructor(token: string | undefined) { |
|||
if (!token) { |
|||
throw new Error("Posthog token is not defined") |
|||
} |
|||
this.posthog = new PostHog(token) |
|||
} |
|||
|
|||
async processEvent( |
|||
event: Event, |
|||
identity: Identity, |
|||
properties: BaseEvent, |
|||
timestamp?: string | number |
|||
): Promise<void> { |
|||
properties.version = pkg.version |
|||
properties.service = env.SERVICE |
|||
properties.environment = env.DEPLOYMENT_ENVIRONMENT |
|||
|
|||
const appId = context.getAppId() |
|||
if (appId) { |
|||
properties.appId = appId |
|||
} |
|||
|
|||
const payload: any = { distinctId: identity.id, event, properties } |
|||
|
|||
if (timestamp) { |
|||
payload.timestamp = new Date(timestamp) |
|||
} |
|||
|
|||
// add groups to the event
|
|||
if (identity.installationId || identity.tenantId) { |
|||
payload.groups = {} |
|||
if (identity.installationId) { |
|||
payload.groups.installation = identity.installationId |
|||
payload.properties.installationId = identity.installationId |
|||
} |
|||
if (identity.tenantId) { |
|||
payload.groups.tenant = identity.tenantId |
|||
payload.properties.tenantId = identity.tenantId |
|||
} |
|||
} |
|||
|
|||
this.posthog.capture(payload) |
|||
} |
|||
|
|||
async identify(identity: Identity, timestamp?: string | number) { |
|||
const payload: any = { distinctId: identity.id, properties: identity } |
|||
if (timestamp) { |
|||
payload.timestamp = new Date(timestamp) |
|||
} |
|||
this.posthog.identify(payload) |
|||
} |
|||
|
|||
async identifyGroup(group: Group, timestamp?: string | number) { |
|||
const payload: any = { |
|||
distinctId: group.id, |
|||
groupType: group.type, |
|||
groupKey: group.id, |
|||
properties: group, |
|||
} |
|||
|
|||
if (timestamp) { |
|||
payload.timestamp = new Date(timestamp) |
|||
} |
|||
this.posthog.groupIdentify(payload) |
|||
} |
|||
|
|||
shutdown() { |
|||
this.posthog.shutdown() |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
import { Event, Identity, Group } from "@budibase/types" |
|||
import { EventProcessor } from "./types" |
|||
|
|||
export default class Processor implements EventProcessor { |
|||
initialised: boolean = false |
|||
processors: EventProcessor[] = [] |
|||
|
|||
constructor(processors: EventProcessor[]) { |
|||
this.processors = processors |
|||
} |
|||
|
|||
async processEvent( |
|||
event: Event, |
|||
identity: Identity, |
|||
properties: any, |
|||
timestamp?: string | number |
|||
): Promise<void> { |
|||
for (const eventProcessor of this.processors) { |
|||
await eventProcessor.processEvent(event, identity, properties, timestamp) |
|||
} |
|||
} |
|||
|
|||
async identify( |
|||
identity: Identity, |
|||
timestamp?: string | number |
|||
): Promise<void> { |
|||
for (const eventProcessor of this.processors) { |
|||
await eventProcessor.identify(identity, timestamp) |
|||
} |
|||
} |
|||
|
|||
async identifyGroup( |
|||
identity: Group, |
|||
timestamp?: string | number |
|||
): Promise<void> { |
|||
for (const eventProcessor of this.processors) { |
|||
await eventProcessor.identifyGroup(identity, timestamp) |
|||
} |
|||
} |
|||
|
|||
shutdown() { |
|||
for (const eventProcessor of this.processors) { |
|||
eventProcessor.shutdown() |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
import AnalyticsProcessor from "./AnalyticsProcessor" |
|||
import LoggingProcessor from "./LoggingProcessor" |
|||
import Processors from "./Processors" |
|||
|
|||
export const analyticsProcessor = new AnalyticsProcessor() |
|||
const loggingProcessor = new LoggingProcessor() |
|||
|
|||
export const processors = new Processors([analyticsProcessor, loggingProcessor]) |
|||
@ -0,0 +1,18 @@ |
|||
import { Event, Identity, Group } from "@budibase/types" |
|||
|
|||
export enum EventProcessorType { |
|||
POSTHOG = "posthog", |
|||
LOGGING = "logging", |
|||
} |
|||
|
|||
export interface EventProcessor { |
|||
processEvent( |
|||
event: Event, |
|||
identity: Identity, |
|||
properties: any, |
|||
timestamp?: string | number |
|||
): Promise<void> |
|||
identify(identity: Identity, timestamp?: string | number): Promise<void> |
|||
identifyGroup(group: Group, timestamp?: string | number): Promise<void> |
|||
shutdown(): void |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
Account, |
|||
AccountCreatedEvent, |
|||
AccountDeletedEvent, |
|||
AccountVerifiedEvent, |
|||
} from "@budibase/types" |
|||
|
|||
export async function created(account: Account) { |
|||
const properties: AccountCreatedEvent = { |
|||
tenantId: account.tenantId, |
|||
} |
|||
await publishEvent(Event.ACCOUNT_CREATED, properties) |
|||
} |
|||
|
|||
export async function deleted(account: Account) { |
|||
const properties: AccountDeletedEvent = { |
|||
tenantId: account.tenantId, |
|||
} |
|||
await publishEvent(Event.ACCOUNT_DELETED, properties) |
|||
} |
|||
|
|||
export async function verified(account: Account) { |
|||
const properties: AccountVerifiedEvent = { |
|||
tenantId: account.tenantId, |
|||
} |
|||
await publishEvent(Event.ACCOUNT_VERIFIED, properties) |
|||
} |
|||
@ -0,0 +1,108 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
App, |
|||
AppCreatedEvent, |
|||
AppUpdatedEvent, |
|||
AppDeletedEvent, |
|||
AppPublishedEvent, |
|||
AppUnpublishedEvent, |
|||
AppFileImportedEvent, |
|||
AppTemplateImportedEvent, |
|||
AppVersionUpdatedEvent, |
|||
AppVersionRevertedEvent, |
|||
AppRevertedEvent, |
|||
AppExportedEvent, |
|||
} from "@budibase/types" |
|||
|
|||
export const created = async (app: App, timestamp?: string | number) => { |
|||
const properties: AppCreatedEvent = { |
|||
appId: app.appId, |
|||
version: app.version, |
|||
} |
|||
await publishEvent(Event.APP_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function updated(app: App) { |
|||
const properties: AppUpdatedEvent = { |
|||
appId: app.appId, |
|||
version: app.version, |
|||
} |
|||
await publishEvent(Event.APP_UPDATED, properties) |
|||
} |
|||
|
|||
export async function deleted(app: App) { |
|||
const properties: AppDeletedEvent = { |
|||
appId: app.appId, |
|||
} |
|||
await publishEvent(Event.APP_DELETED, properties) |
|||
} |
|||
|
|||
export async function published(app: App, timestamp?: string | number) { |
|||
const properties: AppPublishedEvent = { |
|||
appId: app.appId, |
|||
} |
|||
await publishEvent(Event.APP_PUBLISHED, properties, timestamp) |
|||
} |
|||
|
|||
export async function unpublished(app: App) { |
|||
const properties: AppUnpublishedEvent = { |
|||
appId: app.appId, |
|||
} |
|||
await publishEvent(Event.APP_UNPUBLISHED, properties) |
|||
} |
|||
|
|||
export async function fileImported(app: App) { |
|||
const properties: AppFileImportedEvent = { |
|||
appId: app.appId, |
|||
} |
|||
await publishEvent(Event.APP_FILE_IMPORTED, properties) |
|||
} |
|||
|
|||
export async function templateImported(app: App, templateKey: string) { |
|||
const properties: AppTemplateImportedEvent = { |
|||
appId: app.appId, |
|||
templateKey, |
|||
} |
|||
await publishEvent(Event.APP_TEMPLATE_IMPORTED, properties) |
|||
} |
|||
|
|||
export async function versionUpdated( |
|||
app: App, |
|||
currentVersion: string, |
|||
updatedToVersion: string |
|||
) { |
|||
const properties: AppVersionUpdatedEvent = { |
|||
appId: app.appId, |
|||
currentVersion, |
|||
updatedToVersion, |
|||
} |
|||
await publishEvent(Event.APP_VERSION_UPDATED, properties) |
|||
} |
|||
|
|||
export async function versionReverted( |
|||
app: App, |
|||
currentVersion: string, |
|||
revertedToVersion: string |
|||
) { |
|||
const properties: AppVersionRevertedEvent = { |
|||
appId: app.appId, |
|||
currentVersion, |
|||
revertedToVersion, |
|||
} |
|||
await publishEvent(Event.APP_VERSION_REVERTED, properties) |
|||
} |
|||
|
|||
export async function reverted(app: App) { |
|||
const properties: AppRevertedEvent = { |
|||
appId: app.appId, |
|||
} |
|||
await publishEvent(Event.APP_REVERTED, properties) |
|||
} |
|||
|
|||
export async function exported(app: App) { |
|||
const properties: AppExportedEvent = { |
|||
appId: app.appId, |
|||
} |
|||
await publishEvent(Event.APP_EXPORTED, properties) |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
LoginEvent, |
|||
LoginSource, |
|||
LogoutEvent, |
|||
SSOActivatedEvent, |
|||
SSOCreatedEvent, |
|||
SSODeactivatedEvent, |
|||
SSOType, |
|||
SSOUpdatedEvent, |
|||
} from "@budibase/types" |
|||
import { identification } from ".." |
|||
|
|||
export async function login(source: LoginSource) { |
|||
const identity = await identification.getCurrentIdentity() |
|||
const properties: LoginEvent = { |
|||
userId: identity.id, |
|||
source, |
|||
} |
|||
await publishEvent(Event.AUTH_LOGIN, properties) |
|||
} |
|||
|
|||
export async function logout() { |
|||
const identity = await identification.getCurrentIdentity() |
|||
const properties: LogoutEvent = { |
|||
userId: identity.id, |
|||
} |
|||
await publishEvent(Event.AUTH_LOGOUT, properties) |
|||
} |
|||
|
|||
export async function SSOCreated(type: SSOType, timestamp?: string | number) { |
|||
const properties: SSOCreatedEvent = { |
|||
type, |
|||
} |
|||
await publishEvent(Event.AUTH_SSO_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function SSOUpdated(type: SSOType) { |
|||
const properties: SSOUpdatedEvent = { |
|||
type, |
|||
} |
|||
await publishEvent(Event.AUTH_SSO_UPDATED, properties) |
|||
} |
|||
|
|||
export async function SSOActivated(type: SSOType, timestamp?: string | number) { |
|||
const properties: SSOActivatedEvent = { |
|||
type, |
|||
} |
|||
await publishEvent(Event.AUTH_SSO_ACTIVATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function SSODeactivated(type: SSOType) { |
|||
const properties: SSODeactivatedEvent = { |
|||
type, |
|||
} |
|||
await publishEvent(Event.AUTH_SSO_DEACTIVATED, properties) |
|||
} |
|||
@ -0,0 +1,94 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Automation, |
|||
Event, |
|||
AutomationStep, |
|||
AutomationCreatedEvent, |
|||
AutomationDeletedEvent, |
|||
AutomationTestedEvent, |
|||
AutomationStepCreatedEvent, |
|||
AutomationStepDeletedEvent, |
|||
AutomationTriggerUpdatedEvent, |
|||
AutomationsRunEvent, |
|||
} from "@budibase/types" |
|||
|
|||
export async function created( |
|||
automation: Automation, |
|||
timestamp?: string | number |
|||
) { |
|||
const properties: AutomationCreatedEvent = { |
|||
appId: automation.appId, |
|||
automationId: automation._id as string, |
|||
triggerId: automation.definition?.trigger?.id, |
|||
triggerType: automation.definition?.trigger?.stepId, |
|||
} |
|||
await publishEvent(Event.AUTOMATION_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function triggerUpdated(automation: Automation) { |
|||
const properties: AutomationTriggerUpdatedEvent = { |
|||
appId: automation.appId, |
|||
automationId: automation._id as string, |
|||
triggerId: automation.definition?.trigger?.id, |
|||
triggerType: automation.definition?.trigger?.stepId, |
|||
} |
|||
await publishEvent(Event.AUTOMATION_TRIGGER_UPDATED, properties) |
|||
} |
|||
|
|||
export async function deleted(automation: Automation) { |
|||
const properties: AutomationDeletedEvent = { |
|||
appId: automation.appId, |
|||
automationId: automation._id as string, |
|||
triggerId: automation.definition?.trigger?.id, |
|||
triggerType: automation.definition?.trigger?.stepId, |
|||
} |
|||
await publishEvent(Event.AUTOMATION_DELETED, properties) |
|||
} |
|||
|
|||
export async function tested(automation: Automation) { |
|||
const properties: AutomationTestedEvent = { |
|||
appId: automation.appId, |
|||
automationId: automation._id as string, |
|||
triggerId: automation.definition?.trigger?.id, |
|||
triggerType: automation.definition?.trigger?.stepId, |
|||
} |
|||
await publishEvent(Event.AUTOMATION_TESTED, properties) |
|||
} |
|||
|
|||
export const run = async (count: number, timestamp?: string | number) => { |
|||
const properties: AutomationsRunEvent = { |
|||
count, |
|||
} |
|||
await publishEvent(Event.AUTOMATIONS_RUN, properties, timestamp) |
|||
} |
|||
|
|||
export async function stepCreated( |
|||
automation: Automation, |
|||
step: AutomationStep, |
|||
timestamp?: string | number |
|||
) { |
|||
const properties: AutomationStepCreatedEvent = { |
|||
appId: automation.appId, |
|||
automationId: automation._id as string, |
|||
triggerId: automation.definition?.trigger?.id, |
|||
triggerType: automation.definition?.trigger?.stepId, |
|||
stepId: step.id, |
|||
stepType: step.stepId, |
|||
} |
|||
await publishEvent(Event.AUTOMATION_STEP_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function stepDeleted( |
|||
automation: Automation, |
|||
step: AutomationStep |
|||
) { |
|||
const properties: AutomationStepDeletedEvent = { |
|||
appId: automation.appId, |
|||
automationId: automation._id as string, |
|||
triggerId: automation.definition?.trigger?.id, |
|||
triggerType: automation.definition?.trigger?.stepId, |
|||
stepId: step.id, |
|||
stepType: step.stepId, |
|||
} |
|||
await publishEvent(Event.AUTOMATION_STEP_DELETED, properties) |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
AppBackfillSucceededEvent, |
|||
AppBackfillFailedEvent, |
|||
TenantBackfillSucceededEvent, |
|||
TenantBackfillFailedEvent, |
|||
InstallationBackfillSucceededEvent, |
|||
InstallationBackfillFailedEvent, |
|||
} from "@budibase/types" |
|||
const env = require("../../environment") |
|||
|
|||
const shouldSkip = !env.SELF_HOSTED && !env.isDev() |
|||
|
|||
export async function appSucceeded(properties: AppBackfillSucceededEvent) { |
|||
if (shouldSkip) { |
|||
return |
|||
} |
|||
await publishEvent(Event.APP_BACKFILL_SUCCEEDED, properties) |
|||
} |
|||
|
|||
export async function appFailed(error: any) { |
|||
if (shouldSkip) { |
|||
return |
|||
} |
|||
const properties: AppBackfillFailedEvent = { |
|||
error: JSON.stringify(error, Object.getOwnPropertyNames(error)), |
|||
} |
|||
await publishEvent(Event.APP_BACKFILL_FAILED, properties) |
|||
} |
|||
|
|||
export async function tenantSucceeded( |
|||
properties: TenantBackfillSucceededEvent |
|||
) { |
|||
if (shouldSkip) { |
|||
return |
|||
} |
|||
await publishEvent(Event.TENANT_BACKFILL_SUCCEEDED, properties) |
|||
} |
|||
|
|||
export async function tenantFailed(error: any) { |
|||
if (shouldSkip) { |
|||
return |
|||
} |
|||
const properties: TenantBackfillFailedEvent = { |
|||
error: JSON.stringify(error, Object.getOwnPropertyNames(error)), |
|||
} |
|||
await publishEvent(Event.TENANT_BACKFILL_FAILED, properties) |
|||
} |
|||
|
|||
export async function installationSucceeded() { |
|||
if (shouldSkip) { |
|||
return |
|||
} |
|||
const properties: InstallationBackfillSucceededEvent = {} |
|||
await publishEvent(Event.INSTALLATION_BACKFILL_SUCCEEDED, properties) |
|||
} |
|||
|
|||
export async function installationFailed(error: any) { |
|||
if (shouldSkip) { |
|||
return |
|||
} |
|||
const properties: InstallationBackfillFailedEvent = { |
|||
error: JSON.stringify(error, Object.getOwnPropertyNames(error)), |
|||
} |
|||
await publishEvent(Event.INSTALLATION_BACKFILL_FAILED, properties) |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
Datasource, |
|||
DatasourceCreatedEvent, |
|||
DatasourceUpdatedEvent, |
|||
DatasourceDeletedEvent, |
|||
} from "@budibase/types" |
|||
|
|||
export async function created( |
|||
datasource: Datasource, |
|||
timestamp?: string | number |
|||
) { |
|||
const properties: DatasourceCreatedEvent = { |
|||
datasourceId: datasource._id as string, |
|||
source: datasource.source, |
|||
} |
|||
await publishEvent(Event.DATASOURCE_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function updated(datasource: Datasource) { |
|||
const properties: DatasourceUpdatedEvent = { |
|||
datasourceId: datasource._id as string, |
|||
source: datasource.source, |
|||
} |
|||
await publishEvent(Event.DATASOURCE_UPDATED, properties) |
|||
} |
|||
|
|||
export async function deleted(datasource: Datasource) { |
|||
const properties: DatasourceDeletedEvent = { |
|||
datasourceId: datasource._id as string, |
|||
source: datasource.source, |
|||
} |
|||
await publishEvent(Event.DATASOURCE_DELETED, properties) |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
import { publishEvent } from "../events" |
|||
import { Event, SMTPCreatedEvent, SMTPUpdatedEvent } from "@budibase/types" |
|||
|
|||
export async function SMTPCreated(timestamp?: string | number) { |
|||
const properties: SMTPCreatedEvent = {} |
|||
await publishEvent(Event.EMAIL_SMTP_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function SMTPUpdated() { |
|||
const properties: SMTPUpdatedEvent = {} |
|||
await publishEvent(Event.EMAIL_SMTP_UPDATED, properties) |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
export * as account from "./account" |
|||
export * as app from "./app" |
|||
export * as auth from "./auth" |
|||
export * as automation from "./automation" |
|||
export * as datasource from "./datasource" |
|||
export * as email from "./email" |
|||
export * as license from "./license" |
|||
export * as layout from "./layout" |
|||
export * as org from "./org" |
|||
export * as query from "./query" |
|||
export * as role from "./role" |
|||
export * as screen from "./screen" |
|||
export * as rows from "./rows" |
|||
export * as table from "./table" |
|||
export * as serve from "./serve" |
|||
export * as user from "./user" |
|||
export * as view from "./view" |
|||
export * as version from "./version" |
|||
export * as backfill from "./backfill" |
|||
@ -0,0 +1,21 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
Layout, |
|||
LayoutCreatedEvent, |
|||
LayoutDeletedEvent, |
|||
} from "@budibase/types" |
|||
|
|||
export async function created(layout: Layout, timestamp?: string | number) { |
|||
const properties: LayoutCreatedEvent = { |
|||
layoutId: layout._id as string, |
|||
} |
|||
await publishEvent(Event.LAYOUT_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function deleted(layoutId: string) { |
|||
const properties: LayoutDeletedEvent = { |
|||
layoutId, |
|||
} |
|||
await publishEvent(Event.LAYOUT_DELETED, properties) |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
License, |
|||
LicenseActivatedEvent, |
|||
LicenseDowngradedEvent, |
|||
LicenseUpdatedEvent, |
|||
LicenseUpgradedEvent, |
|||
} from "@budibase/types" |
|||
|
|||
// TODO
|
|||
export async function updgraded(license: License) { |
|||
const properties: LicenseUpgradedEvent = {} |
|||
await publishEvent(Event.LICENSE_UPGRADED, properties) |
|||
} |
|||
|
|||
// TODO
|
|||
export async function downgraded(license: License) { |
|||
const properties: LicenseDowngradedEvent = {} |
|||
await publishEvent(Event.LICENSE_DOWNGRADED, properties) |
|||
} |
|||
|
|||
// TODO
|
|||
export async function updated(license: License) { |
|||
const properties: LicenseUpdatedEvent = {} |
|||
await publishEvent(Event.LICENSE_UPDATED, properties) |
|||
} |
|||
|
|||
// TODO
|
|||
export async function activated(license: License) { |
|||
const properties: LicenseActivatedEvent = {} |
|||
await publishEvent(Event.LICENSE_ACTIVATED, properties) |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
import { publishEvent } from "../events" |
|||
import { Event } from "@budibase/types" |
|||
|
|||
export async function nameUpdated(timestamp?: string | number) { |
|||
const properties = {} |
|||
await publishEvent(Event.ORG_NAME_UPDATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function logoUpdated(timestamp?: string | number) { |
|||
const properties = {} |
|||
await publishEvent(Event.ORG_LOGO_UPDATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function platformURLUpdated(timestamp?: string | number) { |
|||
const properties = {} |
|||
await publishEvent(Event.ORG_PLATFORM_URL_UPDATED, properties, timestamp) |
|||
} |
|||
|
|||
// TODO
|
|||
|
|||
export async function analyticsOptOut() { |
|||
const properties = {} |
|||
await publishEvent(Event.ANALYTICS_OPT_OUT, properties) |
|||
} |
|||
|
|||
export async function analyticsOptIn() { |
|||
const properties = {} |
|||
await publishEvent(Event.ANALYTICS_OPT_OUT, properties) |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
Datasource, |
|||
Query, |
|||
QueryCreatedEvent, |
|||
QueryUpdatedEvent, |
|||
QueryDeletedEvent, |
|||
QueryImportedEvent, |
|||
QueryPreviewedEvent, |
|||
QueriesRunEvent, |
|||
} from "@budibase/types" |
|||
|
|||
/* eslint-disable */ |
|||
|
|||
export const created = async ( |
|||
datasource: Datasource, |
|||
query: Query, |
|||
timestamp?: string | number |
|||
) => { |
|||
const properties: QueryCreatedEvent = { |
|||
queryId: query._id as string, |
|||
datasourceId: datasource._id as string, |
|||
source: datasource.source, |
|||
queryVerb: query.queryVerb, |
|||
} |
|||
await publishEvent(Event.QUERY_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export const updated = async (datasource: Datasource, query: Query) => { |
|||
const properties: QueryUpdatedEvent = { |
|||
queryId: query._id as string, |
|||
datasourceId: datasource._id as string, |
|||
source: datasource.source, |
|||
queryVerb: query.queryVerb, |
|||
} |
|||
await publishEvent(Event.QUERY_UPDATED, properties) |
|||
} |
|||
|
|||
export const deleted = async (datasource: Datasource, query: Query) => { |
|||
const properties: QueryDeletedEvent = { |
|||
queryId: query._id as string, |
|||
datasourceId: datasource._id as string, |
|||
source: datasource.source, |
|||
queryVerb: query.queryVerb, |
|||
} |
|||
await publishEvent(Event.QUERY_DELETED, properties) |
|||
} |
|||
|
|||
export const imported = async ( |
|||
datasource: Datasource, |
|||
importSource: any, |
|||
count: any |
|||
) => { |
|||
const properties: QueryImportedEvent = { |
|||
datasourceId: datasource._id as string, |
|||
source: datasource.source, |
|||
count, |
|||
importSource, |
|||
} |
|||
await publishEvent(Event.QUERY_IMPORT, properties) |
|||
} |
|||
|
|||
export const run = async (count: number, timestamp?: string | number) => { |
|||
const properties: QueriesRunEvent = { |
|||
count, |
|||
} |
|||
await publishEvent(Event.QUERIES_RUN, properties, timestamp) |
|||
} |
|||
|
|||
export const previewed = async (datasource: Datasource, query: Query) => { |
|||
const properties: QueryPreviewedEvent = { |
|||
queryId: query._id, |
|||
datasourceId: datasource._id as string, |
|||
source: datasource.source, |
|||
queryVerb: query.queryVerb, |
|||
} |
|||
await publishEvent(Event.QUERY_PREVIEWED, properties) |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
Role, |
|||
RoleAssignedEvent, |
|||
RoleCreatedEvent, |
|||
RoleDeletedEvent, |
|||
RoleUnassignedEvent, |
|||
RoleUpdatedEvent, |
|||
User, |
|||
} from "@budibase/types" |
|||
|
|||
export async function created(role: Role, timestamp?: string | number) { |
|||
const properties: RoleCreatedEvent = { |
|||
roleId: role._id as string, |
|||
permissionId: role.permissionId, |
|||
inherits: role.inherits, |
|||
} |
|||
await publishEvent(Event.ROLE_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function updated(role: Role) { |
|||
const properties: RoleUpdatedEvent = { |
|||
roleId: role._id as string, |
|||
permissionId: role.permissionId, |
|||
inherits: role.inherits, |
|||
} |
|||
await publishEvent(Event.ROLE_UPDATED, properties) |
|||
} |
|||
|
|||
export async function deleted(role: Role) { |
|||
const properties: RoleDeletedEvent = { |
|||
roleId: role._id as string, |
|||
permissionId: role.permissionId, |
|||
inherits: role.inherits, |
|||
} |
|||
await publishEvent(Event.ROLE_DELETED, properties) |
|||
} |
|||
|
|||
export async function assigned(user: User, roleId: string, timestamp?: number) { |
|||
const properties: RoleAssignedEvent = { |
|||
userId: user._id as string, |
|||
roleId, |
|||
} |
|||
await publishEvent(Event.ROLE_ASSIGNED, properties, timestamp) |
|||
} |
|||
|
|||
export async function unassigned(user: User, roleId: string) { |
|||
const properties: RoleUnassignedEvent = { |
|||
userId: user._id as string, |
|||
roleId, |
|||
} |
|||
await publishEvent(Event.ROLE_UNASSIGNED, properties) |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
RowsImportedEvent, |
|||
RowsCreatedEvent, |
|||
RowImportFormat, |
|||
Table, |
|||
} from "@budibase/types" |
|||
|
|||
/* eslint-disable */ |
|||
|
|||
export const created = async (count: number, timestamp?: string | number) => { |
|||
const properties: RowsCreatedEvent = { |
|||
count, |
|||
} |
|||
await publishEvent(Event.ROWS_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export const imported = async ( |
|||
table: Table, |
|||
format: RowImportFormat, |
|||
count: number |
|||
) => { |
|||
const properties: RowsImportedEvent = { |
|||
tableId: table._id as string, |
|||
format, |
|||
count, |
|||
} |
|||
await publishEvent(Event.ROWS_IMPORTED, properties) |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
Screen, |
|||
ScreenCreatedEvent, |
|||
ScreenDeletedEvent, |
|||
} from "@budibase/types" |
|||
|
|||
export async function created(screen: Screen, timestamp?: string | number) { |
|||
const properties: ScreenCreatedEvent = { |
|||
layoutId: screen.layoutId, |
|||
screenId: screen._id as string, |
|||
roleId: screen.routing.roleId, |
|||
} |
|||
await publishEvent(Event.SCREEN_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function deleted(screen: Screen) { |
|||
const properties: ScreenDeletedEvent = { |
|||
layoutId: screen.layoutId, |
|||
screenId: screen._id as string, |
|||
roleId: screen.routing.roleId, |
|||
} |
|||
await publishEvent(Event.SCREEN_DELETED, properties) |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
App, |
|||
BuilderServedEvent, |
|||
Event, |
|||
AppPreviewServedEvent, |
|||
AppServedEvent, |
|||
} from "@budibase/types" |
|||
|
|||
export async function servedBuilder() { |
|||
const properties: BuilderServedEvent = {} |
|||
await publishEvent(Event.SERVED_BUILDER, properties) |
|||
} |
|||
|
|||
export async function servedApp(app: App) { |
|||
const properties: AppServedEvent = { |
|||
appVersion: app.version, |
|||
} |
|||
await publishEvent(Event.SERVED_APP, properties) |
|||
} |
|||
|
|||
export async function servedAppPreview(app: App) { |
|||
const properties: AppPreviewServedEvent = { |
|||
appId: app.appId, |
|||
appVersion: app.version, |
|||
} |
|||
await publishEvent(Event.SERVED_APP_PREVIEW, properties) |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
TableExportFormat, |
|||
TableImportFormat, |
|||
Table, |
|||
TableCreatedEvent, |
|||
TableUpdatedEvent, |
|||
TableDeletedEvent, |
|||
TableExportedEvent, |
|||
TableImportedEvent, |
|||
} from "@budibase/types" |
|||
|
|||
export async function created(table: Table, timestamp?: string | number) { |
|||
const properties: TableCreatedEvent = { |
|||
tableId: table._id as string, |
|||
} |
|||
await publishEvent(Event.TABLE_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function updated(table: Table) { |
|||
const properties: TableUpdatedEvent = { |
|||
tableId: table._id as string, |
|||
} |
|||
await publishEvent(Event.TABLE_UPDATED, properties) |
|||
} |
|||
|
|||
export async function deleted(table: Table) { |
|||
const properties: TableDeletedEvent = { |
|||
tableId: table._id as string, |
|||
} |
|||
await publishEvent(Event.TABLE_DELETED, properties) |
|||
} |
|||
|
|||
export async function exported(table: Table, format: TableExportFormat) { |
|||
const properties: TableExportedEvent = { |
|||
tableId: table._id as string, |
|||
format, |
|||
} |
|||
await publishEvent(Event.TABLE_EXPORTED, properties) |
|||
} |
|||
|
|||
export async function imported(table: Table, format: TableImportFormat) { |
|||
const properties: TableImportedEvent = { |
|||
tableId: table._id as string, |
|||
format, |
|||
} |
|||
await publishEvent(Event.TABLE_IMPORTED, properties) |
|||
} |
|||
@ -0,0 +1,122 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
User, |
|||
UserCreatedEvent, |
|||
UserDeletedEvent, |
|||
UserInviteAcceptedEvent, |
|||
UserInvitedEvent, |
|||
UserPasswordForceResetEvent, |
|||
UserPasswordResetEvent, |
|||
UserPasswordResetRequestedEvent, |
|||
UserPasswordUpdatedEvent, |
|||
UserPermissionAssignedEvent, |
|||
UserPermissionRemovedEvent, |
|||
UserUpdatedEvent, |
|||
} from "@budibase/types" |
|||
|
|||
export async function created(user: User, timestamp?: number) { |
|||
const properties: UserCreatedEvent = { |
|||
userId: user._id as string, |
|||
} |
|||
await publishEvent(Event.USER_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function updated(user: User) { |
|||
const properties: UserUpdatedEvent = { |
|||
userId: user._id as string, |
|||
} |
|||
await publishEvent(Event.USER_UPDATED, properties) |
|||
} |
|||
|
|||
export async function deleted(user: User) { |
|||
const properties: UserDeletedEvent = { |
|||
userId: user._id as string, |
|||
} |
|||
await publishEvent(Event.USER_DELETED, properties) |
|||
} |
|||
|
|||
// PERMISSIONS
|
|||
|
|||
export async function permissionAdminAssigned(user: User, timestamp?: number) { |
|||
const properties: UserPermissionAssignedEvent = { |
|||
userId: user._id as string, |
|||
} |
|||
await publishEvent( |
|||
Event.USER_PERMISSION_ADMIN_ASSIGNED, |
|||
properties, |
|||
timestamp |
|||
) |
|||
} |
|||
|
|||
export async function permissionAdminRemoved(user: User) { |
|||
const properties: UserPermissionRemovedEvent = { |
|||
userId: user._id as string, |
|||
} |
|||
await publishEvent(Event.USER_PERMISSION_ADMIN_REMOVED, properties) |
|||
} |
|||
|
|||
export async function permissionBuilderAssigned( |
|||
user: User, |
|||
timestamp?: number |
|||
) { |
|||
const properties: UserPermissionAssignedEvent = { |
|||
userId: user._id as string, |
|||
} |
|||
await publishEvent( |
|||
Event.USER_PERMISSION_BUILDER_ASSIGNED, |
|||
properties, |
|||
timestamp |
|||
) |
|||
} |
|||
|
|||
export async function permissionBuilderRemoved(user: User) { |
|||
const properties: UserPermissionRemovedEvent = { |
|||
userId: user._id as string, |
|||
} |
|||
await publishEvent(Event.USER_PERMISSION_BUILDER_REMOVED, properties) |
|||
} |
|||
|
|||
// INVITE
|
|||
|
|||
export async function invited() { |
|||
const properties: UserInvitedEvent = {} |
|||
await publishEvent(Event.USER_INVITED, properties) |
|||
} |
|||
|
|||
export async function inviteAccepted(user: User) { |
|||
const properties: UserInviteAcceptedEvent = { |
|||
userId: user._id as string, |
|||
} |
|||
await publishEvent(Event.USER_INVITED_ACCEPTED, properties) |
|||
} |
|||
|
|||
// PASSWORD
|
|||
|
|||
export async function passwordForceReset(user: User) { |
|||
const properties: UserPasswordForceResetEvent = { |
|||
userId: user._id as string, |
|||
} |
|||
await publishEvent(Event.USER_PASSWORD_FORCE_RESET, properties) |
|||
} |
|||
|
|||
export async function passwordUpdated(user: User) { |
|||
const properties: UserPasswordUpdatedEvent = { |
|||
userId: user._id as string, |
|||
} |
|||
await publishEvent(Event.USER_PASSWORD_UPDATED, properties) |
|||
} |
|||
|
|||
export async function passwordResetRequested(user: User) { |
|||
const properties: UserPasswordResetRequestedEvent = { |
|||
userId: user._id as string, |
|||
} |
|||
await publishEvent(Event.USER_PASSWORD_RESET_REQUESTED, properties) |
|||
} |
|||
|
|||
export async function passwordReset(user: User) { |
|||
const properties: UserPasswordResetEvent = { |
|||
userId: user._id as string, |
|||
} |
|||
await publishEvent(Event.USER_PASSWORD_RESET, properties) |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
import { publishEvent } from "../events" |
|||
import { Event, VersionCheckedEvent, VersionChangeEvent } from "@budibase/types" |
|||
|
|||
export async function checked(version: string) { |
|||
const properties: VersionCheckedEvent = { |
|||
currentVersion: version, |
|||
} |
|||
await publishEvent(Event.VERSION_CHECKED, properties) |
|||
} |
|||
|
|||
export async function upgraded(from: string, to: string) { |
|||
const properties: VersionChangeEvent = { |
|||
from, |
|||
to, |
|||
} |
|||
|
|||
await publishEvent(Event.VERSION_UPGRADED, properties) |
|||
} |
|||
|
|||
export async function downgraded(from: string, to: string) { |
|||
const properties: VersionChangeEvent = { |
|||
from, |
|||
to, |
|||
} |
|||
await publishEvent(Event.VERSION_DOWNGRADED, properties) |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
import { publishEvent } from "../events" |
|||
import { |
|||
Event, |
|||
ViewCalculationCreatedEvent, |
|||
ViewCalculationDeletedEvent, |
|||
ViewCalculationUpdatedEvent, |
|||
ViewCreatedEvent, |
|||
ViewDeletedEvent, |
|||
ViewExportedEvent, |
|||
ViewFilterCreatedEvent, |
|||
ViewFilterDeletedEvent, |
|||
ViewFilterUpdatedEvent, |
|||
ViewUpdatedEvent, |
|||
View, |
|||
ViewCalculation, |
|||
Table, |
|||
TableExportFormat, |
|||
} from "@budibase/types" |
|||
|
|||
/* eslint-disable */ |
|||
|
|||
export async function created(view: View, timestamp?: string | number) { |
|||
const properties: ViewCreatedEvent = { |
|||
tableId: view.tableId, |
|||
} |
|||
await publishEvent(Event.VIEW_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function updated(view: View) { |
|||
const properties: ViewUpdatedEvent = { |
|||
tableId: view.tableId, |
|||
} |
|||
await publishEvent(Event.VIEW_UPDATED, properties) |
|||
} |
|||
|
|||
export async function deleted(view: View) { |
|||
const properties: ViewDeletedEvent = { |
|||
tableId: view.tableId, |
|||
} |
|||
await publishEvent(Event.VIEW_DELETED, properties) |
|||
} |
|||
|
|||
export async function exported(table: Table, format: TableExportFormat) { |
|||
const properties: ViewExportedEvent = { |
|||
tableId: table._id as string, |
|||
format, |
|||
} |
|||
await publishEvent(Event.VIEW_EXPORTED, properties) |
|||
} |
|||
|
|||
export async function filterCreated(view: View, timestamp?: string | number) { |
|||
const properties: ViewFilterCreatedEvent = { |
|||
tableId: view.tableId, |
|||
} |
|||
await publishEvent(Event.VIEW_FILTER_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function filterUpdated(view: View) { |
|||
const properties: ViewFilterUpdatedEvent = { |
|||
tableId: view.tableId, |
|||
} |
|||
await publishEvent(Event.VIEW_FILTER_UPDATED, properties) |
|||
} |
|||
|
|||
export async function filterDeleted(view: View) { |
|||
const properties: ViewFilterDeletedEvent = { |
|||
tableId: view.tableId, |
|||
} |
|||
await publishEvent(Event.VIEW_FILTER_DELETED, properties) |
|||
} |
|||
|
|||
export async function calculationCreated( |
|||
view: View, |
|||
timestamp?: string | number |
|||
) { |
|||
const properties: ViewCalculationCreatedEvent = { |
|||
tableId: view.tableId, |
|||
calculation: view.calculation as ViewCalculation, |
|||
} |
|||
await publishEvent(Event.VIEW_CALCULATION_CREATED, properties, timestamp) |
|||
} |
|||
|
|||
export async function calculationUpdated(view: View) { |
|||
const properties: ViewCalculationUpdatedEvent = { |
|||
tableId: view.tableId, |
|||
calculation: view.calculation as ViewCalculation, |
|||
} |
|||
await publishEvent(Event.VIEW_CALCULATION_UPDATED, properties) |
|||
} |
|||
|
|||
export async function calculationDeleted(existingView: View) { |
|||
const properties: ViewCalculationDeletedEvent = { |
|||
tableId: existingView.tableId, |
|||
calculation: existingView.calculation as ViewCalculation, |
|||
} |
|||
await publishEvent(Event.VIEW_CALCULATION_DELETED, properties) |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
const db = require("./db") |
|||
|
|||
module.exports = { |
|||
init(opts = {}) { |
|||
db.init(opts.db) |
|||
}, |
|||
// some default exports from the library, however these ideally shouldn't
|
|||
// be used, instead the syntax require("@budibase/backend-core/db") should be used
|
|||
StaticDatabases: require("./db/utils").StaticDatabases, |
|||
db: require("../db"), |
|||
redis: require("../redis"), |
|||
objectStore: require("../objectStore"), |
|||
utils: require("../utils"), |
|||
cache: require("../cache"), |
|||
auth: require("../auth"), |
|||
constants: require("../constants"), |
|||
migrations: require("../migrations"), |
|||
errors: require("./errors"), |
|||
env: require("./environment"), |
|||
accounts: require("./cloud/accounts"), |
|||
tenancy: require("./tenancy"), |
|||
context: require("../context"), |
|||
featureFlags: require("./featureFlags"), |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
import errors from "./errors" |
|||
const errorClasses = errors.errors |
|||
import * as events from "./events" |
|||
import * as migrations from "./migrations" |
|||
import * as users from "./users" |
|||
import * as accounts from "./cloud/accounts" |
|||
import * as installation from "./installation" |
|||
import env from "./environment" |
|||
import tenancy from "./tenancy" |
|||
import featureFlags from "./featureFlags" |
|||
import sessions from "./security/sessions" |
|||
import deprovisioning from "./context/deprovision" |
|||
import auth from "./auth" |
|||
import constants from "./constants" |
|||
import * as dbConstants from "./db/constants" |
|||
|
|||
// mimic the outer package exports
|
|||
import * as db from "./pkg/db" |
|||
import * as objectStore from "./pkg/objectStore" |
|||
import * as utils from "./pkg/utils" |
|||
import redis from "./pkg/redis" |
|||
import cache from "./pkg/cache" |
|||
import context from "./pkg/context" |
|||
|
|||
const init = (opts: any = {}) => { |
|||
db.init(opts.db) |
|||
} |
|||
|
|||
const core = { |
|||
init, |
|||
db, |
|||
...dbConstants, |
|||
redis, |
|||
objectStore, |
|||
utils, |
|||
users, |
|||
cache, |
|||
auth, |
|||
constants, |
|||
...constants, |
|||
migrations, |
|||
env, |
|||
accounts, |
|||
tenancy, |
|||
context, |
|||
featureFlags, |
|||
events, |
|||
sessions, |
|||
deprovisioning, |
|||
installation, |
|||
errors, |
|||
...errorClasses, |
|||
} |
|||
|
|||
export = core |
|||
@ -0,0 +1,96 @@ |
|||
import * as hashing from "./hashing" |
|||
import * as events from "./events" |
|||
import { StaticDatabases } from "./db/constants" |
|||
import { doWithDB } from "./db" |
|||
import { Installation, IdentityType } from "@budibase/types" |
|||
import * as context from "./context" |
|||
import semver from "semver" |
|||
import { bustCache, withCache, TTL, CacheKeys } from "./cache/generic" |
|||
|
|||
const pkg = require("../package.json") |
|||
|
|||
export const getInstall = async (): Promise<Installation> => { |
|||
return withCache(CacheKeys.INSTALLATION, TTL.ONE_DAY, getInstallFromDB, { |
|||
useTenancy: false, |
|||
}) |
|||
} |
|||
|
|||
const getInstallFromDB = async (): Promise<Installation> => { |
|||
return doWithDB( |
|||
StaticDatabases.PLATFORM_INFO.name, |
|||
async (platformDb: any) => { |
|||
let install: Installation |
|||
try { |
|||
install = await platformDb.get( |
|||
StaticDatabases.PLATFORM_INFO.docs.install |
|||
) |
|||
} catch (e: any) { |
|||
if (e.status === 404) { |
|||
install = { |
|||
_id: StaticDatabases.PLATFORM_INFO.docs.install, |
|||
installId: hashing.newid(), |
|||
version: pkg.version, |
|||
} |
|||
const resp = await platformDb.put(install) |
|||
install._rev = resp.rev |
|||
} else { |
|||
throw e |
|||
} |
|||
} |
|||
return install |
|||
} |
|||
) |
|||
} |
|||
|
|||
const updateVersion = async (version: string): Promise<boolean> => { |
|||
try { |
|||
await doWithDB( |
|||
StaticDatabases.PLATFORM_INFO.name, |
|||
async (platformDb: any) => { |
|||
const install = await getInstall() |
|||
install.version = version |
|||
await platformDb.put(install) |
|||
await bustCache(CacheKeys.INSTALLATION) |
|||
} |
|||
) |
|||
} catch (e: any) { |
|||
if (e.status === 409) { |
|||
// do nothing - version has already been updated
|
|||
// likely in clustered environment
|
|||
return false |
|||
} |
|||
throw e |
|||
} |
|||
return true |
|||
} |
|||
|
|||
export const checkInstallVersion = async (): Promise<void> => { |
|||
const install = await getInstall() |
|||
|
|||
const currentVersion = install.version |
|||
const newVersion = pkg.version |
|||
|
|||
if (currentVersion !== newVersion) { |
|||
const isUpgrade = semver.gt(newVersion, currentVersion) |
|||
const isDowngrade = semver.lt(newVersion, currentVersion) |
|||
|
|||
const success = await updateVersion(newVersion) |
|||
|
|||
if (success) { |
|||
await context.doInIdentityContext( |
|||
{ |
|||
_id: install.installId, |
|||
type: IdentityType.INSTALLATION, |
|||
}, |
|||
async () => { |
|||
if (isUpgrade) { |
|||
await events.version.upgraded(currentVersion, newVersion) |
|||
} else if (isDowngrade) { |
|||
await events.version.downgraded(currentVersion, newVersion) |
|||
} |
|||
} |
|||
) |
|||
await events.identification.identifyInstallationGroup(install.installId) |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
import { |
|||
MigrationType, |
|||
MigrationName, |
|||
MigrationDefinition, |
|||
} from "@budibase/types" |
|||
|
|||
export const DEFINITIONS: MigrationDefinition[] = [ |
|||
{ |
|||
type: MigrationType.GLOBAL, |
|||
name: MigrationName.USER_EMAIL_VIEW_CASING, |
|||
}, |
|||
{ |
|||
type: MigrationType.GLOBAL, |
|||
name: MigrationName.QUOTAS_1, |
|||
}, |
|||
{ |
|||
type: MigrationType.APP, |
|||
name: MigrationName.APP_URLS, |
|||
}, |
|||
{ |
|||
type: MigrationType.GLOBAL, |
|||
name: MigrationName.DEVELOPER_QUOTA, |
|||
}, |
|||
{ |
|||
type: MigrationType.GLOBAL, |
|||
name: MigrationName.PUBLISHED_APP_QUOTA, |
|||
}, |
|||
{ |
|||
type: MigrationType.APP, |
|||
name: MigrationName.EVENT_APP_BACKFILL, |
|||
}, |
|||
{ |
|||
type: MigrationType.GLOBAL, |
|||
name: MigrationName.EVENT_GLOBAL_BACKFILL, |
|||
}, |
|||
{ |
|||
type: MigrationType.INSTALLATION, |
|||
name: MigrationName.EVENT_INSTALLATION_BACKFILL, |
|||
}, |
|||
] |
|||
@ -1,117 +0,0 @@ |
|||
const { DEFAULT_TENANT_ID } = require("../constants") |
|||
const { doWithDB } = require("../db") |
|||
const { DocumentTypes } = require("../db/constants") |
|||
const { getAllApps } = require("../db/utils") |
|||
const environment = require("../environment") |
|||
const { |
|||
doInTenant, |
|||
getTenantIds, |
|||
getGlobalDBName, |
|||
getTenantId, |
|||
} = require("../tenancy") |
|||
|
|||
exports.MIGRATION_TYPES = { |
|||
GLOBAL: "global", // run once, recorded in global db, global db is provided as an argument
|
|||
APP: "app", // run per app, recorded in each app db, app db is provided as an argument
|
|||
} |
|||
|
|||
exports.getMigrationsDoc = async db => { |
|||
// get the migrations doc
|
|||
try { |
|||
return await db.get(DocumentTypes.MIGRATIONS) |
|||
} catch (err) { |
|||
if (err.status && err.status === 404) { |
|||
return { _id: DocumentTypes.MIGRATIONS } |
|||
} |
|||
console.error(err) |
|||
} |
|||
} |
|||
|
|||
const runMigration = async (migration, options = {}) => { |
|||
const tenantId = getTenantId() |
|||
const migrationType = migration.type |
|||
const migrationName = migration.name |
|||
|
|||
// get the db to store the migration in
|
|||
let dbNames |
|||
if (migrationType === exports.MIGRATION_TYPES.GLOBAL) { |
|||
dbNames = [getGlobalDBName()] |
|||
} else if (migrationType === exports.MIGRATION_TYPES.APP) { |
|||
const apps = await getAllApps(migration.opts) |
|||
dbNames = apps.map(app => app.appId) |
|||
} else { |
|||
throw new Error( |
|||
`[Tenant: ${tenantId}] Unrecognised migration type [${migrationType}]` |
|||
) |
|||
} |
|||
|
|||
// run the migration against each db
|
|||
for (const dbName of dbNames) { |
|||
await doWithDB(dbName, async db => { |
|||
try { |
|||
const doc = await exports.getMigrationsDoc(db) |
|||
|
|||
// exit if the migration has been performed already
|
|||
if (doc[migrationName]) { |
|||
if ( |
|||
options.force && |
|||
options.force[migrationType] && |
|||
options.force[migrationType].includes(migrationName) |
|||
) { |
|||
console.log( |
|||
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Forcing` |
|||
) |
|||
} else { |
|||
// the migration has already been performed
|
|||
return |
|||
} |
|||
} |
|||
|
|||
console.log( |
|||
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Running` |
|||
) |
|||
// run the migration with tenant context
|
|||
await migration.fn(db) |
|||
console.log( |
|||
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Complete` |
|||
) |
|||
|
|||
// mark as complete
|
|||
doc[migrationName] = Date.now() |
|||
await db.put(doc) |
|||
} catch (err) { |
|||
console.error( |
|||
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Error: `, |
|||
err |
|||
) |
|||
throw err |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
|
|||
exports.runMigrations = async (migrations, options = {}) => { |
|||
console.log("Running migrations") |
|||
let tenantIds |
|||
if (environment.MULTI_TENANCY) { |
|||
if (!options.tenantIds || !options.tenantIds.length) { |
|||
// run for all tenants
|
|||
tenantIds = await getTenantIds() |
|||
} else { |
|||
tenantIds = options.tenantIds |
|||
} |
|||
} else { |
|||
// single tenancy
|
|||
tenantIds = [DEFAULT_TENANT_ID] |
|||
} |
|||
|
|||
// for all tenants
|
|||
for (const tenantId of tenantIds) { |
|||
// for all migrations
|
|||
for (const migration of migrations) { |
|||
// run the migration
|
|||
await doInTenant(tenantId, () => runMigration(migration, options)) |
|||
} |
|||
} |
|||
console.log("Migrations complete") |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
export * from "./migrations" |
|||
export * from "./definitions" |
|||
@ -0,0 +1,189 @@ |
|||
import { DEFAULT_TENANT_ID } from "../constants" |
|||
import { doWithDB } from "../db" |
|||
import { DocumentTypes, StaticDatabases } from "../db/constants" |
|||
import { getAllApps } from "../db/utils" |
|||
import environment from "../environment" |
|||
import { |
|||
doInTenant, |
|||
getTenantIds, |
|||
getGlobalDBName, |
|||
getTenantId, |
|||
} from "../tenancy" |
|||
import context from "../context" |
|||
import { DEFINITIONS } from "." |
|||
import { |
|||
Migration, |
|||
MigrationOptions, |
|||
MigrationType, |
|||
MigrationNoOpOptions, |
|||
} from "@budibase/types" |
|||
|
|||
export const getMigrationsDoc = async (db: any) => { |
|||
// get the migrations doc
|
|||
try { |
|||
return await db.get(DocumentTypes.MIGRATIONS) |
|||
} catch (err: any) { |
|||
if (err.status && err.status === 404) { |
|||
return { _id: DocumentTypes.MIGRATIONS } |
|||
} else { |
|||
console.error(err) |
|||
throw err |
|||
} |
|||
} |
|||
} |
|||
|
|||
export const backPopulateMigrations = async (opts: MigrationNoOpOptions) => { |
|||
// filter migrations to the type and populate a no-op migration
|
|||
const migrations: Migration[] = DEFINITIONS.filter( |
|||
def => def.type === opts.type |
|||
).map(d => ({ ...d, fn: () => {} })) |
|||
await runMigrations(migrations, { noOp: opts }) |
|||
} |
|||
|
|||
export const runMigration = async ( |
|||
migration: Migration, |
|||
options: MigrationOptions = {} |
|||
) => { |
|||
const migrationType = migration.type |
|||
let tenantId: string |
|||
if (migrationType !== MigrationType.INSTALLATION) { |
|||
tenantId = getTenantId() |
|||
} |
|||
const migrationName = migration.name |
|||
const silent = migration.silent |
|||
|
|||
const log = (message: string) => { |
|||
if (!silent) { |
|||
console.log(message) |
|||
} |
|||
} |
|||
|
|||
// get the db to store the migration in
|
|||
let dbNames |
|||
if (migrationType === MigrationType.GLOBAL) { |
|||
dbNames = [getGlobalDBName()] |
|||
} else if (migrationType === MigrationType.APP) { |
|||
if (options.noOp) { |
|||
dbNames = [options.noOp.appId] |
|||
} else { |
|||
const apps = await getAllApps(migration.appOpts) |
|||
dbNames = apps.map(app => app.appId) |
|||
} |
|||
} else if (migrationType === MigrationType.INSTALLATION) { |
|||
dbNames = [StaticDatabases.PLATFORM_INFO.name] |
|||
} else { |
|||
throw new Error(`Unrecognised migration type [${migrationType}]`) |
|||
} |
|||
|
|||
const length = dbNames.length |
|||
let count = 0 |
|||
|
|||
// run the migration against each db
|
|||
for (const dbName of dbNames) { |
|||
count++ |
|||
const lengthStatement = length > 1 ? `[${count}/${length}]` : "" |
|||
|
|||
await doWithDB(dbName, async (db: any) => { |
|||
try { |
|||
const doc = await exports.getMigrationsDoc(db) |
|||
|
|||
// the migration has already been run
|
|||
if (doc[migrationName]) { |
|||
// check for force
|
|||
if ( |
|||
options.force && |
|||
options.force[migrationType] && |
|||
options.force[migrationType].includes(migrationName) |
|||
) { |
|||
log( |
|||
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Forcing` |
|||
) |
|||
} else { |
|||
// no force, exit
|
|||
return |
|||
} |
|||
} |
|||
|
|||
// check if the migration is not a no-op
|
|||
if (!options.noOp) { |
|||
log( |
|||
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Running ${lengthStatement}` |
|||
) |
|||
|
|||
if (migration.preventRetry) { |
|||
// eagerly set the completion date
|
|||
// so that we never run this migration twice even upon failure
|
|||
doc[migrationName] = Date.now() |
|||
const response = await db.put(doc) |
|||
doc._rev = response.rev |
|||
} |
|||
|
|||
// run the migration
|
|||
if (migrationType === MigrationType.APP) { |
|||
await context.doInAppContext(db.name, async () => { |
|||
await migration.fn(db) |
|||
}) |
|||
} else { |
|||
await migration.fn(db) |
|||
} |
|||
|
|||
log( |
|||
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Complete` |
|||
) |
|||
} |
|||
|
|||
// mark as complete
|
|||
doc[migrationName] = Date.now() |
|||
await db.put(doc) |
|||
} catch (err) { |
|||
console.error( |
|||
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Error: `, |
|||
err |
|||
) |
|||
throw err |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
|
|||
export const runMigrations = async ( |
|||
migrations: Migration[], |
|||
options: MigrationOptions = {} |
|||
) => { |
|||
let tenantIds |
|||
|
|||
if (environment.MULTI_TENANCY) { |
|||
if (options.noOp) { |
|||
tenantIds = [options.noOp.tenantId] |
|||
} else if (!options.tenantIds || !options.tenantIds.length) { |
|||
// run for all tenants
|
|||
tenantIds = await getTenantIds() |
|||
} else { |
|||
tenantIds = options.tenantIds |
|||
} |
|||
} else { |
|||
// single tenancy
|
|||
tenantIds = [DEFAULT_TENANT_ID] |
|||
} |
|||
|
|||
if (tenantIds.length > 1) { |
|||
console.log(`Checking migrations for ${tenantIds.length} tenants`) |
|||
} else { |
|||
console.log("Checking migrations") |
|||
} |
|||
|
|||
let count = 0 |
|||
// for all tenants
|
|||
for (const tenantId of tenantIds) { |
|||
count++ |
|||
if (tenantIds.length > 1) { |
|||
console.log(`Progress [${count}/${tenantIds.length}]`) |
|||
} |
|||
// for all migrations
|
|||
for (const migration of migrations) { |
|||
// run the migration
|
|||
await doInTenant(tenantId, () => runMigration(migration, options)) |
|||
} |
|||
} |
|||
console.log("Migrations complete") |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
// Mimic the outer package export for usage in index.ts
|
|||
// The outer exports can't be used as they now reference dist directly
|
|||
import * as generic from "../cache/generic" |
|||
import * as user from "../cache/user" |
|||
import * as app from "../cache/appMetadata" |
|||
|
|||
export = { |
|||
app, |
|||
user, |
|||
...generic, |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// Mimic the outer package export for usage in index.ts
|
|||
// The outer exports can't be used as they now reference dist directly
|
|||
import { |
|||
getAppDB, |
|||
getDevAppDB, |
|||
getProdAppDB, |
|||
getAppId, |
|||
updateAppId, |
|||
doInAppContext, |
|||
doInTenant, |
|||
} from "../context" |
|||
|
|||
import * as identity from "../context/identity" |
|||
|
|||
export = { |
|||
getAppDB, |
|||
getDevAppDB, |
|||
getProdAppDB, |
|||
getAppId, |
|||
updateAppId, |
|||
doInAppContext, |
|||
doInTenant, |
|||
identity, |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
// Mimic the outer package export for usage in index.ts
|
|||
// The outer exports can't be used as they now reference dist directly
|
|||
export * from "../db" |
|||
export * from "../db/utils" |
|||
export * from "../db/views" |
|||
export * from "../db/pouch" |
|||
export * from "../db/constants" |
|||
@ -0,0 +1,4 @@ |
|||
// Mimic the outer package export for usage in index.ts
|
|||
// The outer exports can't be used as they now reference dist directly
|
|||
export * from "../objectStore" |
|||
export * from "../objectStore/utils" |
|||
@ -0,0 +1,11 @@ |
|||
// Mimic the outer package export for usage in index.ts
|
|||
// The outer exports can't be used as they now reference dist directly
|
|||
import Client from "../redis" |
|||
import utils from "../redis/utils" |
|||
import clients from "../redis/authRedis" |
|||
|
|||
export = { |
|||
Client, |
|||
utils, |
|||
clients, |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
// Mimic the outer package export for usage in index.ts
|
|||
// The outer exports can't be used as they now reference dist directly
|
|||
export * from "../utils" |
|||
export * from "../hashing" |
|||
@ -0,0 +1,21 @@ |
|||
import Redlock from "redlock" |
|||
|
|||
export const getRedlock = (redisClient: any, opts = { retryCount: 10 }) => { |
|||
return new Redlock([redisClient], { |
|||
// the expected clock drift; for more details
|
|||
// see http://redis.io/topics/distlock
|
|||
driftFactor: 0.01, // multiplied by lock ttl to determine drift time
|
|||
|
|||
// the max number of times Redlock will attempt
|
|||
// to lock a resource before erroring
|
|||
retryCount: opts.retryCount, |
|||
|
|||
// the time in ms between attempts
|
|||
retryDelay: 200, // time in ms
|
|||
|
|||
// the max time in ms randomly added to retries
|
|||
// to improve performance under high contention
|
|||
// see https://www.awsarchitectureblog.com/2015/03/backoff.html
|
|||
retryJitter: 200, // time in ms
|
|||
}) |
|||
} |
|||
@ -1,4 +0,0 @@ |
|||
module.exports = { |
|||
...require("../context"), |
|||
...require("./tenancy"), |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
import * as context from "../context" |
|||
import * as tenancy from "./tenancy" |
|||
|
|||
const pkg = { |
|||
...context, |
|||
...tenancy, |
|||
} |
|||
|
|||
export = pkg |
|||
@ -0,0 +1,17 @@ |
|||
require("../../tests/utilities/TestConfiguration") |
|||
const { structures } = require("../../tests/utilities") |
|||
const utils = require("../utils") |
|||
const events = require("../events") |
|||
const { doInTenant, DEFAULT_TENANT_ID }= require("../context") |
|||
|
|||
describe("utils", () => { |
|||
describe("platformLogout", () => { |
|||
it("should call platform logout", async () => { |
|||
await doInTenant(DEFAULT_TENANT_ID, async () => { |
|||
const ctx = structures.koa.newContext() |
|||
await utils.platformLogout({ ctx, userId: "test" }) |
|||
expect(events.auth.logout).toBeCalledTimes(1) |
|||
}) |
|||
}) |
|||
}) |
|||
}) |
|||
@ -0,0 +1,21 @@ |
|||
const { ViewNames } = require("./db/utils") |
|||
const { queryGlobalView } = require("./db/views") |
|||
|
|||
/** |
|||
* Given an email address this will use a view to search through |
|||
* all the users to find one with this email address. |
|||
* @param {string} email the email to lookup the user by. |
|||
* @return {Promise<object|null>} |
|||
*/ |
|||
exports.getGlobalUserByEmail = async email => { |
|||
if (email == null) { |
|||
throw "Must supply an email address to view" |
|||
} |
|||
|
|||
const response = await queryGlobalView(ViewNames.USER_BY_EMAIL, { |
|||
key: email.toLowerCase(), |
|||
include_docs: true, |
|||
}) |
|||
|
|||
return response |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from "./utilities" |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue