Event identification

This commit is contained in:
Rory Powell 2022-05-23 22:14:44 +01:00
parent 4841c84861
commit 28b48a88fe
80 changed files with 724 additions and 408 deletions

View File

@ -46,7 +46,7 @@
}, },
"devDependencies": { "devDependencies": {
"@shopify/jest-koa-mocks": "^3.1.5", "@shopify/jest-koa-mocks": "^3.1.5",
"@budibase/types": "^1.0.126-alpha.0", "@budibase/types": "^1.0.167-alpha.8",
"@types/jest": "^27.4.1", "@types/jest": "^27.4.1",
"@types/koa": "^2.13.3", "@types/koa": "^2.13.3",
"@types/node": "^15.12.4", "@types/node": "^15.12.4",

View File

@ -16,6 +16,7 @@ const ContextKeys = {
TENANT_ID: "tenantId", TENANT_ID: "tenantId",
GLOBAL_DB: "globalDb", GLOBAL_DB: "globalDb",
APP_ID: "appId", APP_ID: "appId",
USER: "user",
// whatever the request app DB was // whatever the request app DB was
CURRENT_DB: "currentDb", CURRENT_DB: "currentDb",
// get the prod app DB from the request // get the prod app DB from the request
@ -173,6 +174,21 @@ exports.doInAppContext = (appId, task) => {
} }
} }
exports.doInUserContext = (user, task) => {
return cls.run(async () => {
cls.setOnContext(ContextKeys.USER, user)
return task()
})
}
exports.getUser = () => {
try {
return cls.getFromContext(ContextKeys.USER)
} catch (e) {
// do nothing - user is not in context
}
}
exports.updateTenantId = tenantId => { exports.updateTenantId = tenantId => {
cls.setOnContext(ContextKeys.TENANT_ID, tenantId) cls.setOnContext(ContextKeys.TENANT_ID, tenantId)
exports.setGlobalDB(tenantId) exports.setGlobalDB(tenantId)

View File

@ -10,6 +10,7 @@ import { getAppMetadata } from "../cache/appMetadata"
import { checkSlashesInUrl } from "../helpers" import { checkSlashesInUrl } from "../helpers"
import { isDevApp, isDevAppID } from "./conversions" import { isDevApp, isDevAppID } from "./conversions"
import { APP_PREFIX } from "./constants" import { APP_PREFIX } from "./constants"
import * as events from "../events"
const UNICODE_MAX = "\ufff0" const UNICODE_MAX = "\ufff0"
@ -380,15 +381,19 @@ export const getScopedFullConfig = async function (
)[0] )[0]
// custom logic for settings doc // custom logic for settings doc
// always provide the platform URL
if (type === Configs.SETTINGS) { if (type === Configs.SETTINGS) {
if (scopedConfig && scopedConfig.doc) { if (scopedConfig && scopedConfig.doc) {
// overrides affected by environment variables
scopedConfig.doc.config.platformUrl = await getPlatformUrl() scopedConfig.doc.config.platformUrl = await getPlatformUrl()
scopedConfig.doc.config.analyticsEnabled =
await events.analytics.enabled()
} else { } else {
// defaults
scopedConfig = { scopedConfig = {
doc: { doc: {
config: { config: {
platformUrl: await getPlatformUrl(), platformUrl: await getPlatformUrl(),
analyticsEnabled: await events.analytics.enabled(),
}, },
}, },
} }

View File

@ -16,7 +16,7 @@ if (!LOADED && isDev() && !isTest()) {
LOADED = true LOADED = true
} }
export = { const env: any = {
isTest, isTest,
isDev, isDev,
JWT_SECRET: process.env.JWT_SECRET, JWT_SECRET: process.env.JWT_SECRET,
@ -59,9 +59,11 @@ export = {
} }
// clean up any environment variable edge cases // clean up any environment variable edge cases
for (let [key, value] of Object.entries(module.exports)) { for (let [key, value] of Object.entries(env)) {
// handle the edge case of "0" to disable an environment variable // handle the edge case of "0" to disable an environment variable
if (value === "0") { if (value === "0") {
module.exports[key] = 0 env[key] = 0
} }
} }
export = env

View File

@ -0,0 +1,45 @@
import env from "../environment"
import * as tenancy from "../tenancy"
import * as dbUtils from "../db/utils"
import { Configs } from "../constants"
export const enabled = async () => {
// cloud - always use the environment variable
if (!env.SELF_HOSTED) {
return !!env.ENABLE_ANALYTICS
}
// self host - prefer the settings doc
// check for explicit true/false values to support
// backwards compatibility where setting may not exist
const settings = await getSettingsDoc()
if (settings?.config?.analyticsEnabled === false) {
return false
} else if (settings?.config?.analyticsEnabled === true) {
return true
}
// 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
}

View File

@ -1,9 +1,9 @@
import { Event } from "@budibase/types" import { Event } from "@budibase/types"
import { processors } from "./processors" import { processors } from "./processors"
import * as identification from "./identification"
export const publishEvent = (event: Event, properties: any) => { export const publishEvent = async (event: Event, properties: any) => {
// in future this should use async events // in future this should use async events via a distributed queue.
// via a queue. For now we can use sync as const identity = identification.getCurrentIdentity()
// this is non-blocking await processors.processEvent(event, identity, properties)
processors.processEvent(event, properties)
} }

View File

@ -0,0 +1,93 @@
import {
isCloudAccount,
isSSOAccount,
} from "./../../../types/src/documents/account/account"
import * as context from "../context"
import env from "../environment"
import {
Hosting,
User,
SessionUser,
Identity,
IdentityType,
Account,
AccountIdentity,
BudibaseIdentity,
} from "@budibase/types"
import { analyticsProcessor } from "./processors"
export const getCurrentIdentity = (): Identity => {
const user: SessionUser | undefined = context.getUser()
const tenantId = context.getTenantId()
let id: string
if (user) {
id = user._id
} else if (env.SELF_HOSTED) {
id = "installationId" // TODO
} else {
id = context.getTenantId()
}
return {
id,
tenantId,
}
}
export const identifyUser = async (user: User) => {
const id = user._id as string
const tenantId = user.tenantId
const hosting = env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD
const type = IdentityType.USER
let builder = user.builder?.global
let admin = user.admin?.global
let authType = user.providerType ? user.providerType : "password"
const identity: BudibaseIdentity = {
id,
tenantId,
hosting,
type,
builder,
admin,
authType,
}
await identify(identity)
}
export const identifyAccount = async (account: Account) => {
let id = account.accountId
const tenantId = account.tenantId
const hosting = account.hosting
let type = IdentityType.ACCOUNT
let authType = isSSOAccount(account)
? (account.providerType as string)
: "password"
if (isCloudAccount(account)) {
if (account.budibaseUserId) {
// use the budibase user as the id if set
id = account.budibaseUserId
type = IdentityType.USER
}
}
const identity: AccountIdentity = {
id,
tenantId,
hosting,
type,
authType,
verified: account.verified,
profession: account.profession,
companySize: account.size,
}
await identify(identity)
}
const identify = async (identity: Identity) => {
await analyticsProcessor.identify(identity)
}

View File

@ -1,10 +1,7 @@
import { processors } from "./processors" import { processors } from "./processors"
export * from "./publishers" export * from "./publishers"
export * as analytics from "./analytics"
export const shutdown = () => { export const shutdown = () => {
processors.shutdown() processors.shutdown()
} }
export const analyticsEnabled = () => {
return true
}

View File

@ -0,0 +1,43 @@
import { Event, Identity } from "@budibase/types"
import { EventProcessor } from "./types"
import env from "../../environment"
import * as analytics from "../analytics"
import PosthogProcessor from "./PosthogProcessor"
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
): Promise<void> {
if (!(await analytics.enabled())) {
return
}
if (this.posthog) {
this.posthog.processEvent(event, identity, properties)
}
}
async identify(identity: Identity) {
if (!(await analytics.enabled())) {
return
}
if (this.posthog) {
this.posthog.identify(identity)
}
}
shutdown() {
if (this.posthog) {
this.posthog.shutdown()
}
}
}

View File

@ -1,12 +1,15 @@
import { Event } from "@budibase/types" import { Event, Identity } from "@budibase/types"
import { getTenantId } from "../../context"
import { EventProcessor } from "./types" import { EventProcessor } from "./types"
export default class LoggingProcessor implements EventProcessor { export default class LoggingProcessor implements EventProcessor {
processEvent(event: Event, properties: any): void { async processEvent(
const tenantId = getTenantId() event: Event,
const userId = getTenantId() // TODO identity: Identity,
console.log(`[audit] [tenant=${tenantId}] [user=${userId}] ${event}`) properties: any
): Promise<void> {
console.log(
`[audit] [tenant=${identity.tenantId}] [identity=${identity.id}] ${event}`
)
} }
shutdown(): void { shutdown(): void {

View File

@ -1,7 +1,6 @@
import PostHog from "posthog-node" import PostHog from "posthog-node"
import { Event } from "@budibase/types" import { Event, Identity } from "@budibase/types"
import { EventProcessor } from "./types" import { EventProcessor } from "./types"
import { getTenantId } from "../../context"
export default class PosthogProcessor implements EventProcessor { export default class PosthogProcessor implements EventProcessor {
posthog: PostHog posthog: PostHog
@ -13,13 +12,16 @@ export default class PosthogProcessor implements EventProcessor {
this.posthog = new PostHog(token) this.posthog = new PostHog(token)
} }
processEvent(event: Event, properties: any): void { async processEvent(
const userId = getTenantId() // TODO event: Event,
this.posthog.capture({ distinctId: userId, event, properties }) identity: Identity,
properties: any
): Promise<void> {
this.posthog.capture({ distinctId: identity.id, event, properties })
} }
identify(distinctId: string, properties: any) { async identify(identity: Identity) {
this.posthog.identify({ distinctId, properties }) this.posthog.identify({ distinctId: identity.id, properties: identity })
} }
shutdown() { shutdown() {

View File

@ -1,22 +1,21 @@
import { Event } from "@budibase/types" import { Event, Identity } from "@budibase/types"
import { EventProcessor } from "./types" import { EventProcessor } from "./types"
import env from "../../environment"
import LoggingProcessor from "./LoggingProcessor"
import PosthogProcessor from "./PosthogProcessor"
export default class Processor implements EventProcessor { export default class Processor implements EventProcessor {
initialised: boolean = false
processors: EventProcessor[] = [] processors: EventProcessor[] = []
constructor() { constructor(processors: EventProcessor[]) {
if (env.ENABLE_ANALYTICS && env.POSTHOG_TOKEN) { this.processors = processors
this.processors.push(new PosthogProcessor(env.POSTHOG_TOKEN))
}
this.processors.push(new LoggingProcessor())
} }
processEvent(event: Event, properties: any): void { async processEvent(
event: Event,
identity: Identity,
properties: any
): Promise<void> {
for (const eventProcessor of this.processors) { for (const eventProcessor of this.processors) {
eventProcessor.processEvent(event, properties) await eventProcessor.processEvent(event, identity, properties)
} }
} }
@ -25,39 +24,4 @@ export default class Processor implements EventProcessor {
eventProcessor.shutdown() eventProcessor.shutdown()
} }
} }
// Identity todo
// export const identify(type: IdentityType, id: string, hosting?: Hosting) {
// const tenantId = getTenantId()
// if (!hosting) {
// hosting = env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD
// }
// const properties = {
// type,
// hosting,
// tenantId,
// }
// this.posthog!.identify(id, properties)
// }
// identifyUser(userId: string) {
// this.identify(IdentityType.USER, userId)
// }
// identifyTenant() {
// let distinctId
// if (env.SELF_HOSTED) {
// distinctId = getTenantId() // TODO: Get installation ID
// } else {
// distinctId = getTenantId()
// }
// this.identify(IdentityType.TENANT, distinctId)
// }
// identifyAccount(account: Account) {
// const distinctId = account.accountId
// const hosting = account.hosting
// this.identify(IdentityType.ACCOUNT, distinctId, hosting)
// }
} }

View File

@ -1,2 +1,8 @@
import AnalyticsProcessor from "./AnalyticsProcessor"
import LoggingProcessor from "./LoggingProcessor"
import Processors from "./Processors" import Processors from "./Processors"
export const processors = new Processors()
export const analyticsProcessor = new AnalyticsProcessor()
const loggingProcessor = new LoggingProcessor()
export const processors = new Processors([analyticsProcessor, loggingProcessor])

View File

@ -1,4 +1,4 @@
import { Event } from "@budibase/types" import { Event, Identity } from "@budibase/types"
export enum EventProcessorType { export enum EventProcessorType {
POSTHOG = "posthog", POSTHOG = "posthog",
@ -6,6 +6,6 @@ export enum EventProcessorType {
} }
export interface EventProcessor { export interface EventProcessor {
processEvent(event: Event, properties: any): void processEvent(event: Event, identity: Identity, properties: any): Promise<void>
shutdown(): void shutdown(): void
} }

View File

@ -1,17 +1,17 @@
import { publishEvent } from "../events" import { publishEvent } from "../events"
import { Event, Account } from "@budibase/types" import { Event, Account } from "@budibase/types"
export function created(account: Account) { export async function created(account: Account) {
const properties = {} const properties = {}
publishEvent(Event.ACCOUNT_CREATED, properties) await publishEvent(Event.ACCOUNT_CREATED, properties)
} }
export function deleted(account: Account) { export async function deleted(account: Account) {
const properties = {} const properties = {}
publishEvent(Event.ACCOUNT_DELETED, properties) await publishEvent(Event.ACCOUNT_DELETED, properties)
} }
export function verified(account: Account) { export async function verified(account: Account) {
const properties = {} const properties = {}
publishEvent(Event.ACCOUNT_VERIFIED, properties) await publishEvent(Event.ACCOUNT_VERIFIED, properties)
} }

View File

@ -20,54 +20,54 @@ export const created = async (app: App) => {
await publishEvent(Event.APP_CREATED, properties) await publishEvent(Event.APP_CREATED, properties)
} }
export function updated(app: App) { export async function updated(app: App) {
const properties: AppUpdatedEvent = {} const properties: AppUpdatedEvent = {}
publishEvent(Event.APP_UPDATED, properties) await publishEvent(Event.APP_UPDATED, properties)
} }
export function deleted(app: App) { export async function deleted(app: App) {
const properties: AppDeletedEvent = {} const properties: AppDeletedEvent = {}
publishEvent(Event.APP_DELETED, properties) await publishEvent(Event.APP_DELETED, properties)
} }
export function published(app: App) { export async function published(app: App) {
const properties: AppPublishedEvent = {} const properties: AppPublishedEvent = {}
publishEvent(Event.APP_PUBLISHED, properties) await publishEvent(Event.APP_PUBLISHED, properties)
} }
export function unpublished(app: App) { export async function unpublished(app: App) {
const properties: AppUnpublishedEvent = {} const properties: AppUnpublishedEvent = {}
publishEvent(Event.APP_UNPUBLISHED, properties) await publishEvent(Event.APP_UNPUBLISHED, properties)
} }
export function fileImported(app: App) { export async function fileImported(app: App) {
const properties: AppFileImportedEvent = {} const properties: AppFileImportedEvent = {}
publishEvent(Event.APP_FILE_IMPORTED, properties) await publishEvent(Event.APP_FILE_IMPORTED, properties)
} }
export function templateImported(templateKey: string) { export async function templateImported(templateKey: string) {
const properties: AppTemplateImportedEvent = { const properties: AppTemplateImportedEvent = {
templateKey, templateKey,
} }
publishEvent(Event.APP_TEMPLATE_IMPORTED, properties) await publishEvent(Event.APP_TEMPLATE_IMPORTED, properties)
} }
export function versionUpdated(app: App) { export async function versionUpdated(app: App) {
const properties: AppVersionUpdatedEvent = {} const properties: AppVersionUpdatedEvent = {}
publishEvent(Event.APP_VERSION_UPDATED, properties) await publishEvent(Event.APP_VERSION_UPDATED, properties)
} }
export function versionReverted(app: App) { export async function versionReverted(app: App) {
const properties: AppVersionRevertedEvent = {} const properties: AppVersionRevertedEvent = {}
publishEvent(Event.APP_VERSION_REVERTED, properties) await publishEvent(Event.APP_VERSION_REVERTED, properties)
} }
export function reverted(app: App) { export async function reverted(app: App) {
const properties: AppRevertedEvent = {} const properties: AppRevertedEvent = {}
publishEvent(Event.APP_REVERTED, properties) await publishEvent(Event.APP_REVERTED, properties)
} }
export function exported(app: App) { export async function exported(app: App) {
const properties: AppExportedEvent = {} const properties: AppExportedEvent = {}
publishEvent(Event.APP_EXPORTED, properties) await publishEvent(Event.APP_EXPORTED, properties)
} }

View File

@ -11,42 +11,42 @@ import {
SSOUpdatedEvent, SSOUpdatedEvent,
} from "@budibase/types" } from "@budibase/types"
export function login(source: LoginSource) { export async function login(source: LoginSource) {
const properties: LoginEvent = { const properties: LoginEvent = {
source, source,
} }
publishEvent(Event.AUTH_LOGIN, properties) await publishEvent(Event.AUTH_LOGIN, properties)
} }
export function logout() { export async function logout() {
const properties: LogoutEvent = {} const properties: LogoutEvent = {}
publishEvent(Event.AUTH_LOGOUT, properties) await publishEvent(Event.AUTH_LOGOUT, properties)
} }
export function SSOCreated(type: SSOType) { export async function SSOCreated(type: SSOType) {
const properties: SSOCreatedEvent = { const properties: SSOCreatedEvent = {
type, type,
} }
publishEvent(Event.AUTH_SSO_CREATED, properties) await publishEvent(Event.AUTH_SSO_CREATED, properties)
} }
export function SSOUpdated(type: SSOType) { export async function SSOUpdated(type: SSOType) {
const properties: SSOUpdatedEvent = { const properties: SSOUpdatedEvent = {
type, type,
} }
publishEvent(Event.AUTH_SSO_UPDATED, properties) await publishEvent(Event.AUTH_SSO_UPDATED, properties)
} }
export function SSOActivated(type: SSOType) { export async function SSOActivated(type: SSOType) {
const properties: SSOActivatedEvent = { const properties: SSOActivatedEvent = {
type, type,
} }
publishEvent(Event.AUTH_SSO_ACTIVATED, properties) await publishEvent(Event.AUTH_SSO_ACTIVATED, properties)
} }
export function SSODeactivated(type: SSOType) { export async function SSODeactivated(type: SSOType) {
const properties: SSODeactivatedEvent = { const properties: SSODeactivatedEvent = {
type, type,
} }
publishEvent(Event.AUTH_SSO_DEACTIVATED, properties) await publishEvent(Event.AUTH_SSO_DEACTIVATED, properties)
} }

View File

@ -12,41 +12,41 @@ import {
AutomationTriggerUpdatedEvent, AutomationTriggerUpdatedEvent,
} from "@budibase/types" } from "@budibase/types"
export function created(automation: Automation) { export async function created(automation: Automation) {
const properties: AutomationCreatedEvent = {} const properties: AutomationCreatedEvent = {}
publishEvent(Event.AUTOMATION_CREATED, properties) await publishEvent(Event.AUTOMATION_CREATED, properties)
} }
export function deleted(automation: Automation) { export async function deleted(automation: Automation) {
const properties: AutomationDeletedEvent = {} const properties: AutomationDeletedEvent = {}
publishEvent(Event.AUTOMATION_DELETED, properties) await publishEvent(Event.AUTOMATION_DELETED, properties)
} }
export function tested(automation: Automation) { export async function tested(automation: Automation) {
const properties: AutomationTestedEvent = {} const properties: AutomationTestedEvent = {}
publishEvent(Event.AUTOMATION_TESTED, properties) await publishEvent(Event.AUTOMATION_TESTED, properties)
} }
// TODO export async function stepCreated(
// exports.run = () => { automation: Automation,
// const properties = {} step: AutomationStep
// events.processEvent(Events.AUTOMATION_RUN, properties) ) {
// }
export function stepCreated(automation: Automation, step: AutomationStep) {
const properties: AutomationStepCreatedEvent = {} const properties: AutomationStepCreatedEvent = {}
publishEvent(Event.AUTOMATION_STEP_CREATED, properties) await publishEvent(Event.AUTOMATION_STEP_CREATED, properties)
} }
export function stepDeleted(automation: Automation, step: AutomationStep) { export async function stepDeleted(
automation: Automation,
step: AutomationStep
) {
const properties: AutomationStepDeletedEvent = {} const properties: AutomationStepDeletedEvent = {}
publishEvent(Event.AUTOMATION_STEP_DELETED, properties) await publishEvent(Event.AUTOMATION_STEP_DELETED, properties)
} }
export function triggerUpdated( export async function triggerUpdated(
automation: Automation, automation: Automation,
trigger: AutomationTrigger trigger: AutomationTrigger
) { ) {
const properties: AutomationTriggerUpdatedEvent = {} const properties: AutomationTriggerUpdatedEvent = {}
publishEvent(Event.AUTOMATION_TRIGGER_UPDATED, properties) await publishEvent(Event.AUTOMATION_TRIGGER_UPDATED, properties)
} }

View File

@ -7,17 +7,17 @@ import {
DatasourceDeletedEvent, DatasourceDeletedEvent,
} from "@budibase/types" } from "@budibase/types"
export function created(datasource: Datasource) { export async function created(datasource: Datasource) {
const properties: DatasourceCreatedEvent = {} const properties: DatasourceCreatedEvent = {}
publishEvent(Event.DATASOURCE_CREATED, properties) await publishEvent(Event.DATASOURCE_CREATED, properties)
} }
export function updated(datasource: Datasource) { export async function updated(datasource: Datasource) {
const properties: DatasourceUpdatedEvent = {} const properties: DatasourceUpdatedEvent = {}
publishEvent(Event.DATASOURCE_UPDATED, properties) await publishEvent(Event.DATASOURCE_UPDATED, properties)
} }
export function deleted(datasource: Datasource) { export async function deleted(datasource: Datasource) {
const properties: DatasourceDeletedEvent = {} const properties: DatasourceDeletedEvent = {}
publishEvent(Event.DATASOURCE_DELETED, properties) await publishEvent(Event.DATASOURCE_DELETED, properties)
} }

View File

@ -6,12 +6,12 @@ import {
SMTPUpdatedEvent, SMTPUpdatedEvent,
} from "@budibase/types" } from "@budibase/types"
export function SMTPCreated(config: SMTPConfig) { export async function SMTPCreated(config: SMTPConfig) {
const properties: SMTPCreatedEvent = {} const properties: SMTPCreatedEvent = {}
publishEvent(Event.EMAIL_SMTP_CREATED, properties) await publishEvent(Event.EMAIL_SMTP_CREATED, properties)
} }
export function SMTPUpdated(config: SMTPConfig) { export async function SMTPUpdated(config: SMTPConfig) {
const properties: SMTPUpdatedEvent = {} const properties: SMTPUpdatedEvent = {}
publishEvent(Event.EMAIL_SMTP_UPDATED, properties) await publishEvent(Event.EMAIL_SMTP_UPDATED, properties)
} }

View File

@ -6,12 +6,12 @@ import {
LayoutDeletedEvent, LayoutDeletedEvent,
} from "@budibase/types" } from "@budibase/types"
export function created(layout: Layout) { export async function created(layout: Layout) {
const properties: LayoutCreatedEvent = {} const properties: LayoutCreatedEvent = {}
publishEvent(Event.LAYOUT_CREATED, properties) await publishEvent(Event.LAYOUT_CREATED, properties)
} }
export function deleted(layout: Layout) { export async function deleted(layout: Layout) {
const properties: LayoutDeletedEvent = {} const properties: LayoutDeletedEvent = {}
publishEvent(Event.LAYOUT_DELETED, properties) await publishEvent(Event.LAYOUT_DELETED, properties)
} }

View File

@ -10,34 +10,34 @@ import {
} from "@budibase/types" } from "@budibase/types"
// TODO // TODO
export function updgraded(license: License) { export async function updgraded(license: License) {
const properties: LicenseUpgradedEvent = {} const properties: LicenseUpgradedEvent = {}
publishEvent(Event.LICENSE_UPGRADED, properties) await publishEvent(Event.LICENSE_UPGRADED, properties)
} }
// TODO // TODO
export function downgraded(license: License) { export async function downgraded(license: License) {
const properties: LicenseDowngradedEvent = {} const properties: LicenseDowngradedEvent = {}
publishEvent(Event.LICENSE_DOWNGRADED, properties) await publishEvent(Event.LICENSE_DOWNGRADED, properties)
} }
// TODO // TODO
export function updated(license: License) { export async function updated(license: License) {
const properties: LicenseUpdatedEvent = {} const properties: LicenseUpdatedEvent = {}
publishEvent(Event.LICENSE_UPDATED, properties) await publishEvent(Event.LICENSE_UPDATED, properties)
} }
// TODO // TODO
export function activated(license: License) { export async function activated(license: License) {
const properties: LicenseActivatedEvent = {} const properties: LicenseActivatedEvent = {}
publishEvent(Event.LICENSE_ACTIVATED, properties) await publishEvent(Event.LICENSE_ACTIVATED, properties)
} }
// TODO // TODO
export function quotaExceeded(quotaName: string, value: number) { export async function quotaExceeded(quotaName: string, value: number) {
const properties: LicenseQuotaExceededEvent = { const properties: LicenseQuotaExceededEvent = {
name: quotaName, name: quotaName,
value, value,
} }
publishEvent(Event.LICENSE_QUOTA_EXCEEDED, properties) await publishEvent(Event.LICENSE_QUOTA_EXCEEDED, properties)
} }

