@budibase/worker 3.2.25 → 3.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/package.json +7 -7
  2. package/src/api/controllers/global/auth.ts +26 -12
  3. package/src/api/controllers/global/configs.ts +14 -6
  4. package/src/api/controllers/global/email.ts +9 -2
  5. package/src/api/controllers/global/license.ts +6 -3
  6. package/src/api/controllers/global/roles.ts +10 -4
  7. package/src/api/controllers/global/self.ts +15 -8
  8. package/src/api/controllers/global/templates.ts +35 -17
  9. package/src/api/controllers/global/users.ts +41 -14
  10. package/src/api/controllers/system/accounts.ts +11 -3
  11. package/src/api/controllers/system/environment.ts +3 -3
  12. package/src/api/controllers/system/logs.ts +2 -2
  13. package/src/api/controllers/system/migrations.ts +12 -2
  14. package/src/api/controllers/system/restore.ts +4 -2
  15. package/src/api/controllers/system/tenants.ts +3 -3
  16. package/src/api/routes/global/email.ts +1 -1
  17. package/src/api/routes/global/tests/email.spec.ts +1 -1
  18. package/src/api/routes/global/tests/realEmail.spec.ts +1 -2
  19. package/src/api/routes/global/tests/templates.spec.ts +2 -5
  20. package/src/api/routes/index.ts +0 -2
  21. package/src/api/routes/system/tests/environment.spec.ts +2 -2
  22. package/src/constants/index.ts +1 -9
  23. package/src/constants/templates/index.ts +17 -15
  24. package/src/middleware/cloudRestricted.ts +2 -2
  25. package/src/sdk/auth/auth.ts +1 -2
  26. package/src/sdk/users/users.ts +1 -1
  27. package/src/tests/controllers.ts +0 -1
  28. package/src/utilities/email.ts +7 -2
  29. package/src/utilities/templates.ts +2 -5
  30. package/src/api/controllers/global/workspaces.ts +0 -53
  31. package/src/api/routes/global/workspaces.ts +0 -37
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/worker",
3
3
  "email": "hi@budibase.com",
4
- "version": "3.2.25",
4
+ "version": "3.2.26",
5
5
  "description": "Budibase background service",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -38,11 +38,11 @@
38
38
  "author": "Budibase",
39
39
  "license": "GPL-3.0",
40
40
  "dependencies": {
41
- "@budibase/backend-core": "3.2.25",
42
- "@budibase/pro": "3.2.25",
43
- "@budibase/shared-core": "3.2.25",
44
- "@budibase/string-templates": "3.2.25",
45
- "@budibase/types": "3.2.25",
41
+ "@budibase/backend-core": "3.2.26",
42
+ "@budibase/pro": "3.2.26",
43
+ "@budibase/shared-core": "3.2.26",
44
+ "@budibase/string-templates": "3.2.26",
45
+ "@budibase/types": "3.2.26",
46
46
  "@koa/router": "13.1.0",
47
47
  "@techpass/passport-openidconnect": "0.3.3",
48
48
  "@types/global-agent": "2.1.1",
@@ -115,5 +115,5 @@
115
115
  }
116
116
  }
117
117
  },
118
- "gitHead": "3f2da7f0e5e8359de19f91d1ec67dbe3abe36169"
118
+ "gitHead": "bde86d6100ccb5827c9dfe272086fd7e52821146"
119
119
  }
@@ -16,8 +16,15 @@ import {
16
16
  PasswordResetUpdateRequest,
17
17
  GoogleInnerConfig,
18
18
  DatasourceAuthCookie,
19
+ LogoutResponse,
20
+ UserCtx,
21
+ SetInitInfoRequest,
22
+ GetInitInfoResponse,
23
+ PasswordResetResponse,
24
+ PasswordResetUpdateResponse,
19
25
  } from "@budibase/types"
20
26
  import env from "../../../environment"
27
+ import { Next } from "koa"
21
28
 
22
29
  import * as authSdk from "../../../sdk/auth"
23
30
  import * as userSdk from "../../../sdk/users"
@@ -52,7 +59,7 @@ async function passportCallback(
52
59
  ctx.set(Header.TOKEN, token)
53
60
  }
54
61
 
