diff --git a/packages/backend-core/src/cache/invite.ts b/packages/backend-core/src/cache/invite.ts index e3d698bcc6..5d896ba04c 100644 --- a/packages/backend-core/src/cache/invite.ts +++ b/packages/backend-core/src/cache/invite.ts @@ -3,18 +3,10 @@ import { Duration } from "../utils" import env from "../environment" import { getTenantId } from "../context" import * as redis from "../redis/init" +import { Invite, InviteWithCode } from "@budibase/types" const TTL_SECONDS = Duration.fromDays(7).toSeconds() -interface Invite { - email: string - info: any -} - -interface InviteWithCode extends Invite { - code: string -} - /** * Given an invite code and invite body, allow the update an existing/valid invite in redis * @param code The invite code for an invite in redis diff --git a/packages/server/src/api/controllers/templates.ts b/packages/server/src/api/controllers/templates.ts index 8008b63fd3..fd3e28aa55 100644 --- a/packages/server/src/api/controllers/templates.ts +++ b/packages/server/src/api/controllers/templates.ts @@ -3,7 +3,7 @@ import { downloadTemplate as dlTemplate } from "../../utilities/fileSystem" import env from "../../environment" import { DownloadTemplateResponse, - FetchTemplateResponse, + FetchGlobalTemplateResponse, UserCtx, } from "@budibase/types" @@ -11,7 +11,7 @@ import { const DEFAULT_TEMPLATES_BUCKET = "prod-budi-templates.s3-eu-west-1.amazonaws.com" -export async function fetch(ctx: UserCtx) { +export async function fetch(ctx: UserCtx) { let type = env.TEMPLATE_REPOSITORY let response, error = false diff --git a/packages/types/src/api/web/auth.ts b/packages/types/src/api/web/auth.ts index d631762b30..0964d71576 100644 --- a/packages/types/src/api/web/auth.ts +++ b/packages/types/src/api/web/auth.ts @@ -3,14 +3,28 @@ export interface LoginRequest { password: string } +export interface LogoutResponse { + message: string +} + +export interface SetInitInfoRequest extends Record {} + +export interface GetInitInfoResponse extends Record {} + export interface PasswordResetRequest { email: string } +export interface PasswordResetResponse { + message: string +} export interface PasswordResetUpdateRequest { resetCode: string password: string } +export interface PasswordResetUpdateResponse { + message: string +} export interface UpdateSelfRequest { firstName?: string diff --git a/packages/types/src/api/web/global/configs.ts b/packages/types/src/api/web/global/configs.ts index b36d974904..e16ed3c27f 100644 --- a/packages/types/src/api/web/global/configs.ts +++ b/packages/types/src/api/web/global/configs.ts @@ -1,4 +1,10 @@ -import { SettingsConfig, SettingsInnerConfig } from "../../../documents" +import { + Config, + ConfigType, + SettingsBrandingConfig, + SettingsConfig, + SettingsInnerConfig, +} from "../../../documents" /** * Settings that aren't stored in the database - enriched at runtime. @@ -22,3 +28,34 @@ export interface PublicOIDCConfig { } export type GetPublicOIDCConfigResponse = PublicOIDCConfig[] + +export interface SaveConfigRequest extends Config {} +export interface SaveConfigResponse { + type: ConfigType + _id: string + _rev: string +} + +export interface DeleteConfigResponse { + message: string +} + +interface ChecklistItem { + checked: boolean + label: string + link: string +} +export interface ConfigChecklistResponse { + apps: ChecklistItem + smtp: ChecklistItem + adminUser: ChecklistItem + sso: ChecklistItem + branding: SettingsBrandingConfig +} + +export type FindConfigResponse = Config | {} + +export interface UploadConfigFileResponse { + message: string + url: string +} diff --git a/packages/types/src/api/web/global/email.ts b/packages/types/src/api/web/global/email.ts new file mode 100644 index 0000000000..a0ca0e8485 --- /dev/null +++ b/packages/types/src/api/web/global/email.ts @@ -0,0 +1,28 @@ +import { EmailAttachment, EmailInvite } from "../../../documents" + +export enum EmailTemplatePurpose { + CORE = "core", + BASE = "base", + PASSWORD_RECOVERY = "password_recovery", + INVITATION = "invitation", + WELCOME = "welcome", + CUSTOM = "custom", +} + +export interface SendEmailRequest { + workspaceId?: string + email: string + userId: string + purpose: EmailTemplatePurpose + contents?: string + from?: string + subject: string + cc?: boolean + bcc?: boolean + automation?: boolean + invite?: EmailInvite + attachments?: EmailAttachment[] +} +export interface SendEmailResponse extends Record { + message: string +} diff --git a/packages/types/src/api/web/global/index.ts b/packages/types/src/api/web/global/index.ts index c43f2928d8..36dce60b6c 100644 --- a/packages/types/src/api/web/global/index.ts +++ b/packages/types/src/api/web/global/index.ts @@ -5,3 +5,7 @@ export * from "./configs" export * from "./scim" export * from "./license" export * from "./oldMigration" +export * from "./email" +export * from "./role" +export * from "./self" +export * from "./template" diff --git a/packages/types/src/api/web/global/license.ts b/packages/types/src/api/web/global/license.ts index 21d8876412..344ab829c2 100644 --- a/packages/types/src/api/web/global/license.ts +++ b/packages/types/src/api/web/global/license.ts @@ -1,5 +1,7 @@ // LICENSE KEY +import { QuotaUsage } from "../../../documents" + export interface ActivateLicenseKeyRequest { licenseKey: string } @@ -23,3 +25,5 @@ export interface GetOfflineLicenseTokenResponse { export interface GetOfflineIdentifierResponse { identifierBase64: string } + +export interface GetQuotaUsageResponse extends QuotaUsage {} diff --git a/packages/types/src/api/web/global/role.ts b/packages/types/src/api/web/global/role.ts new file mode 100644 index 0000000000..1d45995fd2 --- /dev/null +++ b/packages/types/src/api/web/global/role.ts @@ -0,0 +1,17 @@ +import { Role } from "../../../documents" + +interface GlobalRoleResponse { + roles: Role[] + name: string + version: string + url?: string +} + +export interface FetchGlobalRolesResponse + extends Record {} + +export interface FindGlobalRoleResponse extends GlobalRoleResponse {} + +export interface RemoveAppRoleResponse { + message: string +} diff --git a/packages/types/src/api/web/global/self.ts b/packages/types/src/api/web/global/self.ts new file mode 100644 index 0000000000..4ba51d2cd5 --- /dev/null +++ b/packages/types/src/api/web/global/self.ts @@ -0,0 +1,12 @@ +import { DevInfo, User } from "../../../documents" + +export interface GenerateAPIKeyRequest { + userId: string +} +export interface GenerateAPIKeyResponse extends DevInfo {} + +export interface FetchAPIKeyResponse extends DevInfo {} + +export interface GetGlobalSelfResponse extends User { + flags?: Record +} diff --git a/packages/types/src/api/web/global/template.ts b/packages/types/src/api/web/global/template.ts new file mode 100644 index 0000000000..069e6d3d1b --- /dev/null +++ b/packages/types/src/api/web/global/template.ts @@ -0,0 +1,30 @@ +import { Template } from "../../../documents" + +export interface GlobalTemplateDefinition { + name: string + description: string + category: string +} + +export interface GlobalTemplateBinding { + name: string + description: string +} + +export interface FetchGlobalTemplateDefinitionResponse { + info: Record + bindings: Record +} + +export interface SaveGlobalTemplateRequest extends Template {} +export interface SaveGlobalTemplateResponse extends Template {} + +export type FetchGlobalTemplateResponse = Template[] +export type FetchGlobalTemplateByTypeResponse = Template[] +export type FetchGlobalTemplateByOwnerIDResponse = Template[] + +export interface FindGlobalTemplateResponse extends Template {} + +export interface DeleteGlobalTemplateResponse { + message: string +} diff --git a/packages/types/src/api/web/system/account.ts b/packages/types/src/api/web/system/account.ts new file mode 100644 index 0000000000..d854a632cb --- /dev/null +++ b/packages/types/src/api/web/system/account.ts @@ -0,0 +1,4 @@ +import { Account, AccountMetadata } from "../../../documents" + +export interface SaveAccountRequest extends Account {} +export interface SaveAccountResponse extends AccountMetadata {} diff --git a/packages/types/src/api/web/system/environment.ts b/packages/types/src/api/web/system/environment.ts index e27a1338e2..3b6ee9d983 100644 --- a/packages/types/src/api/web/system/environment.ts +++ b/packages/types/src/api/web/system/environment.ts @@ -1,8 +1,11 @@ export interface GetEnvironmentResponse { multiTenancy: boolean + offlineMode: boolean cloud: boolean - accountPortalUrl: string - baseUrl: string + accountPortalUrl?: string disableAccountPortal: boolean + baseUrl?: string isDev: boolean + maintenance: { type: string }[] + passwordMinLength?: string } diff --git a/packages/types/src/api/web/system/index.ts b/packages/types/src/api/web/system/index.ts index 0ce2738ddb..9b03ddd438 100644 --- a/packages/types/src/api/web/system/index.ts +++ b/packages/types/src/api/web/system/index.ts @@ -1,3 +1,8 @@ export * from "./environment" export * from "./status" export * from "./ops" +export * from "./account" +export * from "./log" +export * from "./migration" +export * from "./restore" +export * from "./tenant" diff --git a/packages/types/src/api/web/system/log.ts b/packages/types/src/api/web/system/log.ts new file mode 100644 index 0000000000..8a8cb6e53d --- /dev/null +++ b/packages/types/src/api/web/system/log.ts @@ -0,0 +1 @@ +export type GetLogResponse = Buffer diff --git a/packages/types/src/api/web/system/migration.ts b/packages/types/src/api/web/system/migration.ts new file mode 100644 index 0000000000..f9f6eac48a --- /dev/null +++ b/packages/types/src/api/web/system/migration.ts @@ -0,0 +1,5 @@ +import { MigrationDefinition, MigrationOptions } from "../../../sdk" + +export interface RunGlobalMigrationRequest extends MigrationOptions {} + +export type FetchMigrationDefinitionsResponse = MigrationDefinition[] diff --git a/packages/types/src/api/web/system/restore.ts b/packages/types/src/api/web/system/restore.ts new file mode 100644 index 0000000000..b5e8850051 --- /dev/null +++ b/packages/types/src/api/web/system/restore.ts @@ -0,0 +1,3 @@ +export interface SystemRestoreResponse { + message: string +} diff --git a/packages/types/src/api/web/system/tenant.ts b/packages/types/src/api/web/system/tenant.ts new file mode 100644 index 0000000000..00ed883499 --- /dev/null +++ b/packages/types/src/api/web/system/tenant.ts @@ -0,0 +1,3 @@ +export interface GetTenantInfoResponse { + exists: boolean +} diff --git a/packages/types/src/api/web/user.ts b/packages/types/src/api/web/user.ts index e102f136c3..2f83f012b3 100644 --- a/packages/types/src/api/web/user.ts +++ b/packages/types/src/api/web/user.ts @@ -1,6 +1,15 @@ -import { User } from "../../documents" +import { AccountMetadata, PlatformUser, User } from "../../documents" import { SearchFilters } from "../../sdk" +export interface Invite { + email: string + info: any +} + +export interface InviteWithCode extends Invite { + code: string +} + export interface SaveUserResponse { _id: string _rev: string @@ -47,6 +56,11 @@ export interface InviteUserRequest { email: string userInfo: any } +export interface InviteUserResponse { + message: string + successful: { email: string }[] + unsuccessful: { email: string; reason: string }[] +} export interface DeleteInviteUserRequest { code: string @@ -54,6 +68,9 @@ export interface DeleteInviteUserRequest { export type InviteUsersRequest = InviteUserRequest[] export type DeleteInviteUsersRequest = DeleteInviteUserRequest[] +export interface DeleteInviteUsersResponse { + message: string +} export interface InviteUsersResponse { successful: { email: string }[] @@ -68,6 +85,17 @@ export interface SearchUsersRequest { limit?: number paginate?: boolean } +export interface SearchUsersResponse { + data: User[] + hasNextPage?: boolean + nextPage?: string +} + +export type FetchUsersResponse = User[] + +export interface FindUserResponse extends User {} + +export type LookupTenantUserResponse = PlatformUser export interface CreateAdminUserRequest { email: string @@ -106,3 +134,28 @@ export interface AcceptUserInviteResponse { export interface SyncUserRequest { previousUser?: User } + +export interface DeleteUserResponse { + message: string +} + +export interface CountUserResponse { + userCount: number +} + +export interface CheckInviteResponse { + email: string +} + +export type GetUserInvitesResponse = InviteWithCode[] + +export interface UpdateInviteRequest extends Omit { + email?: string + builder?: { + apps: string[] + } + apps: string[] +} +export interface UpdateInviteResponse extends Invite {} + +export type LookupAccountHolderResponse = AccountMetadata | null diff --git a/packages/types/src/documents/global/devInfo.ts b/packages/types/src/documents/global/devInfo.ts new file mode 100644 index 0000000000..f993fc5df6 --- /dev/null +++ b/packages/types/src/documents/global/devInfo.ts @@ -0,0 +1,6 @@ +import { Document } from "../document" + +export interface DevInfo extends Document { + userId: string + apiKey?: string +} diff --git a/packages/types/src/documents/global/index.ts b/packages/types/src/documents/global/index.ts index 7d2f5a767c..9bf24f078d 100644 --- a/packages/types/src/documents/global/index.ts +++ b/packages/types/src/documents/global/index.ts @@ -8,3 +8,4 @@ export * from "./templates" export * from "./environmentVariables" export * from "./auditLogs" export * from "./apikeys" +export * from "./devInfo" diff --git a/packages/worker/src/api/controllers/global/auth.ts b/packages/worker/src/api/controllers/global/auth.ts index 530a1df102..1e6b0d3aab 100644 --- a/packages/worker/src/api/controllers/global/auth.ts +++ b/packages/worker/src/api/controllers/global/auth.ts @@ -16,8 +16,15 @@ import { PasswordResetUpdateRequest, GoogleInnerConfig, DatasourceAuthCookie, + LogoutResponse, + UserCtx, + SetInitInfoRequest, + GetInitInfoResponse, + PasswordResetResponse, + PasswordResetUpdateResponse, } from "@budibase/types" import env from "../../../environment" +import { Next } from "koa" import * as authSdk from "../../../sdk/auth" import * as userSdk from "../../../sdk/users" @@ -52,7 +59,7 @@ async function passportCallback( ctx.set(Header.TOKEN, token) } -export const login = async (ctx: Ctx, next: any) => { +export const login = async (ctx: Ctx, next: Next) => { const email = ctx.request.body.username const user = await userSdk.db.getUserByEmail(email) @@ -72,7 +79,7 @@ export const login = async (ctx: Ctx, next: any) => { )(ctx, next) } -export const logout = async (ctx: any) => { +export const logout = async (ctx: UserCtx) => { if (ctx.user && ctx.user._id) { await authSdk.logout({ ctx, userId: ctx.user._id }) } @@ -81,13 +88,13 @@ export const logout = async (ctx: any) => { // INIT -export const setInitInfo = (ctx: any) => { +export const setInitInfo = (ctx: UserCtx) => { const initInfo = ctx.request.body setCookie(ctx, initInfo, Cookie.Init) ctx.status = 200 } -export const getInitInfo = (ctx: any) => { +export const getInitInfo = (ctx: UserCtx) => { try { ctx.body = getCookie(ctx, Cookie.Init) || {} } catch (err) { @@ -101,7 +108,9 @@ export const getInitInfo = (ctx: any) => { /** * Reset the user password, used as part of a forgotten password flow. */ -export const reset = async (ctx: Ctx) => { +export const reset = async ( + ctx: Ctx +) => { const { email } = ctx.request.body await authSdk.reset(email) @@ -114,7 +123,9 @@ export const reset = async (ctx: Ctx) => { /** * Perform the user password update if the provided reset code is valid. */ -export const resetUpdate = async (ctx: Ctx) => { +export const resetUpdate = async ( + ctx: Ctx +) => { const { resetCode, password } = ctx.request.body try { await authSdk.resetUpdate(resetCode, password) @@ -130,7 +141,10 @@ export const resetUpdate = async (ctx: Ctx) => { // DATASOURCE -export const datasourcePreAuth = async (ctx: any, next: any) => { +export const datasourcePreAuth = async ( + ctx: UserCtx, + next: Next +) => { const provider = ctx.params.provider const { middleware } = require(`@budibase/backend-core`) const handler = middleware.datasource[provider] @@ -147,7 +161,7 @@ export const datasourcePreAuth = async (ctx: any, next: any) => { return handler.preAuth(passport, ctx, next) } -export const datasourceAuth = async (ctx: any, next: any) => { +export const datasourceAuth = async (ctx: UserCtx, next: Next) => { const authStateCookie = getCookie( ctx, Cookie.DatasourceAuth @@ -171,7 +185,7 @@ export async function googleCallbackUrl(config?: GoogleInnerConfig) { * The initial call that google authentication makes to take you to the google login screen. * On a successful login, you will be redirected to the googleAuth callback route. */ -export const googlePreAuth = async (ctx: any, next: any) => { +export const googlePreAuth = async (ctx: Ctx, next: Next) => { const config = await configs.getGoogleConfig() if (!config) { return ctx.throw(400, "Google config not found") @@ -190,7 +204,7 @@ export const googlePreAuth = async (ctx: any, next: any) => { })(ctx, next) } -export const googleCallback = async (ctx: any, next: any) => { +export const googleCallback = async (ctx: Ctx, next: Next) => { const config = await configs.getGoogleConfig() if (!config) { return ctx.throw(400, "Google config not found") @@ -241,7 +255,7 @@ export const oidcStrategyFactory = async (ctx: any) => { * The initial call that OIDC authentication makes to take you to the configured OIDC login screen. * On a successful login, you will be redirected to the oidcAuth callback route. */ -export const oidcPreAuth = async (ctx: Ctx, next: any) => { +export const oidcPreAuth = async (ctx: Ctx, next: Next) => { const { configId } = ctx.params if (!configId) { ctx.throw(400, "OIDC config id is required") @@ -266,7 +280,7 @@ export const oidcPreAuth = async (ctx: Ctx, next: any) => { })(ctx, next) } -export const oidcCallback = async (ctx: any, next: any) => { +export const oidcCallback = async (ctx: Ctx, next: Next) => { const strategy = await oidcStrategyFactory(ctx) return passport.authenticate( diff --git a/packages/worker/src/api/controllers/global/configs.ts b/packages/worker/src/api/controllers/global/configs.ts index b3f02a44c6..a10fce35f6 100644 --- a/packages/worker/src/api/controllers/global/configs.ts +++ b/packages/worker/src/api/controllers/global/configs.ts @@ -15,8 +15,11 @@ import { AIConfig, AIInnerConfig, Config, + ConfigChecklistResponse, ConfigType, Ctx, + DeleteConfigResponse, + FindConfigResponse, GetPublicOIDCConfigResponse, GetPublicSettingsResponse, GoogleInnerConfig, @@ -29,11 +32,14 @@ import { OIDCLogosConfig, PASSWORD_REPLACEMENT, QuotaUsageType, + SaveConfigRequest, + SaveConfigResponse, SettingsBrandingConfig, SettingsInnerConfig, SSOConfig, SSOConfigType, StaticQuotaName, + UploadConfigFileResponse, UserCtx, } from "@budibase/types" import * as pro from "@budibase/pro" @@ -225,7 +231,9 @@ export async function verifyAIConfig( } } -export async function save(ctx: UserCtx) { +export async function save( + ctx: UserCtx +) { const body = ctx.request.body const type = body.type const config = body.config @@ -337,7 +345,7 @@ function enrichOIDCLogos(oidcLogos: OIDCLogosConfig) { ) } -export async function find(ctx: UserCtx) { +export async function find(ctx: UserCtx) { try { // Find the config with the most granular scope based on context const type = ctx.params.type @@ -473,7 +481,7 @@ export async function publicSettings( } } -export async function upload(ctx: UserCtx) { +export async function upload(ctx: UserCtx) { if (ctx.request.files == null || Array.isArray(ctx.request.files.file)) { ctx.throw(400, "One file must be uploaded.") } @@ -518,7 +526,7 @@ export async function upload(ctx: UserCtx) { } } -export async function destroy(ctx: UserCtx) { +export async function destroy(ctx: UserCtx) { const db = tenancy.getGlobalDB() const { id, rev } = ctx.params try { @@ -537,14 +545,14 @@ export async function destroy(ctx: UserCtx) { } } -export async function configChecklist(ctx: Ctx) { +export async function configChecklist(ctx: Ctx) { const tenantId = tenancy.getTenantId() try { ctx.body = await cache.withCache( cache.CacheKey.CHECKLIST, env.CHECKLIST_CACHE_TTL, - async () => { + async (): Promise => { let apps = [] if (!env.MULTI_TENANCY || tenantId) { // Apps exist diff --git a/packages/worker/src/api/controllers/global/email.ts b/packages/worker/src/api/controllers/global/email.ts index 74a811376a..ad0fc3fa32 100644 --- a/packages/worker/src/api/controllers/global/email.ts +++ b/packages/worker/src/api/controllers/global/email.ts @@ -1,8 +1,15 @@ import { sendEmail as sendEmailFn } from "../../../utilities/email" import { tenancy } from "@budibase/backend-core" -import { BBContext, User } from "@budibase/types" +import { + UserCtx, + User, + SendEmailRequest, + SendEmailResponse, +} from "@budibase/types" -export async function sendEmail(ctx: BBContext) { +export async function sendEmail( + ctx: UserCtx +) { let { workspaceId, email, diff --git a/packages/worker/src/api/controllers/global/license.ts b/packages/worker/src/api/controllers/global/license.ts index 111cb5cea3..04f76a6593 100644 --- a/packages/worker/src/api/controllers/global/license.ts +++ b/packages/worker/src/api/controllers/global/license.ts @@ -5,6 +5,7 @@ import { GetLicenseKeyResponse, GetOfflineIdentifierResponse, GetOfflineLicenseTokenResponse, + GetQuotaUsageResponse, UserCtx, } from "@budibase/types" @@ -36,7 +37,7 @@ export async function deleteLicenseKey(ctx: UserCtx) { // OFFLINE LICENSE export async function activateOfflineLicenseToken( - ctx: UserCtx + ctx: UserCtx ) { const { offlineLicenseToken } = ctx.request.body await licensing.offline.activateOfflineLicenseToken(offlineLicenseToken) @@ -70,14 +71,16 @@ export async function getOfflineLicenseIdentifier( // LICENSES -export const refresh = async (ctx: any) => { +export const refresh = async (ctx: UserCtx) => { await licensing.cache.refresh() ctx.status = 200 } // USAGE -export const getQuotaUsage = async (ctx: any) => { +export const getQuotaUsage = async ( + ctx: UserCtx +) => { ctx.body = await quotas.getQuotaUsage() ctx.status = 200 } diff --git a/packages/worker/src/api/controllers/global/roles.ts b/packages/worker/src/api/controllers/global/roles.ts index e1a3130924..fb93ab504c 100644 --- a/packages/worker/src/api/controllers/global/roles.ts +++ b/packages/worker/src/api/controllers/global/roles.ts @@ -6,9 +6,15 @@ import { tenancy, } from "@budibase/backend-core" import sdk from "../../../sdk" -import { Ctx, App } from "@budibase/types" +import { + Ctx, + App, + FetchGlobalRolesResponse, + FindGlobalRoleResponse, + RemoveAppRoleResponse, +} from "@budibase/types" -export async function fetch(ctx: Ctx) { +export async function fetch(ctx: Ctx) { const tenantId = ctx.user!.tenantId // always use the dev apps as they'll be most up to date (true) const apps = (await dbCore.getAllApps({ tenantId, all: true })) as App[] @@ -31,7 +37,7 @@ export async function fetch(ctx: Ctx) { ctx.body = response } -export async function find(ctx: Ctx) { +export async function find(ctx: Ctx) { const appId = ctx.params.appId await context.doInAppContext(dbCore.getDevAppID(appId), async () => { const db = context.getAppDB() @@ -45,7 +51,7 @@ export async function find(ctx: Ctx) { }) } -export async function removeAppRole(ctx: Ctx) { +export async function removeAppRole(ctx: Ctx) { const { appId } = ctx.params const db = tenancy.getGlobalDB() const users = await sdk.users.db.allUsers() diff --git a/packages/worker/src/api/controllers/global/self.ts b/packages/worker/src/api/controllers/global/self.ts index 14a2ce8c73..f8488f526b 100644 --- a/packages/worker/src/api/controllers/global/self.ts +++ b/packages/worker/src/api/controllers/global/self.ts @@ -10,6 +10,11 @@ import { import env from "../../../environment" import { groups } from "@budibase/pro" import { + DevInfo, + FetchAPIKeyResponse, + GenerateAPIKeyRequest, + GenerateAPIKeyResponse, + GetGlobalSelfResponse, UpdateSelfRequest, UpdateSelfResponse, User, @@ -35,22 +40,24 @@ function cleanupDevInfo(info: any) { return info } -export async function generateAPIKey(ctx: any) { +export async function generateAPIKey( + ctx: UserCtx +) { let userId let apiKey if (env.isTest() && ctx.request.body.userId) { userId = ctx.request.body.userId apiKey = newTestApiKey() } else { - userId = ctx.user._id + userId = ctx.user._id! apiKey = newApiKey() } const db = tenancy.getGlobalDB() const id = dbCore.generateDevInfoID(userId) - let devInfo + let devInfo: DevInfo try { - devInfo = await db.get(id) + devInfo = await db.get(id) } catch (err) { devInfo = { _id: id, userId } } @@ -59,9 +66,9 @@ export async function generateAPIKey(ctx: any) { ctx.body = cleanupDevInfo(devInfo) } -export async function fetchAPIKey(ctx: any) { +export async function fetchAPIKey(ctx: UserCtx) { const db = tenancy.getGlobalDB() - const id = dbCore.generateDevInfoID(ctx.user._id) + const id = dbCore.generateDevInfoID(ctx.user._id!) let devInfo try { devInfo = await db.get(id) @@ -87,11 +94,11 @@ const addSessionAttributesToUser = (ctx: any) => { ctx.body.csrfToken = ctx.user.csrfToken } -export async function getSelf(ctx: any) { +export async function getSelf(ctx: UserCtx) { if (!ctx.user) { ctx.throw(403, "User not logged in") } - const userId = ctx.user._id + const userId = ctx.user._id! ctx.params = { id: userId, } diff --git a/packages/worker/src/api/controllers/global/templates.ts b/packages/worker/src/api/controllers/global/templates.ts index 0abce704c7..d717f47515 100644 --- a/packages/worker/src/api/controllers/global/templates.ts +++ b/packages/worker/src/api/controllers/global/templates.ts @@ -3,10 +3,25 @@ import { TemplateBindings, GLOBAL_OWNER, } from "../../../constants" -import { getTemplates } from "../../../constants/templates" +import { getTemplateByID, getTemplates } from "../../../constants/templates" import { tenancy, db as dbCore } from "@budibase/backend-core" +import { + DeleteGlobalTemplateResponse, + FetchGlobalTemplateByOwnerIDResponse, + FetchGlobalTemplateByTypeResponse, + FetchGlobalTemplateDefinitionResponse, + FetchGlobalTemplateResponse, + FindGlobalTemplateResponse, + SaveGlobalTemplateRequest, + SaveGlobalTemplateResponse, + GlobalTemplateBinding, + GlobalTemplateDefinition, + UserCtx, +} from "@budibase/types" -export async function save(ctx: any) { +export async function save( + ctx: UserCtx +) { const db = tenancy.getGlobalDB() let template = ctx.request.body if (!template.ownerId) { @@ -23,9 +38,11 @@ export async function save(ctx: any) { } } -export async function definitions(ctx: any) { - const bindings: any = {} - const info: any = {} +export async function definitions( + ctx: UserCtx +) { + const bindings: Record = {} + const info: Record = {} for (let template of TemplateMetadata.email) { bindings[template.purpose] = template.bindings info[template.purpose] = { @@ -44,34 +61,35 @@ export async function definitions(ctx: any) { } } -export async function fetch(ctx: any) { +export async function fetch(ctx: UserCtx) { ctx.body = await getTemplates() } -export async function fetchByType(ctx: any) { - // @ts-ignore +export async function fetchByType( + ctx: UserCtx +) { ctx.body = await getTemplates({ type: ctx.params.type, }) } -export async function fetchByOwner(ctx: any) { +export async function fetchByOwner( + ctx: UserCtx +) { // @ts-ignore ctx.body = await getTemplates({ ownerId: ctx.params.ownerId, }) } -export async function find(ctx: any) { - // @ts-ignore - ctx.body = await getTemplates({ - id: ctx.params.id, - }) +export async function find(ctx: UserCtx) { + ctx.body = await getTemplateByID(ctx.params.id) } -export async function destroy(ctx: any) { +export async function destroy( + ctx: UserCtx +) { const db = tenancy.getGlobalDB() await db.remove(ctx.params.id, ctx.params.rev) - ctx.message = `Template ${ctx.params.id} deleted.` - ctx.status = 200 + ctx.body = { message: `Template ${ctx.params.id} deleted.` } } diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index e977d5ff5d..1d36dd524c 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -6,21 +6,34 @@ import { AddSSoUserRequest, BulkUserRequest, BulkUserResponse, + CheckInviteResponse, + CountUserResponse, CreateAdminUserRequest, CreateAdminUserResponse, Ctx, DeleteInviteUserRequest, DeleteInviteUsersRequest, + DeleteInviteUsersResponse, + DeleteUserResponse, + FetchUsersResponse, + FindUserResponse, + GetUserInvitesResponse, Hosting, InviteUserRequest, + InviteUserResponse, InviteUsersRequest, InviteUsersResponse, LockName, LockType, + LookupAccountHolderResponse, + LookupTenantUserResponse, MigrationType, PlatformUserByEmail, SaveUserResponse, SearchUsersRequest, + SearchUsersResponse, + UpdateInviteRequest, + UpdateInviteResponse, User, UserCtx, UserIdentifier, @@ -80,7 +93,7 @@ export const save = async (ctx: UserCtx) => { } } -export const addSsoSupport = async (ctx: Ctx) => { +export const addSsoSupport = async (ctx: Ctx) => { const { email, ssoId } = ctx.request.body try { // Status is changed to 404 from getUserDoc if user is not found @@ -207,7 +220,7 @@ export const adminUser = async ( }) } -export const countByApp = async (ctx: any) => { +export const countByApp = async (ctx: UserCtx) => { const appId = ctx.params.appId try { ctx.body = await userSdk.db.countUsersByApp(appId) @@ -216,7 +229,7 @@ export const countByApp = async (ctx: any) => { } } -export const destroy = async (ctx: any) => { +export const destroy = async (ctx: UserCtx) => { const id = ctx.params.id if (id === ctx.user._id) { ctx.throw(400, "Unable to delete self.") @@ -239,7 +252,9 @@ export const getAppUsers = async (ctx: Ctx) => { ctx.body = { data: users } } -export const search = async (ctx: Ctx) => { +export const search = async ( + ctx: Ctx +) => { const body = ctx.request.body // TODO: for now only two supported search keys; string.email and equal._id @@ -280,7 +295,7 @@ export const search = async (ctx: Ctx) => { } // called internally by app server user fetch -export const fetch = async (ctx: any) => { +export const fetch = async (ctx: UserCtx) => { const all = await userSdk.db.allUsers() // user hashed password shouldn't ever be returned for (let user of all) { @@ -292,11 +307,13 @@ export const fetch = async (ctx: any) => { } // called internally by app server user find -export const find = async (ctx: any) => { +export const find = async (ctx: UserCtx) => { ctx.body = await userSdk.db.getUser(ctx.params.id) } -export const tenantUserLookup = async (ctx: any) => { +export const tenantUserLookup = async ( + ctx: UserCtx +) => { const id = ctx.params.id // is email, check its valid if (id.includes("@") && !emailValidator.validate(id)) { @@ -314,7 +331,9 @@ export const tenantUserLookup = async (ctx: any) => { * This will be paginated to a default of the first 50 users, * So the account holder may not be found until further pagination has occurred */ -export const accountHolderLookup = async (ctx: Ctx) => { +export const accountHolderLookup = async ( + ctx: Ctx +) => { try { const users = await userSdk.core.getAllUsers() const response = await userSdk.core.getExistingAccounts( @@ -366,7 +385,9 @@ export const onboardUsers = async ( ctx.body = { ...resp, created: true } } -export const invite = async (ctx: Ctx) => { +export const invite = async ( + ctx: Ctx +) => { const request = ctx.request.body let multiRequest = [request] @@ -389,12 +410,14 @@ export const invite = async (ctx: Ctx) => { } } -export const inviteMultiple = async (ctx: Ctx) => { +export const inviteMultiple = async ( + ctx: Ctx +) => { ctx.body = await userSdk.invite(ctx.request.body) } export const removeMultipleInvites = async ( - ctx: Ctx + ctx: Ctx ) => { const inviteCodesToRemove = ctx.request.body.map( (invite: DeleteInviteUserRequest) => invite.code @@ -407,7 +430,7 @@ export const removeMultipleInvites = async ( } } -export const checkInvite = async (ctx: any) => { +export const checkInvite = async (ctx: UserCtx) => { const { code } = ctx.params let invite try { @@ -422,7 +445,9 @@ export const checkInvite = async (ctx: any) => { } } -export const getUserInvites = async (ctx: any) => { +export const getUserInvites = async ( + ctx: UserCtx +) => { try { // Restricted to the currently authenticated tenant ctx.body = await cache.invite.getInviteCodes() @@ -431,7 +456,9 @@ export const getUserInvites = async (ctx: any) => { } } -export const updateInvite = async (ctx: any) => { +export const updateInvite = async ( + ctx: UserCtx +) => { const { code } = ctx.params let updateBody = { ...ctx.request.body } diff --git a/packages/worker/src/api/controllers/global/workspaces.ts b/packages/worker/src/api/controllers/global/workspaces.ts deleted file mode 100644 index cf6486fec9..0000000000 --- a/packages/worker/src/api/controllers/global/workspaces.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { tenancy, db as dbCore } from "@budibase/backend-core" -import { BBContext } from "@budibase/types" - -export async function save(ctx: BBContext) { - const db = tenancy.getGlobalDB() - const workspaceDoc = ctx.request.body - - // workspace does not exist yet - if (!workspaceDoc._id) { - workspaceDoc._id = dbCore.generateWorkspaceID() - } - - try { - const response = await db.put(workspaceDoc) - ctx.body = { - _id: response.id, - _rev: response.rev, - } - } catch (err: any) { - ctx.throw(err.status, err) - } -} - -export async function fetch(ctx: BBContext) { - const db = tenancy.getGlobalDB() - const response = await db.allDocs( - dbCore.getWorkspaceParams(undefined, { - include_docs: true, - }) - ) - ctx.body = response.rows.map(row => row.doc) -} - -export async function find(ctx: BBContext) { - const db = tenancy.getGlobalDB() - try { - ctx.body = await db.get(ctx.params.id) - } catch (err: any) { - ctx.throw(err.status, err) - } -} - -export async function destroy(ctx: BBContext) { - const db = tenancy.getGlobalDB() - const { id, rev } = ctx.params - - try { - await db.remove(id, rev) - ctx.body = { message: "Workspace deleted successfully" } - } catch (err: any) { - ctx.throw(err.status, err) - } -} diff --git a/packages/worker/src/api/controllers/system/accounts.ts b/packages/worker/src/api/controllers/system/accounts.ts index 7e6d1b51b8..e02c3f046d 100644 --- a/packages/worker/src/api/controllers/system/accounts.ts +++ b/packages/worker/src/api/controllers/system/accounts.ts @@ -1,7 +1,15 @@ -import { Account, AccountMetadata, Ctx } from "@budibase/types" +import { + Account, + AccountMetadata, + Ctx, + SaveAccountRequest, + SaveAccountResponse, +} from "@budibase/types" import * as accounts from "../../../sdk/accounts" -export const save = async (ctx: Ctx) => { +export const save = async ( + ctx: Ctx +) => { const account = ctx.request.body as Account let metadata: AccountMetadata = { _id: accounts.metadata.formatAccountMetadataId(account.accountId), @@ -14,7 +22,7 @@ export const save = async (ctx: Ctx) => { ctx.status = 200 } -export const destroy = async (ctx: any) => { +export const destroy = async (ctx: Ctx) => { const accountId = accounts.metadata.formatAccountMetadataId( ctx.params.accountId ) diff --git a/packages/worker/src/api/controllers/system/environment.ts b/packages/worker/src/api/controllers/system/environment.ts index 18ecc380db..4b75f49554 100644 --- a/packages/worker/src/api/controllers/system/environment.ts +++ b/packages/worker/src/api/controllers/system/environment.ts @@ -1,4 +1,4 @@ -import { Ctx, MaintenanceType } from "@budibase/types" +import { Ctx, GetEnvironmentResponse, MaintenanceType } from "@budibase/types" import env from "../../../environment" import { env as coreEnv, db as dbCore } from "@budibase/backend-core" import nodeFetch from "node-fetch" @@ -38,13 +38,13 @@ async function isSqsMissing() { return !(await isSqsAvailable()) } -export const fetch = async (ctx: Ctx) => { +export const fetch = async (ctx: Ctx) => { ctx.body = { multiTenancy: !!env.MULTI_TENANCY, offlineMode: !!coreEnv.OFFLINE_MODE, cloud: !env.SELF_HOSTED, accountPortalUrl: env.ACCOUNT_PORTAL_URL, - disableAccountPortal: env.DISABLE_ACCOUNT_PORTAL, + disableAccountPortal: !!env.DISABLE_ACCOUNT_PORTAL, baseUrl: env.PLATFORM_URL, isDev: env.isDev() && !env.isTest(), maintenance: [], diff --git a/packages/worker/src/api/controllers/system/logs.ts b/packages/worker/src/api/controllers/system/logs.ts index a5607d545a..06ae75407d 100644 --- a/packages/worker/src/api/controllers/system/logs.ts +++ b/packages/worker/src/api/controllers/system/logs.ts @@ -1,7 +1,7 @@ -import { UserCtx } from "@budibase/types" +import { GetLogResponse, UserCtx } from "@budibase/types" import { installation, logging } from "@budibase/backend-core" -export async function getLogs(ctx: UserCtx) { +export async function getLogs(ctx: UserCtx) { const logReadStream = logging.system.getLogReadStream() const { installId } = await installation.getInstall() diff --git a/packages/worker/src/api/controllers/system/migrations.ts b/packages/worker/src/api/controllers/system/migrations.ts index 57a5f6261c..dc66412e42 100644 --- a/packages/worker/src/api/controllers/system/migrations.ts +++ b/packages/worker/src/api/controllers/system/migrations.ts @@ -1,13 +1,23 @@ +import { + FetchMigrationDefinitionsResponse, + RunGlobalMigrationRequest, + UserCtx, +} from "@budibase/types" + const { migrate, MIGRATIONS } = require("../../../migrations") -export const runMigrations = async (ctx: any) => { +export const runMigrations = async ( + ctx: UserCtx +) => { const options = ctx.request.body // don't await as can take a while, just return migrate(options) ctx.status = 200 } -export const fetchDefinitions = async (ctx: any) => { +export const fetchDefinitions = async ( + ctx: UserCtx +) => { ctx.body = MIGRATIONS ctx.status = 200 } diff --git a/packages/worker/src/api/controllers/system/restore.ts b/packages/worker/src/api/controllers/system/restore.ts index def6d58a5d..de3b4b8ff0 100644 --- a/packages/worker/src/api/controllers/system/restore.ts +++ b/packages/worker/src/api/controllers/system/restore.ts @@ -1,8 +1,10 @@ import env from "../../../environment" -import { BBContext } from "@budibase/types" +import { SystemRestoreResponse, UserCtx } from "@budibase/types" import { cache } from "@budibase/backend-core" -export async function systemRestored(ctx: BBContext) { +export async function systemRestored( + ctx: UserCtx +) { if (!env.SELF_HOSTED) { ctx.throw(405, "This operation is not allowed in cloud.") } diff --git a/packages/worker/src/api/controllers/system/tenants.ts b/packages/worker/src/api/controllers/system/tenants.ts index 476343a05e..54388bf1fc 100644 --- a/packages/worker/src/api/controllers/system/tenants.ts +++ b/packages/worker/src/api/controllers/system/tenants.ts @@ -1,7 +1,7 @@ -import { UserCtx } from "@budibase/types" +import { GetTenantInfoResponse, UserCtx } from "@budibase/types" import * as tenantSdk from "../../../sdk/tenants" -export async function destroy(ctx: UserCtx) { +export async function destroy(ctx: UserCtx) { const user = ctx.user! const tenantId = ctx.params.tenantId @@ -18,6 +18,6 @@ export async function destroy(ctx: UserCtx) { } } -export async function info(ctx: UserCtx) { +export async function info(ctx: UserCtx) { ctx.body = await tenantSdk.tenantInfo(ctx.params.tenantId) } diff --git a/packages/worker/src/api/routes/global/email.ts b/packages/worker/src/api/routes/global/email.ts index 94e6b9bce5..6b60da47b5 100644 --- a/packages/worker/src/api/routes/global/email.ts +++ b/packages/worker/src/api/routes/global/email.ts @@ -1,7 +1,7 @@ import Router from "@koa/router" import * as controller from "../../controllers/global/email" -import { EmailTemplatePurpose } from "../../../constants" import { auth } from "@budibase/backend-core" +import { EmailTemplatePurpose } from "@budibase/types" import Joi from "joi" const router: Router = new Router() diff --git a/packages/worker/src/api/routes/global/tests/email.spec.ts b/packages/worker/src/api/routes/global/tests/email.spec.ts index 49d7c3bbeb..eb46c501d9 100644 --- a/packages/worker/src/api/routes/global/tests/email.spec.ts +++ b/packages/worker/src/api/routes/global/tests/email.spec.ts @@ -1,8 +1,8 @@ jest.mock("nodemailer") +import { EmailTemplatePurpose } from "@budibase/types" import { TestConfiguration, mocks } from "../../../../tests" const sendMailMock = mocks.email.mock() -import { EmailTemplatePurpose } from "../../../../constants" describe("/api/global/email", () => { const config = new TestConfiguration() diff --git a/packages/worker/src/api/routes/global/tests/realEmail.spec.ts b/packages/worker/src/api/routes/global/tests/realEmail.spec.ts index 99dfb7f824..bf5ed7b4ee 100644 --- a/packages/worker/src/api/routes/global/tests/realEmail.spec.ts +++ b/packages/worker/src/api/routes/global/tests/realEmail.spec.ts @@ -1,11 +1,10 @@ jest.unmock("node-fetch") import { TestConfiguration } from "../../../../tests" -import { EmailTemplatePurpose } from "../../../../constants" import { objectStore } from "@budibase/backend-core" import { helpers } from "@budibase/shared-core" import tk from "timekeeper" -import { EmailAttachment } from "@budibase/types" +import { EmailAttachment, EmailTemplatePurpose } from "@budibase/types" const fetch = require("node-fetch") diff --git a/packages/worker/src/api/routes/global/tests/templates.spec.ts b/packages/worker/src/api/routes/global/tests/templates.spec.ts index 982cae28b5..786023101e 100644 --- a/packages/worker/src/api/routes/global/tests/templates.spec.ts +++ b/packages/worker/src/api/routes/global/tests/templates.spec.ts @@ -1,9 +1,6 @@ -import { - EmailTemplatePurpose, - TemplateMetadata, - TemplateType, -} from "../../../../constants" +import { TemplateMetadata, TemplateType } from "../../../../constants" import { TestConfiguration } from "../../../../tests" +import { EmailTemplatePurpose } from "@budibase/types" // TODO diff --git a/packages/worker/src/api/routes/global/workspaces.ts b/packages/worker/src/api/routes/global/workspaces.ts deleted file mode 100644 index 79afb53b96..0000000000 --- a/packages/worker/src/api/routes/global/workspaces.ts +++ /dev/null @@ -1,37 +0,0 @@ -import Router from "@koa/router" -import * as controller from "../../controllers/global/workspaces" -import { auth } from "@budibase/backend-core" -import Joi from "joi" - -const router: Router = new Router() - -function buildWorkspaceSaveValidation() { - // prettier-ignore - return auth.joiValidator.body(Joi.object({ - _id: Joi.string().optional(), - _rev: Joi.string().optional(), - name: Joi.string().required(), - users: Joi.array().required(), - managers: Joi.array().required(), - roles: Joi.object({ - default: Joi.string().optional(), - app: Joi.object() - .pattern(/.*/, Joi.string()) - .required() - .unknown(true), - }).unknown(true).optional(), - }).required().unknown(true)) -} - -router - .post( - "/api/global/workspaces", - auth.adminOnly, - buildWorkspaceSaveValidation(), - controller.save - ) - .delete("/api/global/workspaces/:id", auth.adminOnly, controller.destroy) - .get("/api/global/workspaces", controller.fetch) - .get("/api/global/workspaces/:id", controller.find) - -export default router diff --git a/packages/worker/src/api/routes/index.ts b/packages/worker/src/api/routes/index.ts index e6cacf110f..741026543c 100644 --- a/packages/worker/src/api/routes/index.ts +++ b/packages/worker/src/api/routes/index.ts @@ -2,7 +2,6 @@ import Router from "@koa/router" import { api as pro } from "@budibase/pro" import userRoutes from "./global/users" import configRoutes from "./global/configs" -import workspaceRoutes from "./global/workspaces" import templateRoutes from "./global/templates" import emailRoutes from "./global/email" import authRoutes from "./global/auth" @@ -24,7 +23,6 @@ export const routes: Router[] = [ configRoutes, userRoutes, pro.users, - workspaceRoutes, authRoutes, templateRoutes, tenantsRoutes, diff --git a/packages/worker/src/api/routes/system/tests/environment.spec.ts b/packages/worker/src/api/routes/system/tests/environment.spec.ts index dbe9be7374..58f523648d 100644 --- a/packages/worker/src/api/routes/system/tests/environment.spec.ts +++ b/packages/worker/src/api/routes/system/tests/environment.spec.ts @@ -22,7 +22,7 @@ describe("/api/system/environment", () => { const env = await config.api.environment.getEnvironment() expect(env.body).toEqual({ cloud: true, - disableAccountPortal: 0, + disableAccountPortal: false, isDev: false, multiTenancy: true, baseUrl: "http://localhost:10000", @@ -36,7 +36,7 @@ describe("/api/system/environment", () => { const env = await config.api.environment.getEnvironment() expect(env.body).toEqual({ cloud: false, - disableAccountPortal: 0, + disableAccountPortal: false, isDev: false, multiTenancy: true, baseUrl: "http://localhost:10000", diff --git a/packages/worker/src/constants/index.ts b/packages/worker/src/constants/index.ts index 17fce666db..309fdeebc1 100644 --- a/packages/worker/src/constants/index.ts +++ b/packages/worker/src/constants/index.ts @@ -1,4 +1,5 @@ import { constants } from "@budibase/backend-core" +import { EmailTemplatePurpose } from "@budibase/types" export const LOGO_URL = "https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg" @@ -19,15 +20,6 @@ export enum TemplateType { EMAIL = "email", } -export enum EmailTemplatePurpose { - CORE = "core", - BASE = "base", - PASSWORD_RECOVERY = "password_recovery", - INVITATION = "invitation", - WELCOME = "welcome", - CUSTOM = "custom", -} - export enum TemplateMetadataNames { BASE = "Base format", PASSWORD_RECOVERY = "Password recovery", diff --git a/packages/worker/src/constants/templates/index.ts b/packages/worker/src/constants/templates/index.ts index 6dd3f556a6..b2e98afe58 100644 --- a/packages/worker/src/constants/templates/index.ts +++ b/packages/worker/src/constants/templates/index.ts @@ -1,13 +1,8 @@ import { readStaticFile } from "../../utilities/fileSystem" -import { - EmailTemplatePurpose, - TemplateType, - TemplatePurpose, - GLOBAL_OWNER, -} from "../index" +import { TemplateType, TemplatePurpose, GLOBAL_OWNER } from "../index" import { join } from "path" import { db as dbCore, tenancy } from "@budibase/backend-core" -import { Template } from "@budibase/types" +import { Template, EmailTemplatePurpose } from "@budibase/types" export const EmailTemplates = { [EmailTemplatePurpose.PASSWORD_RECOVERY]: readStaticFile( @@ -53,8 +48,21 @@ export function addBaseTemplates(templates: Template[], type?: string) { export async function getTemplates({ ownerId, type, - id, -}: { ownerId?: string; type?: string; id?: string } = {}) { +}: { ownerId?: string; type?: string } = {}) { + const db = tenancy.getGlobalDB() + const response = await db.allDocs