View File

@ -1,30 +1,35 @@
import { publishEvent } from "../events" import { publishEvent } from "../events"
import { Event, VersionCheckedEvent } from "@budibase/types" import { Event, VersionCheckedEvent } from "@budibase/types"
export function nameUpdated() { export async function nameUpdated() {
const properties = {} const properties = {}
publishEvent(Event.ORG_NAME_UPDATED, properties) await publishEvent(Event.ORG_NAME_UPDATED, properties)
} }
export function logoUpdated() { export async function logoUpdated() {
const properties = {} const properties = {}
publishEvent(Event.ORG_LOGO_UPDATED, properties) await publishEvent(Event.ORG_LOGO_UPDATED, properties)
} }
export function platformURLUpdated() { export async function platformURLUpdated() {
const properties = {} const properties = {}
publishEvent(Event.ORG_PLATFORM_URL_UPDATED, properties) await publishEvent(Event.ORG_PLATFORM_URL_UPDATED, properties)
} }
export function versionChecked(version: number) { export async function versionChecked(version: number) {
const properties: VersionCheckedEvent = { const properties: VersionCheckedEvent = {
version, version,
} }
publishEvent(Event.UPDATE_VERSION_CHECKED, properties) await publishEvent(Event.UPDATE_VERSION_CHECKED, properties)
} }
// TODO // TODO
export function analyticsOptOut() { export async function analyticsOptOut() {
const properties = {} const properties = {}
publishEvent(Event.ANALYTICS_OPT_OUT, properties) await publishEvent(Event.ANALYTICS_OPT_OUT, properties)
}
export async function analyticsOptIn() {
const properties = {}
await publishEvent(Event.ANALYTICS_OPT_OUT, properties)
} }