55
- export const login = async (ctx: Ctx<LoginRequest>, next: any) => {
62
+ export const login = async (ctx: Ctx<LoginRequest, void>, next: Next) => {
56
63
  const email = ctx.request.body.username
57
64
 
58
65
  const user = await userSdk.db.getUserByEmail(email)
@@ -72,7 +79,7 @@ export const login = async (ctx: Ctx<LoginRequest>, next: any) => {
72
79
  )(ctx, next)
73
80
  }
74
81
 
75
- export const logout = async (ctx: any) => {
82
+ export const logout = async (ctx: UserCtx<void, LogoutResponse>) => {
76
83
  if (ctx.user && ctx.user._id) {
77
84
  await authSdk.logout({ ctx, userId: ctx.user._id })
78
85
  }
@@ -81,13 +88,13 @@ export const logout = async (ctx: any) => {
81
88
 
82
89
  // INIT
83
90
 
84
- export const setInitInfo = (ctx: any) => {
91
+ export const setInitInfo = (ctx: UserCtx<SetInitInfoRequest, void>) => {
85
92
  const initInfo = ctx.request.body
86
93
  setCookie(ctx, initInfo, Cookie.Init)
87
94
  ctx.status = 200
88
95
  }
89
96
 
90
- export const getInitInfo = (ctx: any) => {
97
+ export const getInitInfo = (ctx: UserCtx<void, GetInitInfoResponse>) => {
91
98
  try {
92
99
  ctx.body = getCookie(ctx, Cookie.Init) || {}
93
100
  } catch (err) {
@@ -101,7 +108,9 @@ export const getInitInfo = (ctx: any) => {
101
108
  /**
102
109
  * Reset the user password, used as part of a forgotten password flow.
103
110
  */
104
- export const reset = async (ctx: Ctx<PasswordResetRequest>) => {
111
+ export const reset = async (
112
+ ctx: Ctx<PasswordResetRequest, PasswordResetResponse>
113
+ ) => {
105
114
  const { email } = ctx.request.body
106
115
 
107
116
  await authSdk.reset(email)
@@ -114,7 +123,9 @@ export const reset = async (ctx: Ctx<PasswordResetRequest>) => {
114
123
  /**
115
124
  * Perform the user password update if the provided reset code is valid.
116
125
  */
117
- export const resetUpdate = async (ctx: Ctx<PasswordResetUpdateRequest>) => {
126
+ export const resetUpdate = async (
127
+ ctx: Ctx<PasswordResetUpdateRequest, PasswordResetUpdateResponse>
128
+ ) => {
118
129
  const { resetCode, password } = ctx.request.body
119
130
  try {
120
131
  await authSdk.resetUpdate(resetCode, password)
@@ -130,7 +141,10 @@ export const resetUpdate = async (ctx: Ctx<PasswordResetUpdateRequest>) => {
130
141
 
131
142
  // DATASOURCE
132
143
 
133
- export const datasourcePreAuth = async (ctx: any, next: any) => {
144
+ export const datasourcePreAuth = async (
145
+ ctx: UserCtx<void, void>,
146
+ next: Next
147
+ ) => {
134
148
  const provider = ctx.params.provider
135
149
  const { middleware } = require(`@budibase/backend-core`)
136
150
  const handler = middleware.datasource[provider]
@@ -147,7 +161,7 @@ export const datasourcePreAuth = async (ctx: any, next: any) => {
147
161
  return handler.preAuth(passport, ctx, next)
148
162
  }
149
163
 
150
- export const datasourceAuth = async (ctx: any, next: any) => {
164
+ export const datasourceAuth = async (ctx: UserCtx<void, void>, next: Next) => {
151
165
  const authStateCookie = getCookie<DatasourceAuthCookie>(
152
166
  ctx,
153
167
  Cookie.DatasourceAuth
@@ -171,7 +185,7 @@ export async function googleCallbackUrl(config?: GoogleInnerConfig) {
171
185
  * The initial call that google authentication makes to take you to the google login screen.
172
186
  * On a successful login, you will be redirected to the googleAuth callback route.
173
187
  */
174
- export const googlePreAuth = async (ctx: any, next: any) => {
188
+ export const googlePreAuth = async (ctx: Ctx<void, void>, next: Next) => {
175
189
  const config = await configs.getGoogleConfig()
176
190
  if (!config) {
177
191
  return ctx.throw(400, "Google config not found")
@@ -190,7 +204,7 @@ export const googlePreAuth = async (ctx: any, next: any) => {
190
204
  })(ctx, next)
191
205
  }
192
206
 
193
- export const googleCallback = async (ctx: any, next: any) => {
207
+ export const googleCallback = async (ctx: Ctx<void, void>, next: Next) => {
194
208
  const config = await configs.getGoogleConfig()
195
209
  if (!config) {
196
210
  return ctx.throw(400, "Google config not found")
@@ -241,7 +255,7 @@ export const oidcStrategyFactory = async (ctx: any) => {
241
255
  * The initial call that OIDC authentication makes to take you to the configured OIDC login screen.
242
256
  * On a successful login, you will be redirected to the oidcAuth callback route.
243
257
  */
244
- export const oidcPreAuth = async (ctx: Ctx, next: any) => {
258
+ export const oidcPreAuth = async (ctx: Ctx<void, void>, next: Next) => {
245
259
  const { configId } = ctx.params
246
260
  if (!configId) {
247
261
  ctx.throw(400, "OIDC config id is required")
@@ -266,7 +280,7 @@ export const oidcPreAuth = async (ctx: Ctx, next: any) => {
266
280
  })(ctx, next)
267
281
  }
268
282
 
269
- export const oidcCallback = async (ctx: any, next: any) => {
283
+ export const oidcCallback = async (ctx: Ctx<void, void>, next: Next) => {
270
284
  const strategy = await oidcStrategyFactory(ctx)
271
285
 
272
286
  return passport.authenticate(
@@ -15,8 +15,11 @@ import {
15
15
  AIConfig,
16
16
  AIInnerConfig,
17
17
  Config,
18
+ ConfigChecklistResponse,
18
19
  ConfigType,
19
20
  Ctx,
21
+ DeleteConfigResponse,
22
+ FindConfigResponse,
20
23
  GetPublicOIDCConfigResponse,
21
24
  GetPublicSettingsResponse,
22
25
  GoogleInnerConfig,
@@ -29,11 +32,14 @@ import {
29
32
  OIDCLogosConfig,
30
33
  PASSWORD_REPLACEMENT,
31
34
  QuotaUsageType,
35
+ SaveConfigRequest,
36
+ SaveConfigResponse,
32
37
  SettingsBrandingConfig,
33
38
  SettingsInnerConfig,
34
39
  SSOConfig,
35
40
  SSOConfigType,
36
41
  StaticQuotaName,
42
+ UploadConfigFileResponse,
37
43
  UserCtx,
38
44
  } from "@budibase/types"
39
45
  import * as pro from "@budibase/pro"
@@ -225,7 +231,9 @@ export async function verifyAIConfig(
225
231
  }
226
232
  }
227
233
 
228
- export async function save(ctx: UserCtx<Config>) {
234
+ export async function save(
235
+ ctx: UserCtx<SaveConfigRequest, SaveConfigResponse>
236
+ ) {
229
237
  const body = ctx.request.body
230
238
  const type = body.type
231
239
  const config = body.config
@@ -337,7 +345,7 @@ function enrichOIDCLogos(oidcLogos: OIDCLogosConfig) {
337
345
  )
338
346
  }
339
347
 
340
- export async function find(ctx: UserCtx) {
348
+ export async function find(ctx: UserCtx<void, FindConfigResponse>) {
341
349
  try {
342
350
  // Find the config with the most granular scope based on context
343
351
  const type = ctx.params.type
@@ -473,7 +481,7 @@ export async function publicSettings(
473
481
  }
474
482
  }
475
483
 
476
- export async function upload(ctx: UserCtx) {
484
+ export async function upload(ctx: UserCtx<void, UploadConfigFileResponse>) {
477
485
  if (ctx.request.files == null || Array.isArray(ctx.request.files.file)) {
478
486
  ctx.throw(400, "One file must be uploaded.")
479
487
  }
@@ -518,7 +526,7 @@ export async function upload(ctx: UserCtx) {
518
526
  }
519
527
  }
520
528
 
521
- export async function destroy(ctx: UserCtx) {
529
+ export async function destroy(ctx: UserCtx<void, DeleteConfigResponse>) {
522
530
  const db = tenancy.getGlobalDB()
523
531
  const { id, rev } = ctx.params
524
532
  try {
@@ -537,14 +545,14 @@ export async function destroy(ctx: UserCtx) {
537
545
  }
538
546
  }
539
547
 
540
- export async function configChecklist(ctx: Ctx) {
548
+ export async function configChecklist(ctx: Ctx<void, ConfigChecklistResponse>) {
541
549
  const tenantId = tenancy.getTenantId()
542
550
 
543
551
  try {
544
552
  ctx.body = await cache.withCache(
545
553
  cache.CacheKey.CHECKLIST,
546
554
  env.CHECKLIST_CACHE_TTL,
547
- async () => {
555
+ async (): Promise<ConfigChecklistResponse> => {
548
556
  let apps = []
549
557
  if (!env.MULTI_TENANCY || tenantId) {
550
558
  // Apps exist
@@ -1,8 +1,15 @@
1
1
  import { sendEmail as sendEmailFn } from "../../../utilities/email"
2
2
  import { tenancy } from "@budibase/backend-core"
3
- import { BBContext, User } from "@budibase/types"
3
+ import {
4
+ UserCtx,
5
+ User,
6
+ SendEmailRequest,
7
+ SendEmailResponse,
8
+ } from "@budibase/types"
4
9
 
5
- export async function sendEmail(ctx: BBContext) {
10
+ export async function sendEmail(
11
+ ctx: UserCtx<SendEmailRequest, SendEmailResponse>
12
+ ) {
6
13
  let {
7
14
  workspaceId,
8
15
  email,
@@ -5,6 +5,7 @@ import {
5
5
  GetLicenseKeyResponse,
6
6
  GetOfflineIdentifierResponse,
7
7
  GetOfflineLicenseTokenResponse,
8
+ GetQuotaUsageResponse,
8
9
  UserCtx,
9
10
  } from "@budibase/types"
10
11
 
@@ -36,7 +37,7 @@ export async function deleteLicenseKey(ctx: UserCtx<void, void>) {
36
37
  // OFFLINE LICENSE
37
38
 
38
39
  export async function activateOfflineLicenseToken(
39
- ctx: UserCtx<ActivateOfflineLicenseTokenRequest>
40
+ ctx: UserCtx<ActivateOfflineLicenseTokenRequest, void>
40
41
  ) {
41
42
  const { offlineLicenseToken } = ctx.request.body
42
43
  await licensing.offline.activateOfflineLicenseToken(offlineLicenseToken)
@@ -70,14 +71,16 @@ export async function getOfflineLicenseIdentifier(
70
71
 
71
72
  // LICENSES
72
73
 
73
- export const refresh = async (ctx: any) => {
74
+ export const refresh = async (ctx: UserCtx<void, void>) => {
74
75
  await licensing.cache.refresh()
75
76
  ctx.status = 200
76
77
  }
77
78
 
78
79
  // USAGE
79
80
 
80
- export const getQuotaUsage = async (ctx: any) => {
81
+ export const getQuotaUsage = async (
82
+ ctx: UserCtx<void, GetQuotaUsageResponse>
83
+ ) => {
81
84
  ctx.body = await quotas.getQuotaUsage()
82
85
  ctx.status = 200
83
86
  }
@@ -6,9 +6,15 @@ import {
6
6
  tenancy,
7
7
  } from "@budibase/backend-core"
8
8
  import sdk from "../../../sdk"
9
- import { Ctx, App } from "@budibase/types"
9
+ import {
10
+ Ctx,
11
+ App,
12
+ FetchGlobalRolesResponse,
13
+ FindGlobalRoleResponse,
14
+ RemoveAppRoleResponse,
15
+ } from "@budibase/types"
10
16
 
11
- export async function fetch(ctx: Ctx) {
17
+ export async function fetch(ctx: Ctx<void, FetchGlobalRolesResponse>) {
12
18
  const tenantId = ctx.user!.tenantId
13
19
  // always use the dev apps as they'll be most up to date (true)
14
20
  const apps = (await dbCore.getAllApps({ tenantId, all: true })) as App[]
@@ -31,7 +37,7 @@ export async function fetch(ctx: Ctx) {
31
37
  ctx.body = response
32
38
  }
33
39
 
34
- export async function find(ctx: Ctx) {
40
+ export async function find(ctx: Ctx<void, FindGlobalRoleResponse>) {
35
41
  const appId = ctx.params.appId
36
42
  await context.doInAppContext(dbCore.getDevAppID(appId), async () => {
37
43
  const db = context.getAppDB()
@@ -45,7 +51,7 @@ export async function find(ctx: Ctx) {
45
51
  })
46
52
  }
47
53
 
48
- export async function removeAppRole(ctx: Ctx) {
54
+ export async function removeAppRole(ctx: Ctx<void, RemoveAppRoleResponse>) {
49
55
  const { appId } = ctx.params
50
56
  const db = tenancy.getGlobalDB()
51
57
  const users = await sdk.users.db.allUsers()
@@ -10,6 +10,11 @@ import {
10
10
  import env from "../../../environment"
11
11
  import { groups } from "@budibase/pro"
12
12
  import {
13
+ DevInfo,
14
+ FetchAPIKeyResponse,
15
+ GenerateAPIKeyRequest,
16
+ GenerateAPIKeyResponse,
17
+ GetGlobalSelfResponse,
13
18
  UpdateSelfRequest,
14
19
  UpdateSelfResponse,
15
20
  User,
@@ -35,22 +40,24 @@ function cleanupDevInfo(info: any) {
35
40
  return info
36
41
  }
37
42
 
38
- export async function generateAPIKey(ctx: any) {
43
+ export async function generateAPIKey(
44
+ ctx: UserCtx<GenerateAPIKeyRequest, GenerateAPIKeyResponse>
45
+ ) {
39
46
  let userId
40
47
  let apiKey
41
48
  if (env.isTest() && ctx.request.body.userId) {
42
49
  userId = ctx.request.body.userId
43
50
  apiKey = newTestApiKey()
44
51
  } else {
45
- userId = ctx.user._id
52
+ userId = ctx.user._id!
46
53
  apiKey = newApiKey()
47
54
  }
48
55
 
49
56
  const db = tenancy.getGlobalDB()
50
57
  const id = dbCore.generateDevInfoID(userId)
51
- let devInfo
58
+ let devInfo: DevInfo
52
59
  try {
53
- devInfo = await db.get<any>(id)
60
+ devInfo = await db.get<DevInfo>(id)
54
61
  } catch (err) {
55
62
  devInfo = { _id: id, userId }
56
63
  }
@@ -59,9 +66,9 @@ export async function generateAPIKey(ctx: any) {
59
66
  ctx.body = cleanupDevInfo(devInfo)
60
67
  }
61
68
 
62
- export async function fetchAPIKey(ctx: any) {
69
+ export async function fetchAPIKey(ctx: UserCtx<void, FetchAPIKeyResponse>) {
63
70
  const db = tenancy.getGlobalDB()
64
- const id = dbCore.generateDevInfoID(ctx.user._id)
71
+ const id = dbCore.generateDevInfoID(ctx.user._id!)
65
72
  let devInfo
66
73
  try {
67
74
  devInfo = await db.get(id)
@@ -87,11 +94,11 @@ const addSessionAttributesToUser = (ctx: any) => {
87
94
  ctx.body.csrfToken = ctx.user.csrfToken
88
95
  }
89
96
 
90
- export async function getSelf(ctx: any) {
97
+ export async function getSelf(ctx: UserCtx<void, GetGlobalSelfResponse>) {
91
98
  if (!ctx.user) {
92
99
  ctx.throw(403, "User not logged in")
93
100
  }
94
- const userId = ctx.user._id
101
+ const userId = ctx.user._id!
95
102
  ctx.params = {
96
103
  id: userId,
97
104
  }
@@ -3,10 +3,25 @@ import {
3
3
  TemplateBindings,
4
4
  GLOBAL_OWNER,
5
5
  } from "../../../constants"
6
- import { getTemplates } from "../../../constants/templates"
6
+ import { getTemplateByID, getTemplates } from "../../../constants/templates"
7
7
  import { tenancy, db as dbCore } from "@budibase/backend-core"
8
+ import {
9
+ DeleteGlobalTemplateResponse,
10
+ FetchGlobalTemplateByOwnerIDResponse,
11
+ FetchGlobalTemplateByTypeResponse,
12
+ FetchGlobalTemplateDefinitionResponse,
13
+ FetchGlobalTemplateResponse,
14
+ FindGlobalTemplateResponse,
15
+ SaveGlobalTemplateRequest,
16
+ SaveGlobalTemplateResponse,
17
+ GlobalTemplateBinding,
18
+ GlobalTemplateDefinition,
19
+ UserCtx,
20
+ } from "@budibase/types"
8
21
 
9
- export async function save(ctx: any) {
22
+ export async function save(
23
+ ctx: UserCtx<SaveGlobalTemplateRequest, SaveGlobalTemplateResponse>
24
+ ) {
10
25
  const db = tenancy.getGlobalDB()
11
26
  let template = ctx.request.body
12
27
  if (!template.ownerId) {
@@ -23,9 +38,11 @@ export async function save(ctx: any) {
23
38
  }
24
39
  }
25
40
 
26
- export async function definitions(ctx: any) {
27
- const bindings: any = {}
28
- const info: any = {}
41
+ export async function definitions(
42
+ ctx: UserCtx<void, FetchGlobalTemplateDefinitionResponse>
43
+ ) {
44
+ const bindings: Record<string, GlobalTemplateBinding[]> = {}
45
+ const info: Record<string, GlobalTemplateDefinition> = {}
29
46
  for (let template of TemplateMetadata.email) {
30
47
  bindings[template.purpose] = template.bindings
31
48
  info[template.purpose] = {
@@ -44,34 +61,35 @@ export async function definitions(ctx: any) {
44
61
  }
45
62
  }
46
63
 
47
- export async function fetch(ctx: any) {
64
+ export async function fetch(ctx: UserCtx<void, FetchGlobalTemplateResponse>) {
48
65
  ctx.body = await getTemplates()
49
66
  }
50
67
 
51
- export async function fetchByType(ctx: any) {
52
- // @ts-ignore
68
+ export async function fetchByType(
69
+ ctx: UserCtx<void, FetchGlobalTemplateByTypeResponse>
70
+ ) {
53
71
  ctx.body = await getTemplates({
54
72
  type: ctx.params.type,
55
73
  })
56
74
  }
57
75
 
58
- export async function fetchByOwner(ctx: any) {
76
+ export async function fetchByOwner(
77
+ ctx: UserCtx<void, FetchGlobalTemplateByOwnerIDResponse>
78
+ ) {
59
79
  // @ts-ignore
60
80
  ctx.body = await getTemplates({
61
81
  ownerId: ctx.params.ownerId,
62
82
  })
63
83
  }
64
84
 
65
- export async function find(ctx: any) {
66
- // @ts-ignore
67
- ctx.body = await getTemplates({
68
- id: ctx.params.id,
69
- })
85
+ export async function find(ctx: UserCtx<void, FindGlobalTemplateResponse>) {
86
+ ctx.body = await getTemplateByID(ctx.params.id)
70
87
  }
71
88
 
72
- export async function destroy(ctx: any) {
89
+ export async function destroy(
90
+ ctx: UserCtx<void, DeleteGlobalTemplateResponse>
91
+ ) {
73
92
  const db = tenancy.getGlobalDB()
74
93
  await db.remove(ctx.params.id, ctx.params.rev)
75
- ctx.message = `Template ${ctx.params.id} deleted.`
76
- ctx.status = 200
94
+ ctx.body = { message: `Template ${ctx.params.id} deleted.` }
77
95
  }
@@ -6,21 +6,34 @@ import {
6
6
  AddSSoUserRequest,
7
7
  BulkUserRequest,
8
8
  BulkUserResponse,
9
+ CheckInviteResponse,
10
+ CountUserResponse,
9
11
  CreateAdminUserRequest,
10
12
  CreateAdminUserResponse,
11
13
  Ctx,
12
14
  DeleteInviteUserRequest,
13
15
  DeleteInviteUsersRequest,
16
+ DeleteInviteUsersResponse,
17
+ DeleteUserResponse,
18
+ FetchUsersResponse,
19
+ FindUserResponse,
20
+ GetUserInvitesResponse,
14
21
  Hosting,
15
22
  InviteUserRequest,
23
+ InviteUserResponse,
16
24
  InviteUsersRequest,
17
25
  InviteUsersResponse,
18
26
  LockName,
19
27
  LockType,
28
+ LookupAccountHolderResponse,
29
+ LookupTenantUserResponse,
20
30
  MigrationType,
21
31
  PlatformUserByEmail,
22
32
  SaveUserResponse,
23
33
  SearchUsersRequest,
34
+ SearchUsersResponse,
35
+ UpdateInviteRequest,
36
+ UpdateInviteResponse,
24
37
  User,
25
38
  UserCtx,
26
39
  UserIdentifier,
@@ -80,7 +93,7 @@ export const save = async (ctx: UserCtx<User, SaveUserResponse>) => {
80
93
  }
81
94
  }
82
95
 
83
- export const addSsoSupport = async (ctx: Ctx<AddSSoUserRequest>) => {
96
+ export const addSsoSupport = async (ctx: Ctx<AddSSoUserRequest, void>) => {
84
97
  const { email, ssoId } = ctx.request.body
85
98
  try {
86
99
  // Status is changed to 404 from getUserDoc if user is not found
@@ -207,7 +220,7 @@ export const adminUser = async (
207
220
  })
208
221
  }
209
222
 
210
- export const countByApp = async (ctx: any) => {
223
+ export const countByApp = async (ctx: UserCtx<void, CountUserResponse>) => {
211
224
  const appId = ctx.params.appId
212
225
  try {
213
226
  ctx.body = await userSdk.db.countUsersByApp(appId)
@@ -216,7 +229,7 @@ export const countByApp = async (ctx: any) => {
216
229
  }
217
230
  }
218
231
 
219
- export const destroy = async (ctx: any) => {
232
+ export const destroy = async (ctx: UserCtx<void, DeleteUserResponse>) => {
220
233
  const id = ctx.params.id
221
234
  if (id === ctx.user._id) {
222
235
  ctx.throw(400, "Unable to delete self.")
@@ -239,7 +252,9 @@ export const getAppUsers = async (ctx: Ctx<SearchUsersRequest>) => {
239
252
  ctx.body = { data: users }
240
253
  }
241
254
 
242
- export const search = async (ctx: Ctx<SearchUsersRequest>) => {
255
+ export const search = async (
256
+ ctx: Ctx<SearchUsersRequest, SearchUsersResponse>
257
+ ) => {
243
258
  const body = ctx.request.body
244
259
 
245
260
  // TODO: for now only two supported search keys; string.email and equal._id
@@ -280,7 +295,7 @@ export const search = async (ctx: Ctx<SearchUsersRequest>) => {
280
295
  }
281
296
 
282
297
  // called internally by app server user fetch
283
- export const fetch = async (ctx: any) => {
298
+ export const fetch = async (ctx: UserCtx<void, FetchUsersResponse>) => {
284
299
  const all = await userSdk.db.allUsers()
285
300
  // user hashed password shouldn't ever be returned
286
301
  for (let user of all) {
@@ -292,11 +307,13 @@ export const fetch = async (ctx: any) => {
292
307
  }
293
308
 
294
309
  // called internally by app server user find
295
- export const find = async (ctx: any) => {
310
+ export const find = async (ctx: UserCtx<void, FindUserResponse>) => {
296
311
  ctx.body = await userSdk.db.getUser(ctx.params.id)
297
312
  }
298
313
 
299
- export const tenantUserLookup = async (ctx: any) => {
314
+ export const tenantUserLookup = async (
315
+ ctx: UserCtx<void, LookupTenantUserResponse>
316
+ ) => {
300
317
  const id = ctx.params.id
301
318
  // is email, check its valid
302
319
  if (id.includes("@") && !emailValidator.validate(id)) {
@@ -314,7 +331,9 @@ export const tenantUserLookup = async (ctx: any) => {
314
331
  * This will be paginated to a default of the first 50 users,
315
332
  * So the account holder may not be found until further pagination has occurred
316
333
  */
317
- export const accountHolderLookup = async (ctx: Ctx) => {
334
+ export const accountHolderLookup = async (
335
+ ctx: Ctx<void, LookupAccountHolderResponse>
336
+ ) => {
318
337
  try {
319
338
  const users = await userSdk.core.getAllUsers()
320
339
  const response = await userSdk.core.getExistingAccounts(
@@ -366,7 +385,9 @@ export const onboardUsers = async (
366
385
  ctx.body = { ...resp, created: true }
367
386
  }
368
387
 
369
- export const invite = async (ctx: Ctx<InviteUserRequest>) => {
388
+ export const invite = async (
389
+ ctx: Ctx<InviteUserRequest, InviteUserResponse>
390
+ ) => {
370
391
  const request = ctx.request.body
371
392
 
372
393
  let multiRequest = [request]
@@ -389,12 +410,14 @@ export const invite = async (ctx: Ctx<InviteUserRequest>) => {
389
410
  }
390
411
  }
391
412
 
392
- export const inviteMultiple = async (ctx: Ctx<InviteUsersRequest>) => {
413
+ export const inviteMultiple = async (
414
+ ctx: Ctx<InviteUsersRequest, InviteUsersResponse>
415
+ ) => {
393
416
  ctx.body = await userSdk.invite(ctx.request.body)
394
417
  }
395
418
 
396
419
  export const removeMultipleInvites = async (
397
- ctx: Ctx<DeleteInviteUsersRequest>
420
+ ctx: Ctx<DeleteInviteUsersRequest, DeleteInviteUsersResponse>
398
421
  ) => {
399
422
  const inviteCodesToRemove = ctx.request.body.map(
400
423
  (invite: DeleteInviteUserRequest) => invite.code
@@ -407,7 +430,7 @@ export const removeMultipleInvites = async (
407
430
  }
408
431
  }
409
432
 
410
- export const checkInvite = async (ctx: any) => {
433
+ export const checkInvite = async (ctx: UserCtx<void, CheckInviteResponse>) => {
411
434
  const { code } = ctx.params
412
435
  let invite
413
436
  try {
@@ -422,7 +445,9 @@ export const checkInvite = async (ctx: any) => {
422
445
  }
423
446
  }
424
447
 
425
- export const getUserInvites = async (ctx: any) => {
448
+ export const getUserInvites = async (
449
+ ctx: UserCtx<void, GetUserInvitesResponse>
450
+ ) => {
426
451
  try {
427
452
  // Restricted to the currently authenticated tenant
428
453
  ctx.body = await cache.invite.getInviteCodes()
@@ -431,7 +456,9 @@ export const getUserInvites = async (ctx: any) => {
431
456
  }
432
457
  }
433
458
 
434
- export const updateInvite = async (ctx: any) => {
459
+ export const updateInvite = async (
460
+ ctx: UserCtx<UpdateInviteRequest, UpdateInviteResponse>
461
+ ) => {
435
462
  const { code } = ctx.params
436
463
  let updateBody = { ...ctx.request.body }
437
464
 
@@ -1,7 +1,15 @@
1
- import { Account, AccountMetadata, Ctx } from "@budibase/types"
1
+ import {
2
+ Account,
3
+ AccountMetadata,
4
+ Ctx,
5
+ SaveAccountRequest,
6
+ SaveAccountResponse,
7
+ } from "@budibase/types"
2
8
  import * as accounts from "../../../sdk/accounts"
3
9
 
4
- export const save = async (ctx: Ctx<Account, AccountMetadata>) => {
10
+ export const save = async (
11
+ ctx: Ctx<SaveAccountRequest, SaveAccountResponse>
12
+ ) => {
5
13
  const account = ctx.request.body as Account
6
14
  let metadata: AccountMetadata = {
7
15
  _id: accounts.metadata.formatAccountMetadataId(account.accountId),
@@ -14,7 +22,7 @@ export const save = async (ctx: Ctx<Account, AccountMetadata>) => {
14
22
  ctx.status = 200
15
23
  }
16
24
 
17
- export const destroy = async (ctx: any) => {
25
+ export const destroy = async (ctx: Ctx<void, void>) => {
18
26
  const accountId = accounts.metadata.formatAccountMetadataId(
19
27
  ctx.params.accountId
20
28
  )
@@ -1,4 +1,4 @@
1
- import { Ctx, MaintenanceType } from "@budibase/types"
1
+ import { Ctx, GetEnvironmentResponse, MaintenanceType } from "@budibase/types"
2
2
  import env from "../../../environment"
3
3
  import { env as coreEnv, db as dbCore } from "@budibase/backend-core"
4
4
  import nodeFetch from "node-fetch"
@@ -38,13 +38,13 @@ async function isSqsMissing() {
38
38
  return !(await isSqsAvailable())
39
39
  }
40
40
 
41
- export const fetch = async (ctx: Ctx) => {
41
+ export const fetch = async (ctx: Ctx<void, GetEnvironmentResponse>) => {
42
42
  ctx.body = {
43
43
  multiTenancy: !!env.MULTI_TENANCY,
44
44
  offlineMode: !!coreEnv.OFFLINE_MODE,
45
45
  cloud: !env.SELF_HOSTED,
46
46
  accountPortalUrl: env.ACCOUNT_PORTAL_URL,
47
- disableAccountPortal: env.DISABLE_ACCOUNT_PORTAL,
47
+ disableAccountPortal: !!env.DISABLE_ACCOUNT_PORTAL,
48
48
  baseUrl: env.PLATFORM_URL,
49
49
  isDev: env.isDev() && !env.isTest(),
50
50
  maintenance: [],
@@ -1,7 +1,7 @@
1
- import { UserCtx } from "@budibase/types"
1
+ import { GetLogResponse, UserCtx } from "@budibase/types"
2
2
  import { installation, logging } from "@budibase/backend-core"
3
3
 
4
- export async function getLogs(ctx: UserCtx) {
4
+ export async function getLogs(ctx: UserCtx<void, GetLogResponse>) {
5
5
  const logReadStream = logging.system.getLogReadStream()
6
6
 
7
7
  const { installId } = await installation.getInstall()
@@ -1,13 +1,23 @@
1
+ import {
2
+ FetchMigrationDefinitionsResponse,
3
+ RunGlobalMigrationRequest,
4
+ UserCtx,
5
+ } from "@budibase/types"
6
+
1
7
  const { migrate, MIGRATIONS } = require("../../../migrations")
2
8
 
3
- export const runMigrations = async (ctx: any) => {
9
+ export const runMigrations = async (
10
+ ctx: UserCtx<RunGlobalMigrationRequest, void>
11
+ ) => {
4
12
  const options = ctx.request.body
5
13
  // don't await as can take a while, just return
6
14
  migrate(options)
7
15
  ctx.status = 200
8
16
  }
9
17
 
10
- export const fetchDefinitions = async (ctx: any) => {
18
+ export const fetchDefinitions = async (
19
+ ctx: UserCtx<void, FetchMigrationDefinitionsResponse>
20
+ ) => {
11
21
  ctx.body = MIGRATIONS
12
22
  ctx.status = 200
13
23
  }
@@ -1,8 +1,10 @@
1
1
  import env from "../../../environment"
2
- import { BBContext } from "@budibase/types"
2
+ import { SystemRestoreResponse, UserCtx } from "@budibase/types"
3
3
  import { cache } from "@budibase/backend-core"
4
4
 
5
- export async function systemRestored(ctx: BBContext) {
5
+ export async function systemRestored(
6
+ ctx: UserCtx<void, SystemRestoreResponse>
7
+ ) {
6
8
  if (!env.SELF_HOSTED) {
7
9
  ctx.throw(405, "This operation is not allowed in cloud.")
8
10
  }
@@ -1,7 +1,7 @@
1
- import { UserCtx } from "@budibase/types"
1
+ import { GetTenantInfoResponse, UserCtx } from "@budibase/types"
2
2
  import * as tenantSdk from "../../../sdk/tenants"
3
3
 
4
- export async function destroy(ctx: UserCtx) {
4
+ export async function destroy(ctx: UserCtx<void, void>) {
5
5
  const user = ctx.user!
6
6
  const tenantId = ctx.params.tenantId
7
7
 
@@ -18,6 +18,6 @@ export async function destroy(ctx: UserCtx) {
18
18
  }
19
19
  }
20
20
 
21
- export async function info(ctx: UserCtx) {
21
+ export async function info(ctx: UserCtx<void, GetTenantInfoResponse>) {
22
22
  ctx.body = await tenantSdk.tenantInfo(ctx.params.tenantId)
23
23
  }
@@ -1,7 +1,7 @@
1
1
  import Router from "@koa/router"
2
2
  import * as controller from "../../controllers/global/email"
3
- import { EmailTemplatePurpose } from "../../../constants"
4
3
  import { auth } from "@budibase/backend-core"
4
+ import { EmailTemplatePurpose } from "@budibase/types"
5
5
  import Joi from "joi"
6
6
 
7
7
  const router: Router = new Router()
@@ -1,8 +1,8 @@
1
1
  jest.mock("nodemailer")
2
+ import { EmailTemplatePurpose } from "@budibase/types"
2
3
  import { TestConfiguration, mocks } from "../../../../tests"
3
4
 
4
5
  const sendMailMock = mocks.email.mock()
5
- import { EmailTemplatePurpose } from "../../../../constants"
6
6
 
7
7
  describe("/api/global/email", () => {
8
8
  const config = new TestConfiguration()
@@ -1,11 +1,10 @@
1
1
  jest.unmock("node-fetch")
2
2
  import { TestConfiguration } from "../../../../tests"
3
- import { EmailTemplatePurpose } from "../../../../constants"
4
3
  import { objectStore } from "@budibase/backend-core"
5
4
  import { helpers } from "@budibase/shared-core"
6
5
 
7
6
  import tk from "timekeeper"
8
- import { EmailAttachment } from "@budibase/types"
7
+ import { EmailAttachment, EmailTemplatePurpose } from "@budibase/types"
9
8
 
10
9
  const fetch = require("node-fetch")
11
10
 
@@ -1,9 +1,6 @@
1
- import {
2
- EmailTemplatePurpose,
3
- TemplateMetadata,
4
- TemplateType,
5
- } from "../../../../constants"
1
+ import { TemplateMetadata, TemplateType } from "../../../../constants"
6
2
  import { TestConfiguration } from "../../../../tests"
3
+ import { EmailTemplatePurpose } from "@budibase/types"
7
4
 
8
5
  // TODO
9
6
 
@@ -2,7 +2,6 @@ import Router from "@koa/router"
2
2
  import { api as pro } from "@budibase/pro"
3
3
  import userRoutes from "./global/users"
4
4
  import configRoutes from "./global/configs"
5
- import workspaceRoutes from "./global/workspaces"
6
5
  import templateRoutes from "./global/templates"
7
6
  import emailRoutes from "./global/email"
8
7
  import authRoutes from "./global/auth"
@@ -24,7 +23,6 @@ export const routes: Router[] = [
24
23
  configRoutes,
25
24
  userRoutes,
26
25
  pro.users,
27
- workspaceRoutes,
28
26
  authRoutes,
29
27
  templateRoutes,
30
28
  tenantsRoutes,
@@ -22,7 +22,7 @@ describe("/api/system/environment", () => {
22
22
  const env = await config.api.environment.getEnvironment()
23
23
  expect(env.body).toEqual({
24
24
  cloud: true,
25
- disableAccountPortal: 0,
25
+ disableAccountPortal: false,
26
26
  isDev: false,
27
27
  multiTenancy: true,
28
28
  baseUrl: "http://localhost:10000",
@@ -36,7 +36,7 @@ describe("/api/system/environment", () => {
36
36
  const env = await config.api.environment.getEnvironment()
37
37
  expect(env.body).toEqual({
38
38
  cloud: false,
39
- disableAccountPortal: 0,
39
+ disableAccountPortal: false,
40
40
  isDev: false,
41
41
  multiTenancy: true,
42
42
  baseUrl: "http://localhost:10000",
@@ -1,4 +1,5 @@
1
1
  import { constants } from "@budibase/backend-core"
2
+ import { EmailTemplatePurpose } from "@budibase/types"
2
3
 
3
4
  export const LOGO_URL =
4
5
  "https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg"
@@ -19,15 +20,6 @@ export enum TemplateType {
19
20
  EMAIL = "email",
20
21
  }
21
22
 
22
- export enum EmailTemplatePurpose {
23
- CORE = "core",
24
- BASE = "base",
25
- PASSWORD_RECOVERY = "password_recovery",
26
- INVITATION = "invitation",
27
- WELCOME = "welcome",
28
- CUSTOM = "custom",
29
- }
30
-
31
23
  export enum TemplateMetadataNames {
32
24
  BASE = "Base format",
33
25
  PASSWORD_RECOVERY = "Password recovery",
@@ -1,13 +1,8 @@
1
1
  import { readStaticFile } from "../../utilities/fileSystem"
2
- import {
3
- EmailTemplatePurpose,
4
- TemplateType,
5
- TemplatePurpose,
6
- GLOBAL_OWNER,
7
- } from "../index"
2
+ import { TemplateType, TemplatePurpose, GLOBAL_OWNER } from "../index"
8
3
  import { join } from "path"
9
4
  import { db as dbCore, tenancy } from "@budibase/backend-core"
10
- import { Template } from "@budibase/types"
5
+ import { Template, EmailTemplatePurpose } from "@budibase/types"
11
6
 
12
7
  export const EmailTemplates = {
13
8
  [EmailTemplatePurpose.PASSWORD_RECOVERY]: readStaticFile(
@@ -53,26 +48,33 @@ export function addBaseTemplates(templates: Template[], type?: string) {
53
48
  export async function getTemplates({
54
49
  ownerId,
55
50
  type,
56
- id,
57
- }: { ownerId?: string; type?: string; id?: string } = {}) {
51
+ }: { ownerId?: string; type?: string } = {}) {
58
52
  const db = tenancy.getGlobalDB()
59
53
  const response = await db.allDocs<Template>(
60
- dbCore.getTemplateParams(ownerId || GLOBAL_OWNER, id, {
54
+ dbCore.getTemplateParams(ownerId || GLOBAL_OWNER, undefined, {
61
55
  include_docs: true,
62
56
  })
63
57
  )
64
58
  let templates = response.rows.map(row => row.doc!)
65
- // should only be one template with ID
66
- if (id) {
67
- return templates[0]
68
- }
69
59
  if (type) {
70
60
  templates = templates.filter(template => template.type === type)
71
61
  }
72
62
  return addBaseTemplates(templates, type)
73
63
  }
74
64
 
65
+ export async function getTemplateByID(id: string, ownerId?: string) {
66
+ const db = tenancy.getGlobalDB()
67
+ const response = await db.allDocs<Template>(
68
+ dbCore.getTemplateParams(ownerId || GLOBAL_OWNER, id, {
69
+ include_docs: true,
70
+ })
71
+ )
72
+ let templates = response.rows.map(row => row.doc!)
73
+ // should only be one template with ID
74
+ return templates[0]
75
+ }
76
+
75
77
  export async function getTemplateByPurpose(type: string, purpose: string) {
76
- const templates = (await getTemplates({ type })) as Template[]
78
+ const templates = await getTemplates({ type })
77
79
  return templates.find((template: Template) => template.purpose === purpose)
78
80
  }
@@ -1,12 +1,12 @@
1
1
  import env from "../environment"
2
2
  import { constants, utils } from "@budibase/backend-core"
3
- import { BBContext } from "@budibase/types"
3
+ import { UserCtx } from "@budibase/types"
4
4
 
5
5
  /**
6
6
  * This is a restricted endpoint in the cloud.
7
7
  * Ensure that the correct API key has been supplied.
8
8
  */
9
- export default async (ctx: BBContext, next: any) => {
9
+ export default async (ctx: UserCtx, next: any) => {
10
10
  if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
11
11
  const apiKey = ctx.request.headers[constants.Header.API_KEY]
12
12
  if (!apiKey) {
@@ -8,11 +8,10 @@ import {
8
8
  utils as coreUtils,
9
9
  cache,
10
10
  } from "@budibase/backend-core"
11
- import { PlatformLogoutOpts, User } from "@budibase/types"
11
+ import { PlatformLogoutOpts, User, EmailTemplatePurpose } from "@budibase/types"
12
12
  import jwt from "jsonwebtoken"
13
13
  import * as userSdk from "../users"
14
14
  import * as emails from "../../utilities/email"
15
- import { EmailTemplatePurpose } from "../../constants"
16
15
 
17
16
  // LOGIN / LOGOUT
18
17
 
@@ -3,9 +3,9 @@ import {
3
3
  InviteUserRequest,
4
4
  InviteUsersRequest,
5
5
  InviteUsersResponse,
6
+ EmailTemplatePurpose,
6
7
  } from "@budibase/types"
7
8
  import { sendEmail } from "../../utilities/email"
8
- import { EmailTemplatePurpose } from "../../constants"
9
9
 
10
10
  export async function invite(
11
11
  users: InviteUsersRequest
@@ -1,5 +1,4 @@
1
1
  export * as email from "../api/controllers/global/email"
2
- export * as workspaces from "../api/controllers/global/workspaces"
3
2
  export * as config from "../api/controllers/global/configs"
4
3
  export * as templates from "../api/controllers/global/templates"
5
4
  export * as users from "../api/controllers/global/users"
@@ -1,9 +1,14 @@
1
1
  import env from "../environment"
2
- import { EmailTemplatePurpose, TemplateType } from "../constants"
2
+ import { TemplateType } from "../constants"
3
3
  import { getTemplateByPurpose, EmailTemplates } from "../constants/templates"
4
4
  import { getSettingsTemplateContext } from "./templates"
5
5
  import { processString } from "@budibase/string-templates"
6
- import { User, SendEmailOpts, SMTPInnerConfig } from "@budibase/types"
6
+ import {
7
+ User,
8
+ SendEmailOpts,
9
+ SMTPInnerConfig,
10
+ EmailTemplatePurpose,
11
+ } from "@budibase/types"
7
12
  import { configs, cache, objectStore } from "@budibase/backend-core"
8
13
  import ical from "ical-generator"
9
14
  import _ from "lodash"
@@ -1,9 +1,6 @@
1
1
  import { tenancy, configs } from "@budibase/backend-core"
2
- import {
3
- InternalTemplateBinding,
4
- LOGO_URL,
5
- EmailTemplatePurpose,
6
- } from "../constants"
2
+ import { EmailTemplatePurpose } from "@budibase/types"
3
+ import { InternalTemplateBinding, LOGO_URL } from "../constants"
7
4
  import { checkSlashesInUrl } from "./index"
8
5
 
9
6
  const BASE_COMPANY = "Budibase"
@@ -1,53 +0,0 @@
1
- import { tenancy, db as dbCore } from "@budibase/backend-core"
2
- import { BBContext } from "@budibase/types"
3
-
4
- export async function save(ctx: BBContext) {
5
- const db = tenancy.getGlobalDB()
6
- const workspaceDoc = ctx.request.body
7
-
8
- // workspace does not exist yet
9
- if (!workspaceDoc._id) {
10
- workspaceDoc._id = dbCore.generateWorkspaceID()
11
- }
12
-
13
- try {
14
- const response = await db.put(workspaceDoc)
15
- ctx.body = {
16
- _id: response.id,
17
- _rev: response.rev,
18
- }
19
- } catch (err: any) {
20
- ctx.throw(err.status, err)
21
- }
22
- }
23
-
24
- export async function fetch(ctx: BBContext) {
25
- const db = tenancy.getGlobalDB()
26
- const response = await db.allDocs(
27
- dbCore.getWorkspaceParams(undefined, {
28
- include_docs: true,
29
- })
30
- )
31
- ctx.body = response.rows.map(row => row.doc)
32
- }
33
-
34
- export async function find(ctx: BBContext) {
35
- const db = tenancy.getGlobalDB()
36
- try {
37
- ctx.body = await db.get(ctx.params.id)
38
- } catch (err: any) {
39
- ctx.throw(err.status, err)
40
- }
41
- }
42
-
43
- export async function destroy(ctx: BBContext) {
44
- const db = tenancy.getGlobalDB()
45
- const { id, rev } = ctx.params
46
-
47
- try {
48
- await db.remove(id, rev)
49
- ctx.body = { message: "Workspace deleted successfully" }
50
- } catch (err: any) {
51
- ctx.throw(err.status, err)
52
- }
53
- }
@@ -1,37 +0,0 @@
1
- import Router from "@koa/router"
2
- import * as controller from "../../controllers/global/workspaces"
3
- import { auth } from "@budibase/backend-core"
4
- import Joi from "joi"
5
-
6
- const router: Router = new Router()
7
-
8
- function buildWorkspaceSaveValidation() {
9
- // prettier-ignore
10
- return auth.joiValidator.body(Joi.object({
11
- _id: Joi.string().optional(),
12
- _rev: Joi.string().optional(),
13
- name: Joi.string().required(),
14
- users: Joi.array().required(),
15
- managers: Joi.array().required(),
16
- roles: Joi.object({
17
- default: Joi.string().optional(),
18
- app: Joi.object()
19
- .pattern(/.*/, Joi.string())
20
- .required()
21
- .unknown(true),
22
- }).unknown(true).optional(),
23
- }).required().unknown(true))
24
- }
25
-
26
- router
27
- .post(
28
- "/api/global/workspaces",
29
- auth.adminOnly,
30
- buildWorkspaceSaveValidation(),
31
- controller.save
32
- )
33
- .delete("/api/global/workspaces/:id", auth.adminOnly, controller.destroy)
34
- .get("/api/global/workspaces", controller.fetch)
35
- .get("/api/global/workspaces/:id", controller.find)
36
-
37
- export default router