View File

@ -12,37 +12,31 @@ import {
/* eslint-disable */ /* eslint-disable */
export const created = (datasource: Datasource, query: Query) => { export const created = async (datasource: Datasource, query: Query) => {
const properties: QueryCreatedEvent = {} const properties: QueryCreatedEvent = {}
publishEvent(Event.QUERY_CREATED, properties) await publishEvent(Event.QUERY_CREATED, properties)
} }
export const updated = (datasource: Datasource, query: Query) => { export const updated = async (datasource: Datasource, query: Query) => {
const properties: QueryUpdatedEvent = {} const properties: QueryUpdatedEvent = {}
publishEvent(Event.QUERY_UPDATED, properties) await publishEvent(Event.QUERY_UPDATED, properties)
} }
export const deleted = (datasource: Datasource, query: Query) => { export const deleted = async (datasource: Datasource, query: Query) => {
const properties: QueryDeletedEvent = {} const properties: QueryDeletedEvent = {}
publishEvent(Event.QUERY_DELETED, properties) await publishEvent(Event.QUERY_DELETED, properties)
} }
export const imported = ( export const imported = async (
datasource: Datasource, datasource: Datasource,
importSource: any, importSource: any,
count: any count: any
) => { ) => {
const properties: QueryImportedEvent = {} const properties: QueryImportedEvent = {}
publishEvent(Event.QUERY_IMPORT, properties) await publishEvent(Event.QUERY_IMPORT, properties)
} }
// TODO export const previewed = async (datasource: Datasource) => {
// exports.run = () => {
// const properties = {}
// events.processEvent(Events.QUERY_RUN, properties)
// }
export const previewed = (datasource: Datasource) => {
const properties: QueryPreviewedEvent = {} const properties: QueryPreviewedEvent = {}
publishEvent(Event.QUERY_PREVIEWED, properties) await publishEvent(Event.QUERY_PREVIEWED, properties)
} }

View File

@ -12,27 +12,27 @@ import {
/* eslint-disable */ /* eslint-disable */
export function created(role: Role) { export async function created(role: Role) {
const properties: RoleCreatedEvent = {} const properties: RoleCreatedEvent = {}
publishEvent(Event.ROLE_CREATED, properties) await publishEvent(Event.ROLE_CREATED, properties)
} }
export function updated(role: Role) { export async function updated(role: Role) {
const properties: RoleUpdatedEvent = {} const properties: RoleUpdatedEvent = {}
publishEvent(Event.ROLE_UPDATED, properties) await publishEvent(Event.ROLE_UPDATED, properties)
} }
export function deleted(role: Role) { export async function deleted(role: Role) {
const properties: RoleDeletedEvent = {} const properties: RoleDeletedEvent = {}
publishEvent(Event.ROLE_DELETED, properties) await publishEvent(Event.ROLE_DELETED, properties)
} }
export function assigned(user: User, role: string) { export async function assigned(user: User, role: string) {
const properties: RoleAssignedEvent = {} const properties: RoleAssignedEvent = {}
publishEvent(Event.ROLE_ASSIGNED, properties) await publishEvent(Event.ROLE_ASSIGNED, properties)
} }
export function unassigned(user: User, role: string) { export async function unassigned(user: User, role: string) {
const properties: RoleUnassignedEvent = {} const properties: RoleUnassignedEvent = {}
publishEvent(Event.ROLE_UNASSIGNED, properties) await publishEvent(Event.ROLE_UNASSIGNED, properties)
} }

View File

@ -9,18 +9,18 @@ import {
/* eslint-disable */ /* eslint-disable */
export const created = (count: number) => { export const created = async (count: number) => {
const properties: RowsCreatedEvent = { const properties: RowsCreatedEvent = {
count, count,
} }
publishEvent(Event.ROWS_CREATED, properties) await publishEvent(Event.ROWS_CREATED, properties)
} }
export const imported = ( export const imported = async (
table: Table, table: Table,
format: RowImportFormat, format: RowImportFormat,
count: number count: number
) => { ) => {
const properties: RowsImportedEvent = {} const properties: RowsImportedEvent = {}
publishEvent(Event.ROWS_IMPORTED, properties) await publishEvent(Event.ROWS_IMPORTED, properties)
} }

View File

@ -6,12 +6,12 @@ import {
ScreenDeletedEvent, ScreenDeletedEvent,
} from "@budibase/types" } from "@budibase/types"
export function created(screen: Screen) { export async function created(screen: Screen) {
const properties: ScreenCreatedEvent = {} const properties: ScreenCreatedEvent = {}
publishEvent(Event.SCREEN_CREATED, properties) await publishEvent(Event.SCREEN_CREATED, properties)
} }
export function deleted(screen: Screen) { export async function deleted(screen: Screen) {
const properties: ScreenDeletedEvent = {} const properties: ScreenDeletedEvent = {}
publishEvent(Event.SCREEN_DELETED, properties) await publishEvent(Event.SCREEN_DELETED, properties)
} }

View File

@ -9,17 +9,17 @@ import {
/* eslint-disable */ /* eslint-disable */
export function servedBuilder(version: number) { export async function servedBuilder(version: number) {
const properties: BuilderServedEvent = {} const properties: BuilderServedEvent = {}
publishEvent(Event.SERVED_BUILDER, properties) await publishEvent(Event.SERVED_BUILDER, properties)
} }
export function servedApp(app: App) { export async function servedApp(app: App) {
const properties: AppServedEvent = {} const properties: AppServedEvent = {}
publishEvent(Event.SERVED_APP, properties) await publishEvent(Event.SERVED_APP, properties)
} }
export function servedAppPreview(app: App) { export async function servedAppPreview(app: App) {
const properties: AppPreviewServedEvent = {} const properties: AppPreviewServedEvent = {}
publishEvent(Event.SERVED_APP_PREVIEW, properties) await publishEvent(Event.SERVED_APP_PREVIEW, properties)
} }

View File

@ -13,33 +13,27 @@ import {
/* eslint-disable */ /* eslint-disable */
export function created(table: Table) { export async function created(table: Table) {
const properties: TableCreatedEvent = {} const properties: TableCreatedEvent = {}
publishEvent(Event.TABLE_CREATED, properties) await publishEvent(Event.TABLE_CREATED, properties)
} }
export function updated(table: Table) { export async function updated(table: Table) {
const properties: TableUpdatedEvent = {} const properties: TableUpdatedEvent = {}
publishEvent(Event.TABLE_UPDATED, properties) await publishEvent(Event.TABLE_UPDATED, properties)
} }
export function deleted(table: Table) { export async function deleted(table: Table) {
const properties: TableDeletedEvent = {} const properties: TableDeletedEvent = {}
publishEvent(Event.TABLE_DELETED, properties) await publishEvent(Event.TABLE_DELETED, properties)
} }
export function exported(table: Table, format: TableExportFormat) { export async function exported(table: Table, format: TableExportFormat) {
const properties: TableExportedEvent = {} const properties: TableExportedEvent = {}
publishEvent(Event.TABLE_EXPORTED, properties) await publishEvent(Event.TABLE_EXPORTED, properties)
} }
export function imported(table: Table, format: TableImportFormat) { export async function imported(table: Table, format: TableImportFormat) {
const properties: TableImportedEvent = {} const properties: TableImportedEvent = {}
publishEvent(Event.TABLE_IMPORTED, properties) await publishEvent(Event.TABLE_IMPORTED, properties)
}
// TODO
export function permissionUpdated() {
const properties = {}
publishEvent(Event.TABLE_PERMISSION_UPDATED, properties)
} }

View File

@ -17,73 +17,73 @@ import {
/* eslint-disable */ /* eslint-disable */
export function created(user: User) { export async function created(user: User) {
const properties: UserCreatedEvent = {} const properties: UserCreatedEvent = {}
publishEvent(Event.USER_CREATED, properties) await publishEvent(Event.USER_CREATED, properties)
} }
export function updated(user: User) { export async function updated(user: User) {
const properties: UserUpdatedEvent = {} const properties: UserUpdatedEvent = {}
publishEvent(Event.USER_UPDATED, properties) await publishEvent(Event.USER_UPDATED, properties)
} }
export function deleted(user: User) { export async function deleted(user: User) {
const properties: UserDeletedEvent = {} const properties: UserDeletedEvent = {}
publishEvent(Event.USER_DELETED, properties) await publishEvent(Event.USER_DELETED, properties)
} }
// PERMISSIONS // PERMISSIONS
export function permissionAdminAssigned(user: User) { export async function permissionAdminAssigned(user: User) {
const properties: UserPermissionAssignedEvent = {} const properties: UserPermissionAssignedEvent = {}
publishEvent(Event.USER_PERMISSION_ADMIN_ASSIGNED, properties) await publishEvent(Event.USER_PERMISSION_ADMIN_ASSIGNED, properties)
} }
export function permissionAdminRemoved(user: User) { export async function permissionAdminRemoved(user: User) {
const properties: UserPermissionRemovedEvent = {} const properties: UserPermissionRemovedEvent = {}
publishEvent(Event.USER_PERMISSION_ADMIN_REMOVED, properties) await publishEvent(Event.USER_PERMISSION_ADMIN_REMOVED, properties)
} }
export function permissionBuilderAssigned(user: User) { export async function permissionBuilderAssigned(user: User) {
const properties: UserPermissionAssignedEvent = {} const properties: UserPermissionAssignedEvent = {}
publishEvent(Event.USER_PERMISSION_BUILDER_ASSIGNED, properties) await publishEvent(Event.USER_PERMISSION_BUILDER_ASSIGNED, properties)
} }
export function permissionBuilderRemoved(user: User) { export async function permissionBuilderRemoved(user: User) {
const properties: UserPermissionRemovedEvent = {} const properties: UserPermissionRemovedEvent = {}
publishEvent(Event.USER_PERMISSION_BUILDER_REMOVED, properties) await publishEvent(Event.USER_PERMISSION_BUILDER_REMOVED, properties)
} }
// INVITE // INVITE
export function invited(userInfo: any) { export async function invited(userInfo: any) {
const properties: UserInvitedEvent = {} const properties: UserInvitedEvent = {}
publishEvent(Event.USER_INVITED, properties) await publishEvent(Event.USER_INVITED, properties)
} }
export function inviteAccepted(user: User) { export async function inviteAccepted(user: User) {
const properties: UserInviteAcceptedEvent = {} const properties: UserInviteAcceptedEvent = {}
publishEvent(Event.USER_INVITED_ACCEPTED, properties) await publishEvent(Event.USER_INVITED_ACCEPTED, properties)
} }
// PASSWORD // PASSWORD
export function passwordForceReset(user: User) { export async function passwordForceReset(user: User) {
const properties: UserPasswordForceResetEvent = {} const properties: UserPasswordForceResetEvent = {}
publishEvent(Event.USER_PASSWORD_FORCE_RESET, properties) await publishEvent(Event.USER_PASSWORD_FORCE_RESET, properties)
} }
export function passwordUpdated(user: User) { export async function passwordUpdated(user: User) {
const properties: UserPasswordUpdatedEvent = {} const properties: UserPasswordUpdatedEvent = {}
publishEvent(Event.USER_PASSWORD_UPDATED, properties) await publishEvent(Event.USER_PASSWORD_UPDATED, properties)
} }
export function passwordResetRequested(user: User) { export async function passwordResetRequested(user: User) {
const properties: UserPasswordResetRequestedEvent = {} const properties: UserPasswordResetRequestedEvent = {}
publishEvent(Event.USER_PASSWORD_RESET_REQUESTED, properties) await publishEvent(Event.USER_PASSWORD_RESET_REQUESTED, properties)
} }
export function passwordReset(user: User) { export async function passwordReset(user: User) {
const properties: UserPasswordResetEvent = {} const properties: UserPasswordResetEvent = {}
publishEvent(Event.USER_PASSWORD_RESET, properties) await publishEvent(Event.USER_PASSWORD_RESET, properties)
} }

View File

@ -19,52 +19,52 @@ import {
/* eslint-disable */ /* eslint-disable */
export function created(view: View) { export async function created(view: View) {
const properties: ViewCreatedEvent = {} const properties: ViewCreatedEvent = {}
publishEvent(Event.VIEW_CREATED, properties) await publishEvent(Event.VIEW_CREATED, properties)
} }
export function updated(view: View) { export async function updated(view: View) {
const properties: ViewUpdatedEvent = {} const properties: ViewUpdatedEvent = {}
publishEvent(Event.VIEW_UPDATED, properties) await publishEvent(Event.VIEW_UPDATED, properties)
} }
export function deleted() { export async function deleted() {
const properties: ViewDeletedEvent = {} const properties: ViewDeletedEvent = {}
publishEvent(Event.VIEW_DELETED, properties) await publishEvent(Event.VIEW_DELETED, properties)
} }
export function exported(table: Table, format: TableExportFormat) { export async function exported(table: Table, format: TableExportFormat) {
const properties: ViewExportedEvent = {} const properties: ViewExportedEvent = {}
publishEvent(Event.VIEW_EXPORTED, properties) await publishEvent(Event.VIEW_EXPORTED, properties)
} }
export function filterCreated() { export async function filterCreated() {
const properties: ViewFilterCreatedEvent = {} const properties: ViewFilterCreatedEvent = {}
publishEvent(Event.VIEW_FILTER_CREATED, properties) await publishEvent(Event.VIEW_FILTER_CREATED, properties)
} }
export function filterUpdated() { export async function filterUpdated() {
const properties: ViewFilterUpdatedEvent = {} const properties: ViewFilterUpdatedEvent = {}
publishEvent(Event.VIEW_FILTER_UPDATED, properties) await publishEvent(Event.VIEW_FILTER_UPDATED, properties)
} }
export function filterDeleted() { export async function filterDeleted() {
const properties: ViewFilterDeletedEvent = {} const properties: ViewFilterDeletedEvent = {}
publishEvent(Event.VIEW_FILTER_DELETED, properties) await publishEvent(Event.VIEW_FILTER_DELETED, properties)
} }
export function calculationCreated(calculation: ViewCalculation) { export async function calculationCreated(calculation: ViewCalculation) {
const properties: ViewCalculationCreatedEvent = {} const properties: ViewCalculationCreatedEvent = {}
publishEvent(Event.VIEW_CALCULATION_CREATED, properties) await publishEvent(Event.VIEW_CALCULATION_CREATED, properties)
} }
export function calculationUpdated() { export async function calculationUpdated() {
const properties: ViewCalculationUpdatedEvent = {} const properties: ViewCalculationUpdatedEvent = {}
publishEvent(Event.VIEW_CALCULATION_UPDATED, properties) await publishEvent(Event.VIEW_CALCULATION_UPDATED, properties)
} }
export function calculationDeleted() { export async function calculationDeleted() {
const properties: ViewCalculationDeletedEvent = {} const properties: ViewCalculationDeletedEvent = {}
publishEvent(Event.VIEW_CALCULATION_DELETED, properties) await publishEvent(Event.VIEW_CALCULATION_DELETED, properties)
} }

View File

@ -7,6 +7,7 @@ const env = require("../environment")
const { SEPARATOR, ViewNames, queryGlobalView } = require("../../db") const { SEPARATOR, ViewNames, queryGlobalView } = require("../../db")
const { getGlobalDB, doInTenant } = require("../tenancy") const { getGlobalDB, doInTenant } = require("../tenancy")
const { decrypt } = require("../security/encryption") const { decrypt } = require("../security/encryption")
const context = require("../context")
function finalise( function finalise(
ctx, ctx,
@ -132,7 +133,12 @@ module.exports = (
} }
// isAuthenticated is a function, so use a variable to be able to check authed state // isAuthenticated is a function, so use a variable to be able to check authed state
finalise(ctx, { authenticated, user, internal, version, publicEndpoint }) finalise(ctx, { authenticated, user, internal, version, publicEndpoint })
return next()
if (user && user.email) {
return context.doInUserContext(user, next)
} else {
return next
}
} catch (err) { } catch (err) {
// invalid token, clear the cookie // invalid token, clear the cookie
if (err && err.name === "JsonWebTokenError") { if (err && err.name === "JsonWebTokenError") {
@ -141,7 +147,7 @@ module.exports = (
// allow configuring for public access // allow configuring for public access
if ((opts && opts.publicAllowed) || publicEndpoint) { if ((opts && opts.publicAllowed) || publicEndpoint) {
finalise(ctx, { authenticated: false, version, publicEndpoint }) finalise(ctx, { authenticated: false, version, publicEndpoint })
return next() return context.doInUserContext({ _id: "public_user" }, next)
} else { } else {
ctx.throw(err.status || 403, err) ctx.throw(err.status || 403, err)
} }

View File

@ -1,6 +1,8 @@
jest.mock("../../../events", () => { jest.mock("../../../events", () => {
return { return {
analyticsEnabled: () => false, analytics: {
enabled: () => false,
},
shutdown: () => {}, shutdown: () => {},
account: { account: {
created: jest.fn(), created: jest.fn(),

View File

@ -194,6 +194,6 @@ exports.platformLogout = async ({ ctx, userId, keepActiveSession }) => {
userId, userId,
sessions.map(({ sessionId }) => sessionId) sessions.map(({ sessionId }) => sessionId)
) )
events.auth.logout() await events.auth.logout()
await userCache.invalidateUser(userId) await userCache.invalidateUser(userId)
} }

View File

@ -8,7 +8,7 @@ const SERVER_PORT = cypressConfig.env.PORT
const WORKER_PORT = cypressConfig.env.WORKER_PORT const WORKER_PORT = cypressConfig.env.WORKER_PORT
process.env.NODE_ENV = "cypress" process.env.NODE_ENV = "cypress"
process.env.ENABLE_ANALYTICS = "false" process.env.ENABLE_ANALYTICS = "0"
process.env.JWT_SECRET = cypressConfig.env.JWT_SECRET process.env.JWT_SECRET = cypressConfig.env.JWT_SECRET
process.env.COUCH_URL = `leveldb://${tmpdir}/.data/` process.env.COUCH_URL = `leveldb://${tmpdir}/.data/`
process.env.SELF_HOSTED = 1 process.env.SELF_HOSTED = 1

View File

@ -130,7 +130,7 @@
<Divider size="S" /> <Divider size="S" />
<Layout gap="XS" noPadding> <Layout gap="XS" noPadding>
<Heading size="S">Analytics</Heading> <Heading size="S">Analytics</Heading>
<Body size="S">Choose whether to opt-in or opt-out of analtics</Body> <Body size="S">Choose whether to opt-in or opt-out of analytics.</Body>
</Layout> </Layout>
<div class="fields"> <div class="fields">
<div class="field"> <div class="field">

View File

@ -145,7 +145,7 @@
"@babel/core": "^7.14.3", "@babel/core": "^7.14.3",
"@babel/preset-env": "^7.14.4", "@babel/preset-env": "^7.14.4",
"@budibase/standard-components": "^0.9.139", "@budibase/standard-components": "^0.9.139",
"@budibase/types": "^1.0.126-alpha.0", "@budibase/types": "^1.0.167-alpha.8",
"@jest/test-sequencer": "^24.8.0", "@jest/test-sequencer": "^24.8.0",
"@types/apidoc": "^0.50.0", "@types/apidoc": "^0.50.0",
"@types/bson": "^4.2.0", "@types/bson": "^4.2.0",

View File

@ -10,7 +10,7 @@ const WORKER_PORT = "4200"
// @ts-ignore // @ts-ignore
process.env.NODE_ENV = "cypress" process.env.NODE_ENV = "cypress"
process.env.ENABLE_ANALYTICS = "false" process.env.ENABLE_ANALYTICS = "0"
process.env.JWT_SECRET = "budibase" process.env.JWT_SECRET = "budibase"
process.env.COUCH_URL = `leveldb://${tmpdir}/.data/` process.env.COUCH_URL = `leveldb://${tmpdir}/.data/`
process.env.SELF_HOSTED = "1" process.env.SELF_HOSTED = "1"

View File

@ -2,6 +2,6 @@ import { events } from "@budibase/backend-core"
export const isEnabled = async (ctx: any) => { export const isEnabled = async (ctx: any) => {
ctx.body = { ctx.body = {
enabled: events.analyticsEnabled(), enabled: events.analytics.enabled(),
} }
} }

View File

@ -292,14 +292,14 @@ const performAppCreate = async (ctx: any) => {
return newApplication return newApplication
} }
const creationEvents = (request: any, app: App) => { const creationEvents = async (request: any, app: App) => {
let creationFns = [] let creationFns: ((app: App) => Promise<void>)[] = []
const body = request.body const body = request.body
if (body.useTemplate === "true") { if (body.useTemplate === "true") {
// from template // from template
if (body.templateKey) { if (body.templateKey) {
creationFns.push(() => events.app.templateImported(body.templateKey)) creationFns.push(app => events.app.templateImported(body.templateKey))
} }
// from file // from file
else if (request.files?.templateFile) { else if (request.files?.templateFile) {
@ -313,12 +313,12 @@ const creationEvents = (request: any, app: App) => {
creationFns.push(events.app.created) creationFns.push(events.app.created)
for (let fn of creationFns) { for (let fn of creationFns) {
fn(app) await fn(app)
} }
} }
const appPostCreate = async (ctx: any, app: App) => { const appPostCreate = async (ctx: any, app: App) => {
creationEvents(ctx.request, app) await creationEvents(ctx.request, app)
// app import & template creation // app import & template creation
if (ctx.request.body.useTemplate === "true") { if (ctx.request.body.useTemplate === "true") {
const rows = await getUniqueRows([app.appId]) const rows = await getUniqueRows([app.appId])
@ -363,7 +363,7 @@ export const update = async (ctx: any) => {
} }
const app = await updateAppPackage(ctx.request.body, ctx.params.appId) const app = await updateAppPackage(ctx.request.body, ctx.params.appId)
events.app.updated(app) await events.app.updated(app)
ctx.status = 200 ctx.status = 200
ctx.body = app ctx.body = app
} }
@ -386,7 +386,7 @@ export const updateClient = async (ctx: any) => {
revertableVersion: currentVersion, revertableVersion: currentVersion,
} }
const app = await updateAppPackage(appPackageUpdates, ctx.params.appId) const app = await updateAppPackage(appPackageUpdates, ctx.params.appId)
events.app.versionUpdated(app) await events.app.versionUpdated(app)
ctx.status = 200 ctx.status = 200
ctx.body = app ctx.body = app
} }
@ -410,7 +410,7 @@ export const revertClient = async (ctx: any) => {
revertableVersion: null, revertableVersion: null,
} }
const app = await updateAppPackage(appPackageUpdates, ctx.params.appId) const app = await updateAppPackage(appPackageUpdates, ctx.params.appId)
events.app.versionReverted(app) await events.app.versionReverted(app)
ctx.status = 200 ctx.status = 200
ctx.body = app ctx.body = app
} }
@ -429,10 +429,10 @@ const destroyApp = async (ctx: any) => {
if (isUnpublish) { if (isUnpublish) {
await quotas.removePublishedApp() await quotas.removePublishedApp()
events.app.unpublished(app) await events.app.unpublished(app)
} else { } else {
await quotas.removeApp() await quotas.removeApp()
events.app.deleted(app) await events.app.deleted(app)
} }
/* istanbul ignore next */ /* istanbul ignore next */

View File

@ -71,9 +71,9 @@ exports.create = async function (ctx) {
newAuto: automation, newAuto: automation,
}) })
const response = await db.put(automation) const response = await db.put(automation)
events.automation.created() await events.automation.created()
for (let step of automation.definition.steps) { for (let step of automation.definition.steps) {
events.automation.stepCreated(step) await events.automation.stepCreated(step)
} }
automation._rev = response.rev automation._rev = response.rev
@ -97,19 +97,20 @@ const getDeletedSteps = (oldAutomation, automation) => {
return oldAutomation.definition.steps.filter(s => !stepIds.includes(s.id)) return oldAutomation.definition.steps.filter(s => !stepIds.includes(s.id))
} }
const handleStepEvents = (oldAutomation, automation) => { const handleStepEvents = async (oldAutomation, automation) => {
// new steps // new steps
const newSteps = getNewSteps(oldAutomation, automation) const newSteps = getNewSteps(oldAutomation, automation)
for (let step of newSteps) { for (let step of newSteps) {
events.automation.stepCreated(step) await events.automation.stepCreated(step)
} }
// old steps // old steps
const deletedSteps = getDeletedSteps(oldAutomation, automation) const deletedSteps = getDeletedSteps(oldAutomation, automation)
for (let step of deletedSteps) { for (let step of deletedSteps) {
events.automation.stepDeleted(step) await events.automation.stepDeleted(step)
} }
} }
exports.update = async function (ctx) { exports.update = async function (ctx) {
const db = getAppDB() const db = getAppDB()
let automation = ctx.request.body let automation = ctx.request.body
@ -133,7 +134,7 @@ exports.update = async function (ctx) {
: {} : {}
// trigger has been updated, remove the test inputs // trigger has been updated, remove the test inputs
if (oldAutoTrigger && oldAutoTrigger.id !== newAutoTrigger.id) { if (oldAutoTrigger && oldAutoTrigger.id !== newAutoTrigger.id) {
events.automation.triggerUpdated() await events.automation.triggerUpdated()
await deleteEntityMetadata( await deleteEntityMetadata(
ctx.appId, ctx.appId,
MetadataTypes.AUTOMATION_TEST_INPUT, MetadataTypes.AUTOMATION_TEST_INPUT,
@ -141,7 +142,7 @@ exports.update = async function (ctx) {
) )
} }
handleStepEvents(oldAutomation, automation) await handleStepEvents(oldAutomation, automation)
ctx.status = 200 ctx.status = 200
ctx.body = { ctx.body = {
@ -179,7 +180,7 @@ exports.destroy = async function (ctx) {
// delete metadata first // delete metadata first
await cleanupAutomationMetadata(automationId) await cleanupAutomationMetadata(automationId)
ctx.body = await db.remove(automationId, ctx.params.rev) ctx.body = await db.remove(automationId, ctx.params.rev)
events.automation.deleted() await events.automation.deleted()
} }
exports.getActionList = async function (ctx) { exports.getActionList = async function (ctx) {
@ -247,5 +248,5 @@ exports.test = async function (ctx) {
}) })
await clearTestFlag(automation._id) await clearTestFlag(automation._id)
ctx.body = response ctx.body = response
events.automation.tested() await events.automation.tested()
} }

View File

@ -7,5 +7,5 @@ exports.exportAppDump = async function (ctx) {
const backupIdentifier = `${appName}-export-${new Date().getTime()}.txt` const backupIdentifier = `${appName}-export-${new Date().getTime()}.txt`
ctx.attachment(backupIdentifier) ctx.attachment(backupIdentifier)
ctx.body = await streamBackup(appId) ctx.body = await streamBackup(appId)
events.app.exported() await events.app.exported()
} }

View File

@ -109,7 +109,7 @@ exports.update = async function (ctx) {
} }
const response = await db.put(datasource) const response = await db.put(datasource)
events.datasource.updated() await events.datasource.updated()
datasource._rev = response.rev datasource._rev = response.rev
// Drain connection pools when configuration is changed // Drain connection pools when configuration is changed
@ -144,7 +144,7 @@ exports.save = async function (ctx) {
} }
const dbResp = await db.put(datasource) const dbResp = await db.put(datasource)
events.datasource.created() await events.datasource.created()
datasource._rev = dbResp.rev datasource._rev = dbResp.rev
// Drain connection pools when configuration is changed // Drain connection pools when configuration is changed
@ -179,7 +179,7 @@ exports.destroy = async function (ctx) {
// delete the datasource // delete the datasource
await db.remove(ctx.params.datasourceId, ctx.params.revId) await db.remove(ctx.params.datasourceId, ctx.params.revId)
events.datasource.deleted() await events.datasource.deleted()
ctx.message = `Datasource deleted.` ctx.message = `Datasource deleted.`
ctx.status = 200 ctx.status = 200

View File

@ -195,7 +195,7 @@ const _deployApp = async function (ctx: any) {
app = await deployApp(deployment) app = await deployApp(deployment)
} }
events.app.published(app) await events.app.published(app)
ctx.body = deployment ctx.body = deployment
} }

View File

@ -118,7 +118,7 @@ exports.revert = async ctx => {
ctx.body = { ctx.body = {
message: "Reverted changes successfully.", message: "Reverted changes successfully.",
} }
events.app.reverted() await events.app.reverted()
} catch (err) { } catch (err) {
ctx.throw(400, `Unable to revert. ${err}`) ctx.throw(400, `Unable to revert. ${err}`)
} finally { } finally {

View File

@ -20,7 +20,7 @@ exports.save = async function (ctx) {
layout._id = layout._id || generateLayoutID() layout._id = layout._id || generateLayoutID()
const response = await db.put(layout) const response = await db.put(layout)
events.layout.created() await events.layout.created()
layout._rev = response.rev layout._rev = response.rev
ctx.body = layout ctx.body = layout
@ -48,7 +48,7 @@ exports.destroy = async function (ctx) {
} }
await db.remove(layoutId, layoutRev) await db.remove(layoutId, layoutRev)
events.layout.deleted() await events.layout.deleted()
ctx.body = { message: "Layout deleted successfully" } ctx.body = { message: "Layout deleted successfully" }
ctx.status = 200 ctx.status = 200
} }

View File

@ -84,9 +84,9 @@ export class RestImporter {
const count = successQueries.length const count = successQueries.length
const importSource = this.source.getImportSource() const importSource = this.source.getImportSource()
const datasource: Datasource = await db.get(datasourceId) const datasource: Datasource = await db.get(datasourceId)
events.query.imported(datasource, importSource, count) await events.query.imported(datasource, importSource, count)
for (let query of successQueries) { for (let query of successQueries) {
events.query.created(datasource, query) await events.query.created(datasource, query)
} }
return { return {

View File

@ -92,7 +92,7 @@ export async function save(ctx: any) {
} }
const response = await db.put(query) const response = await db.put(query)
eventFn() await eventFn()
query._rev = response.rev query._rev = response.rev
ctx.body = query ctx.body = query
@ -132,7 +132,7 @@ export async function preview(ctx: any) {
}) })
const { rows, keys, info, extra } = await quotas.addQuery(runFn) const { rows, keys, info, extra } = await quotas.addQuery(runFn)
events.query.previewed(datasource) await events.query.previewed(datasource)
ctx.body = { ctx.body = {
rows, rows,
schemaFields: [...new Set(keys)], schemaFields: [...new Set(keys)],
@ -223,5 +223,5 @@ export async function destroy(ctx: any) {
await db.remove(ctx.params.queryId, ctx.params.revId) await db.remove(ctx.params.queryId, ctx.params.revId)
ctx.message = `Query deleted.` ctx.message = `Query deleted.`
ctx.status = 200 ctx.status = 200
events.query.deleted(datasource, query) await events.query.deleted(datasource, query)
} }

View File

@ -66,9 +66,9 @@ exports.save = async function (ctx) {
} }
const result = await db.put(role) const result = await db.put(role)
if (isCreate) { if (isCreate) {
events.role.created(role) await events.role.created(role)
} else { } else {
events.role.updated(role) await events.role.updated(role)
} }
await updateRolesOnUserTable(db, _id, UpdateRolesOptions.CREATED) await updateRolesOnUserTable(db, _id, UpdateRolesOptions.CREATED)
role._rev = result.rev role._rev = result.rev
@ -97,7 +97,7 @@ exports.destroy = async function (ctx) {
} }
await db.remove(roleId, ctx.params.rev) await db.remove(roleId, ctx.params.rev)
events.role.deleted(role) await events.role.deleted(role)
await updateRolesOnUserTable( await updateRolesOnUserTable(
db, db,
ctx.params.roleId, ctx.params.roleId,

View File

@ -33,7 +33,7 @@ exports.save = async ctx => {
const response = await db.put(screen) const response = await db.put(screen)
if (eventFn) { if (eventFn) {
eventFn(screen) await eventFn(screen)
} }
ctx.message = `Screen ${screen.name} saved.` ctx.message = `Screen ${screen.name} saved.`
ctx.body = { ctx.body = {
@ -50,7 +50,7 @@ exports.destroy = async ctx => {
await db.remove(id, ctx.params.screenRev) await db.remove(id, ctx.params.screenRev)
events.screen.deleted(screen) await events.screen.deleted(screen)
ctx.body = { ctx.body = {
message: "Screen deleted successfully", message: "Screen deleted successfully",
} }

View File

@ -43,7 +43,7 @@ async function prepareUpload({ s3Key, bucket, metadata, file }) {
exports.serveBuilder = async function (ctx) { exports.serveBuilder = async function (ctx) {
let builderPath = resolve(TOP_LEVEL_PATH, "builder") let builderPath = resolve(TOP_LEVEL_PATH, "builder")
await send(ctx, ctx.file, { root: builderPath }) await send(ctx, ctx.file, { root: builderPath })
events.serve.servedBuilder(version) await events.serve.servedBuilder(version)
} }
exports.uploadFile = async function (ctx) { exports.uploadFile = async function (ctx) {
@ -94,9 +94,9 @@ exports.serveApp = async function (ctx) {
} }
if (isDevAppID(appInfo.appId)) { if (isDevAppID(appInfo.appId)) {
events.serve.servedAppPreview(appInfo) await events.serve.servedAppPreview(appInfo)
} else { } else {
events.serve.servedApp(appInfo) await events.serve.servedApp(appInfo)
} }
} }

View File

@ -60,12 +60,12 @@ exports.save = async function (ctx) {
table.dataImport && table.dataImport.csvString ? "csv" : undefined table.dataImport && table.dataImport.csvString ? "csv" : undefined
const savedTable = await pickApi({ table }).save(ctx) const savedTable = await pickApi({ table }).save(ctx)
if (!table._id) { if (!table._id) {
events.table.created(savedTable) await events.table.created(savedTable)
} else { } else {
events.table.updated(savedTable) await events.table.updated(savedTable)
} }
if (importFormat) { if (importFormat) {
events.table.imported(savedTable, importFormat) await events.table.imported(savedTable, importFormat)
} }
ctx.status = 200 ctx.status = 200
ctx.message = `Table ${table.name} saved successfully.` ctx.message = `Table ${table.name} saved successfully.`
@ -78,7 +78,7 @@ exports.destroy = async function (ctx) {
const appId = ctx.appId const appId = ctx.appId
const tableId = ctx.params.tableId const tableId = ctx.params.tableId
const deletedTable = await pickApi({ tableId }).destroy(ctx) const deletedTable = await pickApi({ tableId }).destroy(ctx)
events.table.deleted(deletedTable) await events.table.deleted(deletedTable)
ctx.eventEmitter && ctx.eventEmitter &&
ctx.eventEmitter.emitTable(`table:delete`, appId, deletedTable) ctx.eventEmitter.emitTable(`table:delete`, appId, deletedTable)
ctx.status = 200 ctx.status = 200

View File

@ -149,7 +149,7 @@ export async function handleDataImport(user: any, table: any, dataImport: any) {
} }
await quotas.addRows(finalData.length, () => db.bulkDocs(finalData)) await quotas.addRows(finalData.length, () => db.bulkDocs(finalData))
events.rows.imported(table, "csv", finalData.length) await events.rows.imported(table, "csv", finalData.length)
return table return table
} }

View File

@ -39,7 +39,7 @@ exports.save = async ctx => {
existingTable.views[viewName] = existingTable.views[originalName] existingTable.views[viewName] = existingTable.views[originalName]
} }
await db.put(table) await db.put(table)
handleViewEvents(existingTable.views[viewName], table.views[viewName]) await handleViewEvents(existingTable.views[viewName], table.views[viewName])
ctx.body = { ctx.body = {
...table.views[viewToSave.name], ...table.views[viewToSave.name],
@ -47,16 +47,16 @@ exports.save = async ctx => {
} }
} }
const calculationEvents = (existingView, newView) => { const calculationEvents = async (existingView, newView) => {
const existingCalculation = existingView && existingView.calculation const existingCalculation = existingView && existingView.calculation
const newCalculation = newView && newView.calculation const newCalculation = newView && newView.calculation
if (existingCalculation && !newCalculation) { if (existingCalculation && !newCalculation) {
events.view.calculationDeleted() await events.view.calculationDeleted()
} }
if (!existingCalculation && newCalculation) { if (!existingCalculation && newCalculation) {
events.view.calculationCreated() await events.view.calculationCreated()
} }
if ( if (
@ -64,11 +64,11 @@ const calculationEvents = (existingView, newView) => {
newCalculation && newCalculation &&
existingCalculation !== newCalculation existingCalculation !== newCalculation
) { ) {
events.view.calculationUpdated() await events.view.calculationUpdated()
} }
} }
const filterEvents = (existingView, newView) => { const filterEvents = async (existingView, newView) => {
const hasExistingFilters = !!( const hasExistingFilters = !!(
existingView && existingView &&
existingView.filters && existingView.filters &&
@ -77,11 +77,11 @@ const filterEvents = (existingView, newView) => {
const hasNewFilters = !!(newView && newView.filters && newView.filters.length) const hasNewFilters = !!(newView && newView.filters && newView.filters.length)
if (hasExistingFilters && !hasNewFilters) { if (hasExistingFilters && !hasNewFilters) {
events.view.filterDeleted() await events.view.filterDeleted()
} }
if (!hasExistingFilters && hasNewFilters) { if (!hasExistingFilters && hasNewFilters) {
events.view.filterCreated() await events.view.filterCreated()
} }
if ( if (
@ -89,18 +89,18 @@ const filterEvents = (existingView, newView) => {
hasNewFilters && hasNewFilters &&
!isEqual(existingView.filters, newView.filters) !isEqual(existingView.filters, newView.filters)
) { ) {
events.view.filterUpdated() await events.view.filterUpdated()
} }
} }
const handleViewEvents = (existingView, newView) => { const handleViewEvents = async (existingView, newView) => {
if (!existingView) { if (!existingView) {
events.view.created() await events.view.created()
} else { } else {
events.view.updated() await events.view.updated()
} }
calculationEvents(existingView, newView) await calculationEvents(existingView, newView)
filterEvents(existingView, newView) await filterEvents(existingView, newView)
} }
exports.destroy = async ctx => { exports.destroy = async ctx => {
@ -110,7 +110,7 @@ exports.destroy = async ctx => {
const table = await db.get(view.meta.tableId) const table = await db.get(view.meta.tableId)
delete table.views[viewName] delete table.views[viewName]
await db.put(table) await db.put(table)
events.view.deleted() await events.view.deleted()
ctx.body = view ctx.body = view
} }
@ -182,8 +182,8 @@ exports.exportView = async ctx => {
ctx.body = apiFileReturn(exporter(headers, rows)) ctx.body = apiFileReturn(exporter(headers, rows))
if (viewName.startsWith(DocumentTypes.TABLE)) { if (viewName.startsWith(DocumentTypes.TABLE)) {
events.table.exported(table, format) await events.table.exported(table, format)
} else { } else {
events.view.exported(table, format) await events.view.exported(table, format)
} }
} }

View File

@ -5,10 +5,10 @@ export const backfill = async (appDb: any) => {
const app: App = await appDb.get(db.DocumentTypes.APP_METADATA) const app: App = await appDb.get(db.DocumentTypes.APP_METADATA)
if (db.isDevAppID(app.appId)) { if (db.isDevAppID(app.appId)) {
events.app.created(app) await events.app.created(app)
} }
if (db.isProdAppID(app.appId)) { if (db.isProdAppID(app.appId)) {
events.app.published(app) await events.app.published(app)
} }
} }

View File

@ -16,10 +16,10 @@ export const backfill = async (appDb: any) => {
const automations = await getAutomations(appDb) const automations = await getAutomations(appDb)
for (const automation of automations) { for (const automation of automations) {
events.automation.created(automation) await events.automation.created(automation)
for (const step of automation.definition.steps) { for (const step of automation.definition.steps) {
events.automation.stepCreated(automation, step) await events.automation.stepCreated(automation, step)
} }
} }
} }

View File

@ -16,7 +16,7 @@ export const backfill = async (appDb: any) => {
const datasources: Datasource[] = await getDatasources(appDb) const datasources: Datasource[] = await getDatasources(appDb)
for (const datasource of datasources) { for (const datasource of datasources) {
events.datasource.created(datasource) await events.datasource.created(datasource)
} }
} }
} }

View File

@ -16,7 +16,7 @@ export const backfill = async (appDb: any) => {
const layouts: Layout[] = await getLayouts(appDb) const layouts: Layout[] = await getLayouts(appDb)
for (const layout of layouts) { for (const layout of layouts) {
events.layout.created(layout) await events.layout.created(layout)
} }
} }
} }

View File

@ -27,7 +27,7 @@ export const backfill = async (appDb: any) => {
appDb, appDb,
query.datasourceId query.datasourceId
) )
events.query.created(datasource, query) await events.query.created(datasource, query)
} }
} }
} }

View File

@ -16,7 +16,7 @@ export const backfill = async (appDb: any) => {
const roles = await getRoles(appDb) const roles = await getRoles(appDb)
for (const role of roles) { for (const role of roles) {
events.role.created(role) await events.role.created(role)
} }
} }
} }

View File

@ -16,7 +16,7 @@ export const backfill = async (appDb: any) => {
const screens = await getScreens(appDb) const screens = await getScreens(appDb)
for (const screen of screens) { for (const screen of screens) {
events.screen.created(screen) await events.screen.created(screen)
} }
} }
} }

View File

@ -16,18 +16,18 @@ export const backfill = async (appDb: any) => {
const tables = await getTables(appDb) const tables = await getTables(appDb)
for (const table of tables) { for (const table of tables) {
events.table.created(table) await events.table.created(table)
if (table.views) { if (table.views) {
for (const view of Object.values(table.views)) { for (const view of Object.values(table.views)) {
events.view.created(view) await events.view.created(view)
if (view.calculation) { if (view.calculation) {
events.view.calculationCreated(view.calculation) await events.view.calculationCreated(view.calculation)
} }
if (view.filters?.length) { if (view.filters?.length) {
events.view.filterCreated() await events.view.filterCreated()
} }
} }
} }

View File

@ -25,16 +25,16 @@ export const backfill = async (globalDb: any) => {
for (const config of configs) { for (const config of configs) {
if (isSMTPConfig(config)) { if (isSMTPConfig(config)) {
events.email.SMTPCreated(config) await events.email.SMTPCreated(config)
} }
if (isGoogleConfig(config)) { if (isGoogleConfig(config)) {
events.auth.SSOCreated("google") await events.auth.SSOCreated("google")
if (config.config.activated) { if (config.config.activated) {
events.auth.SSOActivated("google") await events.auth.SSOActivated("google")
} }
} }
if (isOIDCConfig(config)) { if (isOIDCConfig(config)) {
events.auth.SSOCreated("oidc") await events.auth.SSOCreated("oidc")
if (config.config.configs[0].activated) { if (config.config.configs[0].activated) {
events.auth.SSOActivated("oidc") events.auth.SSOActivated("oidc")
} }
@ -42,12 +42,12 @@ export const backfill = async (globalDb: any) => {
if (isSettingsConfig(config)) { if (isSettingsConfig(config)) {
const company = config.config.company const company = config.config.company
if (company && company !== "Budibase") { if (company && company !== "Budibase") {
events.org.nameUpdated() await events.org.nameUpdated()
} }
const logoUrl = config.config.logoUrl const logoUrl = config.config.logoUrl
if (logoUrl) { if (logoUrl) {
events.org.logoUpdated() await events.org.logoUpdated()
} }
const platformUrl = config.config.platformUrl const platformUrl = config.config.platformUrl
@ -56,7 +56,7 @@ export const backfill = async (globalDb: any) => {
platformUrl !== "http://localhost:10000" && platformUrl !== "http://localhost:10000" &&
env.SELF_HOSTED env.SELF_HOSTED
) { ) {
events.org.platformURLUpdated() await events.org.platformURLUpdated()
} }
} }
} }

View File

@ -11,6 +11,6 @@ export const backfill = async () => {
const rows: Row[] = await getUniqueRows(appIds) const rows: Row[] = await getUniqueRows(appIds)
const rowCount = rows ? rows.length : 0 const rowCount = rows ? rows.length : 0
if (rowCount) { if (rowCount) {
events.rows.created(rowCount) await events.rows.created(rowCount)
} }
} }

View File

@ -19,19 +19,19 @@ export const backfill = async (globalDb: any) => {
const users = await getUsers(globalDb) const users = await getUsers(globalDb)
for (const user of users) { for (const user of users) {
events.user.created(user) await events.user.created(user)
if (user.admin?.global) { if (user.admin?.global) {
events.user.permissionAdminAssigned(user) await events.user.permissionAdminAssigned(user)
} }
if (user.builder?.global) { if (user.builder?.global) {
events.user.permissionBuilderAssigned(user) await events.user.permissionBuilderAssigned(user)
} }
if (user.roles) { if (user.roles) {
for (const [appId, role] of Object.entries(user.roles)) { for (const [appId, role] of Object.entries(user.roles)) {
events.role.assigned(user, role) await events.role.assigned(user, role)
} }
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/types", "name": "@budibase/types",
"version": "1.0.126-alpha.0", "version": "1.0.167-alpha.8",
"description": "Budibase types", "description": "Budibase types",
"main": "src/index.ts", "main": "src/index.ts",
"types": "src/index.ts", "types": "src/index.ts",

View File

@ -1 +1,2 @@
export * from "./hosting" export * from "./hosting"
export * from "./sessions"

View File

@ -0,0 +1,36 @@
import { User, Account } from "../documents"
import { Hosting } from "./hosting"
/**
* Account portal user session. Used for self hosted accounts only.
*/
export interface AccountUserSession {
_id: string
email: string
tenantId: string
accountPortalAccess: boolean
account: Account
}
/**
* Budibase user session.
*/
export interface BudibaseUserSession extends User {
_id: string // overwrite potentially undefined
account?: Account
accountPortalAccess?: boolean
}
export const isAccountSession = (
user: AccountUserSession | BudibaseUserSession
): user is AccountUserSession => {
return user.account?.hosting === Hosting.SELF
}
export const isUserSession = (
user: AccountUserSession | BudibaseUserSession
): user is BudibaseUserSession => {
return !user.account || user.account?.hosting === Hosting.CLOUD
}
export type SessionUser = AccountUserSession | BudibaseUserSession

View File

@ -1,7 +1,82 @@
import { Hosting } from "../../core" import { Hosting } from "../../core"
export interface Account { export interface CreateAccount {
accountId: string email: string
tenantId: string
hosting: Hosting hosting: Hosting
verified: boolean authType: AuthType
// optional fields - for sso based sign ups
registrationStep?: string
// profile
tenantName?: string
name?: string
size?: string
profession?: string
}
export interface CreatePassswordAccount extends CreateAccount {
password: string
}
export const isCreatePasswordAccount = (
account: CreateAccount
): account is CreatePassswordAccount => account.authType === AuthType.PASSWORD
export interface UpdateAccount {
stripeCustomerId?: string
licenseKey?: string
}
export interface Account extends CreateAccount {
// generated
accountId: string
createdAt: number
// registration
verified: boolean
verificationSent: boolean
// licensing
tier: string // deprecated
stripeCustomerId?: string
licenseKey?: string
}
export interface PasswordAccount extends Account {
password: string
}
export const isPasswordAccount = (
account: Account
): account is PasswordAccount =>
account.authType === AuthType.PASSWORD && account.hosting === Hosting.SELF
export interface CloudAccount extends Account {
password?: string
budibaseUserId: string
}
export const isCloudAccount = (account: Account): account is CloudAccount =>
account.hosting === Hosting.CLOUD
export const isSelfHostAccount = (account: Account) =>
account.hosting === Hosting.SELF
export const isSSOAccount = (account: Account): account is SSOAccount =>
account.authType === AuthType.SSO
export interface SSOAccount extends Account {
pictureUrl?: string
provider?: string
providerType?: string
oauth2?: OAuthTokens
thirdPartyProfile: any // TODO: define what the google profile looks like
}
export enum AuthType {
SSO = "sso",
PASSWORD = "password",
}
export interface OAuthTokens {
accessToken: string
refreshToken: string
} }

View File

@ -8,6 +8,8 @@ export interface User extends Document {
admin?: { admin?: {
global: boolean global: boolean
} }
providerType?: string
tenantId: string
} }
export interface UserRoles { export interface UserRoles {

View File

@ -42,6 +42,7 @@ export enum Event {
// ORG / ANALYTICS // ORG / ANALYTICS
ANALYTICS_OPT_OUT = "analytics:opt:out", ANALYTICS_OPT_OUT = "analytics:opt:out",
ANALYTICS_OPT_IN = "analytics:opt:in",
// APP // APP
APP_CREATED = "app:created", APP_CREATED = "app:created",
@ -88,7 +89,6 @@ export enum Event {
TABLE_EXPORTED = "table:exported", TABLE_EXPORTED = "table:exported",
TABLE_IMPORTED = "table:imported", TABLE_IMPORTED = "table:imported",
TABLE_DATA_IMPORTED = "table:data:imported", TABLE_DATA_IMPORTED = "table:data:imported",
TABLE_PERMISSION_UPDATED = "table:permission:updated",
// VIEW // VIEW
VIEW_CREATED = "view:created", VIEW_CREATED = "view:created",
@ -140,12 +140,6 @@ export enum Event {
ACCOUNT_VERIFIED = "account:verified", ACCOUNT_VERIFIED = "account:verified",
} }
export enum IdentityType {
TENANT = "tenant",
USER = "user",
ACCOUNT = "account",
}
export type RowImportFormat = "csv" export type RowImportFormat = "csv"
export type TableExportFormat = "json" | "csv" export type TableExportFormat = "json" | "csv"
export type TableImportFormat = "csv" export type TableImportFormat = "csv"

View File

@ -0,0 +1,29 @@
import { Hosting } from "../core"
export enum IdentityType {
USER = "user", // cloud and self hosted users
ACCOUNT = "account", // self hosted accounts
TENANT = "tenant", // cloud and self hosted tenants
}
export interface Identity {
id: string
tenantId: string
}
export interface UserIdentity extends Identity {
hosting: Hosting
type: IdentityType
authType: string
}
export interface BudibaseIdentity extends UserIdentity {
builder?: boolean
admin?: boolean
}
export interface AccountIdentity extends UserIdentity {
verified: boolean
profession: string | undefined
companySize: string | undefined
}

View File

@ -15,3 +15,4 @@ export * from "./serve"
export * from "./table" export * from "./table"
export * from "./user" export * from "./user"
export * from "./view" export * from "./view"
export * from "./identification"

View File

@ -64,7 +64,7 @@
"server-destroy": "^1.0.1" "server-destroy": "^1.0.1"
}, },
"devDependencies": { "devDependencies": {
"@budibase/types": "^1.0.126-alpha.0", "@budibase/types": "^1.0.167-alpha.8",
"@types/jest": "^26.0.23", "@types/jest": "^26.0.23",
"@types/koa": "^2.13.3", "@types/koa": "^2.13.3",
"@types/koa-router": "^7.4.2", "@types/koa-router": "^7.4.2",

View File

@ -71,7 +71,7 @@ export const authenticate = async (ctx: any, next: any) => {
"local", "local",
async (err: any, user: any, info: any) => { async (err: any, user: any, info: any) => {
await authInternal(ctx, user, err, info) await authInternal(ctx, user, err, info)
events.auth.login("local") await events.auth.login("local")
ctx.status = 200 ctx.status = 200
} }
)(ctx, next) )(ctx, next)
@ -112,7 +112,7 @@ export const reset = async (ctx: any) => {
user, user,
subject: "{{ company }} platform password reset", subject: "{{ company }} platform password reset",
}) })
events.user.passwordResetRequested(user) await events.user.passwordResetRequested(user)
} }
} catch (err) { } catch (err) {
console.log(err) console.log(err)
@ -139,7 +139,7 @@ export const resetUpdate = async (ctx: any) => {
} }
// remove password from the user before sending events // remove password from the user before sending events
delete user.password delete user.password
events.user.passwordReset(user) await events.user.passwordReset(user)
} catch (err) { } catch (err) {
ctx.throw(400, "Cannot reset password.") ctx.throw(400, "Cannot reset password.")
} }
@ -212,7 +212,7 @@ export const googleAuth = async (ctx: any, next: any) => {
{ successRedirect: "/", failureRedirect: "/error" }, { successRedirect: "/", failureRedirect: "/error" },
async (err: any, user: any, info: any) => { async (err: any, user: any, info: any) => {
await authInternal(ctx, user, err, info) await authInternal(ctx, user, err, info)
events.auth.login("google") await events.auth.login("google")
ctx.redirect("/") ctx.redirect("/")
} }
)(ctx, next) )(ctx, next)
@ -256,7 +256,7 @@ export const oidcAuth = async (ctx: any, next: any) => {
{ successRedirect: "/", failureRedirect: "/error" }, { successRedirect: "/", failureRedirect: "/error" },
async (err: any, user: any, info: any) => { async (err: any, user: any, info: any) => {
await authInternal(ctx, user, err, info) await authInternal(ctx, user, err, info)
events.auth.login("oidc") await events.auth.login("oidc")
ctx.redirect("/") ctx.redirect("/")
} }
)(ctx, next) )(ctx, next)

View File

@ -162,7 +162,7 @@ exports.save = async function (ctx) {
try { try {
const response = await db.put(ctx.request.body) const response = await db.put(ctx.request.body)
for (const fn of eventFns) { for (const fn of eventFns) {
fn() await fn()
} }
ctx.body = { ctx.body = {
type, type,

View File

@ -145,8 +145,8 @@ exports.updateSelf = async ctx => {
// remove the old password from the user before sending events // remove the old password from the user before sending events
delete user.password delete user.password
events.user.updated(user) await events.user.updated(user)
if (passwordChange) { if (passwordChange) {
events.user.passwordUpdated(user) await events.user.passwordUpdated(user)
} }
} }

View File

@ -123,7 +123,7 @@ export const invite = async (ctx: any) => {
ctx.body = { ctx.body = {
message: "Invitation has been sent.", message: "Invitation has been sent.",
} }
events.user.invited(userInfo) await events.user.invited(userInfo)
} }
export const inviteAccept = async (ctx: any) => { export const inviteAccept = async (ctx: any) => {
@ -139,7 +139,7 @@ export const inviteAccept = async (ctx: any) => {
email, email,
...info, ...info,
}) })
events.user.inviteAccepted(user) await events.user.inviteAccepted(user)
return user return user
}) })
} catch (err: any) { } catch (err: any) {

View File

@ -1,19 +1,19 @@
import { events } from "@budibase/backend-core" import { events } from "@budibase/backend-core"
import { User, UserRoles } from "@budibase/types" import { User, UserRoles } from "@budibase/types"
export const handleDeleteEvents = (user: any) => { export const handleDeleteEvents = async (user: any) => {
events.user.deleted(user) await events.user.deleted(user)
if (isBuilder(user)) { if (isBuilder(user)) {
events.user.permissionBuilderRemoved(user) await events.user.permissionBuilderRemoved(user)
} }
if (isAdmin(user)) { if (isAdmin(user)) {
events.user.permissionAdminRemoved(user) await events.user.permissionAdminRemoved(user)
} }
} }
const assignAppRoleEvents = ( const assignAppRoleEvents = async (
user: User, user: User,
roles: UserRoles, roles: UserRoles,
existingRoles: UserRoles existingRoles: UserRoles
@ -21,12 +21,12 @@ const assignAppRoleEvents = (
for (const [appId, role] of Object.entries(roles)) { for (const [appId, role] of Object.entries(roles)) {
// app role in existing is not same as new // app role in existing is not same as new
if (!existingRoles || existingRoles[appId] !== role) { if (!existingRoles || existingRoles[appId] !== role) {
events.role.assigned(user, role) await events.role.assigned(user, role)
} }
} }
} }
const unassignAppRoleEvents = ( const unassignAppRoleEvents = async (
user: User, user: User,
roles: UserRoles, roles: UserRoles,
existingRoles: UserRoles existingRoles: UserRoles
@ -37,29 +37,29 @@ const unassignAppRoleEvents = (
for (const [appId, role] of Object.entries(existingRoles)) { for (const [appId, role] of Object.entries(existingRoles)) {
// app role in new is not same as existing // app role in new is not same as existing
if (!roles || roles[appId] !== role) { if (!roles || roles[appId] !== role) {
events.role.unassigned(user, role) await events.role.unassigned(user, role)
} }
} }
} }
const handleAppRoleEvents = (user: any, existingUser: any) => { const handleAppRoleEvents = async (user: any, existingUser: any) => {
const roles = user.roles const roles = user.roles
const existingRoles = existingUser?.roles const existingRoles = existingUser?.roles
assignAppRoleEvents(user, roles, existingRoles) await assignAppRoleEvents(user, roles, existingRoles)
unassignAppRoleEvents(user, roles, existingRoles) await unassignAppRoleEvents(user, roles, existingRoles)
} }
export const handleSaveEvents = (user: any, existingUser: any) => { export const handleSaveEvents = async (user: any, existingUser: any) => {
if (existingUser) { if (existingUser) {
events.user.updated(user) await events.user.updated(user)
if (isRemovingBuilder(user, existingUser)) { if (isRemovingBuilder(user, existingUser)) {
events.user.permissionBuilderRemoved(user) await events.user.permissionBuilderRemoved(user)
} }
if (isRemovingAdmin(user, existingUser)) { if (isRemovingAdmin(user, existingUser)) {
events.user.permissionAdminRemoved(user) await events.user.permissionAdminRemoved(user)
} }
if ( if (
@ -67,21 +67,21 @@ export const handleSaveEvents = (user: any, existingUser: any) => {
user.forceResetPassword && user.forceResetPassword &&
user.password user.password
) { ) {
events.user.passwordForceReset(user) await events.user.passwordForceReset(user)
} }
} else { } else {
events.user.created(user) await events.user.created(user)
} }
if (isAddingBuilder(user, existingUser)) { if (isAddingBuilder(user, existingUser)) {
events.user.permissionBuilderAssigned(user) await events.user.permissionBuilderAssigned(user)
} }
if (isAddingAdmin(user, existingUser)) { if (isAddingAdmin(user, existingUser)) {
events.user.permissionAdminAssigned(user) await events.user.permissionAdminAssigned(user)
} }
handleAppRoleEvents(user, existingUser) await handleAppRoleEvents(user, existingUser)
} }
const isBuilder = (user: any) => user.builder && user.builder.global const isBuilder = (user: any) => user.builder && user.builder.global

View File

@ -129,7 +129,7 @@ export const save = async (
} }
user._rev = response.rev user._rev = response.rev
eventHelpers.handleSaveEvents(user, dbUser) await eventHelpers.handleSaveEvents(user, dbUser)
await tenancy.tryAddTenant(tenantId, _id, email) await tenancy.tryAddTenant(tenantId, _id, email)
await cache.user.invalidateUser(response.id) await cache.user.invalidateUser(response.id)
@ -169,7 +169,7 @@ export const destroy = async (id: string, currentUser: any) => {
await deprovisioning.removeUserFromInfoDB(dbUser) await deprovisioning.removeUserFromInfoDB(dbUser)
await db.remove(dbUser._id, dbUser._rev) await db.remove(dbUser._id, dbUser._rev)
eventHelpers.handleDeleteEvents(dbUser) await eventHelpers.handleDeleteEvents(dbUser)
await quotas.removeUser(dbUser) await quotas.removeUser(dbUser)
await cache.user.invalidateUser(dbUser._id) await cache.user.invalidateUser(dbUser._id)
await sessions.invalidateSessions(dbUser._id) await sessions.invalidateSessions(dbUser._id)