@budibase/worker 1.0.105-alpha.4 → 1.0.105-alpha.42

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.
package/nodemon.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "watch": ["src", "../backend-core"],
2
+ "watch": ["src", "../backend-core", "../../../budibase-pro/packages/pro"],
3
3
  "ext": "js,ts,json",
4
4
  "ignore": ["src/**/*.spec.ts", "src/**/*.spec.js"],
5
5
  "exec": "ts-node src/index.ts"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/worker",
3
3
  "email": "hi@budibase.com",
4
- "version": "1.0.105-alpha.4",
4
+ "version": "1.0.105-alpha.42",
5
5
  "description": "Budibase background service",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -31,14 +31,17 @@
31
31
  "author": "Budibase",
32
32
  "license": "GPL-3.0",
33
33
  "dependencies": {
34
- "@budibase/backend-core": "^1.0.105-alpha.4",
35
- "@budibase/string-templates": "^1.0.105-alpha.4",
34
+ "@budibase/backend-core": "^1.0.105-alpha.42",
35
+ "@budibase/pro": "1.0.105-alpha.41",
36
+ "@budibase/string-templates": "^1.0.105-alpha.42",
36
37
  "@koa/router": "^8.0.0",
37
- "@sentry/node": "^6.0.0",
38
+ "@sentry/node": "6.17.7",
38
39
  "@techpass/passport-openidconnect": "^0.3.0",
40
+ "@types/global-agent": "^2.1.1",
39
41
  "aws-sdk": "^2.811.0",
40
42
  "bcryptjs": "^2.4.3",
41
43
  "dotenv": "^8.2.0",
44
+ "global-agent": "^3.0.0",
42
45
  "got": "^11.8.1",
43
46
  "joi": "^17.4.0",
44
47
  "koa": "^2.7.0",
@@ -63,7 +66,9 @@
63
66
  "@types/jest": "^26.0.23",
64
67
  "@types/koa": "^2.13.3",
65
68
  "@types/koa-router": "^7.4.2",
69
+ "@types/koa__router": "^8.0.11",
66
70
  "@types/node": "^15.12.4",
71
+ "@typescript-eslint/parser": "5.12.0",
67
72
  "copyfiles": "^2.4.1",
68
73
  "eslint": "^6.8.0",
69
74
  "jest": "^27.0.5",
@@ -74,7 +79,7 @@
74
79
  "supertest": "^6.1.3",
75
80
  "ts-jest": "^27.0.3",
76
81
  "ts-node": "^10.0.0",
77
- "typescript": "4.3.5",
82
+ "typescript": "4.5.5",
78
83
  "update-dotenv": "^1.1.1"
79
84
  },
80
85
  "jest": {
@@ -84,5 +89,5 @@
84
89
  "./scripts/jestSetup.js"
85
90
  ]
86
91
  },
87
- "gitHead": "13d4ab9d95397b1b002fff436a429320f22a5ab4"
92
+ "gitHead": "7b5535822573344f0fa91bf1910349548d26e958"
88
93
  }
@@ -21,8 +21,9 @@ const {
21
21
  isMultiTenant,
22
22
  } = require("@budibase/backend-core/tenancy")
23
23
  const env = require("../../../environment")
24
+ import { users } from "@budibase/pro"
24
25
 
25
- const ssoCallbackUrl = async (config, type) => {
26
+ const ssoCallbackUrl = async (config: any, type: any) => {
26
27
  // incase there is a callback URL from before
27
28
  if (config && config.callbackURL) {
28
29
  return config.callbackURL
@@ -42,15 +43,15 @@ const ssoCallbackUrl = async (config, type) => {
42
43
  return `${publicConfig.platformUrl}${callbackUrl}`
43
44
  }
44
45
 
45
- exports.googleCallbackUrl = async config => {
46
+ export const googleCallbackUrl = async (config: any) => {
46
47
  return ssoCallbackUrl(config, "google")
47
48
  }
48
49
 
49
- exports.oidcCallbackUrl = async config => {
50
+ export const oidcCallbackUrl = async (config: any) => {
50
51
  return ssoCallbackUrl(config, "oidc")
51
52
  }
52
53
 
53
- async function authInternal(ctx, user, err = null, info = null) {
54
+ async function authInternal(ctx: any, user: any, err = null, info = null) {
54
55
  if (err) {
55
56
  console.error("Authentication error", err)
56
57
  return ctx.throw(403, info ? info : "Unauthorized")
@@ -71,20 +72,23 @@ async function authInternal(ctx, user, err = null, info = null) {
71
72
  }
72
73
  }
73
74
 
74
- exports.authenticate = async (ctx, next) => {
75
- return passport.authenticate("local", async (err, user, info) => {
76
- await authInternal(ctx, user, err, info)
77
- ctx.status = 200
78
- })(ctx, next)
75
+ export const authenticate = async (ctx: any, next: any) => {
76
+ return passport.authenticate(
77
+ "local",
78
+ async (err: any, user: any, info: any) => {
79
+ await authInternal(ctx, user, err, info)
80
+ ctx.status = 200
81
+ }
82
+ )(ctx, next)
79
83
  }
80
84
 
81
- exports.setInitInfo = ctx => {
85
+ export const setInitInfo = (ctx: any) => {
82
86
  const initInfo = ctx.request.body
83
87
  setCookie(ctx, initInfo, Cookies.Init)
84
88
  ctx.status = 200
85
89
  }
86
90
 
87
- exports.getInitInfo = ctx => {
91
+ export const getInitInfo = (ctx: any) => {
88
92
  try {
89
93
  ctx.body = getCookie(ctx, Cookies.Init) || {}
90
94
  } catch (err) {
@@ -96,7 +100,7 @@ exports.getInitInfo = ctx => {
96
100
  /**
97
101
  * Reset the user password, used as part of a forgotten password flow.
98
102
  */
99
- exports.reset = async ctx => {
103
+ export const reset = async (ctx: any) => {
100
104
  const { email } = ctx.request.body
101
105
  const configured = await isEmailConfigured()
102
106
  if (!configured) {
@@ -126,7 +130,7 @@ exports.reset = async ctx => {
126
130
  /**
127
131
  * Perform the user password update if the provided reset code is valid.
128
132
  */
129
- exports.resetUpdate = async ctx => {
133
+ export const resetUpdate = async (ctx: any) => {
130
134
  const { resetCode, password } = ctx.request.body
131
135
  try {
132
136
  const { userId } = await checkResetPasswordCode(resetCode)
@@ -142,14 +146,14 @@ exports.resetUpdate = async ctx => {
142
146
  }
143
147
  }
144
148
 
145
- exports.logout = async ctx => {
149
+ export const logout = async (ctx: any) => {
146
150
  if (ctx.user && ctx.user._id) {
147
151
  await platformLogout({ ctx, userId: ctx.user._id })
148
152
  }
149
153
  ctx.body = { message: "User logged out." }
150
154
  }
151
155
 
152
- exports.datasourcePreAuth = async (ctx, next) => {
156
+ export const datasourcePreAuth = async (ctx: any, next: any) => {
153
157
  const provider = ctx.params.provider
154
158
  const middleware = require(`@budibase/backend-core/middleware`)
155
159
  const handler = middleware.datasource[provider]
@@ -167,7 +171,7 @@ exports.datasourcePreAuth = async (ctx, next) => {
167
171
  return handler.preAuth(passport, ctx, next)
168
172
  }
169
173
 
170
- exports.datasourceAuth = async (ctx, next) => {
174
+ export const datasourceAuth = async (ctx: any, next: any) => {
171
175
  const authStateCookie = getCookie(ctx, Cookies.DatasourceAuth)
172
176
  const provider = authStateCookie.provider
173
177
  const middleware = require(`@budibase/backend-core/middleware`)
@@ -179,7 +183,7 @@ exports.datasourceAuth = async (ctx, next) => {
179
183
  * The initial call that google authentication makes to take you to the google login screen.
180
184
  * On a successful login, you will be redirected to the googleAuth callback route.
181
185
  */
182
- exports.googlePreAuth = async (ctx, next) => {
186
+ export const googlePreAuth = async (ctx: any, next: any) => {
183
187
  const db = getGlobalDB()
184
188
 
185
189
  const config = await core.db.getScopedConfig(db, {
@@ -187,14 +191,14 @@ exports.googlePreAuth = async (ctx, next) => {
187
191
  workspace: ctx.query.workspace,
188
192
  })
189
193
  let callbackUrl = await exports.googleCallbackUrl(config)
190
- const strategy = await google.strategyFactory(config, callbackUrl)
194
+ const strategy = await google.strategyFactory(config, callbackUrl, users.save)
191
195
 
192
196
  return passport.authenticate(strategy, {
193
197
  scope: ["profile", "email"],
194
198
  })(ctx, next)
195
199
  }
196
200
 
197
- exports.googleAuth = async (ctx, next) => {
201
+ export const googleAuth = async (ctx: any, next: any) => {
198
202
  const db = getGlobalDB()
199
203
 
200
204
  const config = await core.db.getScopedConfig(db, {
@@ -202,12 +206,12 @@ exports.googleAuth = async (ctx, next) => {
202
206
  workspace: ctx.query.workspace,
203
207
  })
204
208
  const callbackUrl = await exports.googleCallbackUrl(config)
205
- const strategy = await google.strategyFactory(config, callbackUrl)
209
+ const strategy = await google.strategyFactory(config, callbackUrl, users.save)
206
210
 
207
211
  return passport.authenticate(
208
212
  strategy,
209
213
  { successRedirect: "/", failureRedirect: "/error" },
210
- async (err, user, info) => {
214
+ async (err: any, user: any, info: any) => {
211
215
  await authInternal(ctx, user, err, info)
212
216
 
213
217
  ctx.redirect("/")
@@ -215,24 +219,24 @@ exports.googleAuth = async (ctx, next) => {
215
219
  )(ctx, next)
216
220
  }
217
221
 
218
- async function oidcStrategyFactory(ctx, configId) {
222
+ async function oidcStrategyFactory(ctx: any, configId: any) {
219
223
  const db = getGlobalDB()
220
224
  const config = await core.db.getScopedConfig(db, {
221
225
  type: Configs.OIDC,
222
226
  group: ctx.query.group,
223
227
  })
224
228
 
225
- const chosenConfig = config.configs.filter(c => c.uuid === configId)[0]
229
+ const chosenConfig = config.configs.filter((c: any) => c.uuid === configId)[0]
226
230
  let callbackUrl = await exports.oidcCallbackUrl(chosenConfig)
227
231
 
228
- return oidc.strategyFactory(chosenConfig, callbackUrl)
232
+ return oidc.strategyFactory(chosenConfig, callbackUrl, users.save)
229
233
  }
230
234
 
231
235
  /**
232
236
  * The initial call that OIDC authentication makes to take you to the configured OIDC login screen.
233
237
  * On a successful login, you will be redirected to the oidcAuth callback route.
234
238
  */
235
- exports.oidcPreAuth = async (ctx, next) => {
239
+ export const oidcPreAuth = async (ctx: any, next: any) => {
236
240
  const { configId } = ctx.params
237
241
  const strategy = await oidcStrategyFactory(ctx, configId)
238
242
 
@@ -244,14 +248,14 @@ exports.oidcPreAuth = async (ctx, next) => {
244
248
  })(ctx, next)
245
249
  }
246
250
 
247
- exports.oidcAuth = async (ctx, next) => {
251
+ export const oidcAuth = async (ctx: any, next: any) => {
248
252
  const configId = getCookie(ctx, Cookies.OIDC_CONFIG)
249
253
  const strategy = await oidcStrategyFactory(ctx, configId)
250
254
 
251
255
  return passport.authenticate(
252
256
  strategy,
253
257
  { successRedirect: "/", failureRedirect: "/error" },
254
- async (err, user, info) => {
258
+ async (err: any, user: any, info: any) => {
255
259
  await authInternal(ctx, user, err, info)
256
260
 
257
261
  ctx.redirect("/")
@@ -0,0 +1,30 @@
1
+ import { licensing, quotas } from "@budibase/pro"
2
+
3
+ export const activate = async (ctx: any) => {
4
+ const { licenseKey } = ctx.request.body
5
+ if (!licenseKey) {
6
+ ctx.throw(400, "licenseKey is required")
7
+ }
8
+
9
+ await licensing.activateLicenseKey(licenseKey)
10
+ ctx.status = 200
11
+ }
12
+
13
+ export const refresh = async (ctx: any) => {
14
+ await licensing.cache.refresh()
15
+ ctx.status = 200
16
+ }
17
+
18
+ export const getInfo = async (ctx: any) => {
19
+ const licenseInfo = await licensing.getLicenseInfo()
20
+ if (licenseInfo) {
21
+ licenseInfo.licenseKey = "*"
22
+ ctx.body = licenseInfo
23
+ }
24
+ ctx.status = 200
25
+ }
26
+
27
+ export const getQuotaUsage = async (ctx: any) => {
28
+ const usage = await quotas.getQuotaUsage()
29
+ ctx.body = usage
30
+ }
@@ -15,6 +15,7 @@ const { encrypt } = require("@budibase/backend-core/encryption")
15
15
  const { newid } = require("@budibase/backend-core/utils")
16
16
  const { getUser } = require("../../utilities")
17
17
  const { Cookies } = require("@budibase/backend-core/constants")
18
+ const { featureFlags } = require("@budibase/backend-core")
18
19
 
19
20
  function newApiKey() {
20
21
  return encrypt(`${getTenantId()}${SEPARATOR}${newid()}`)
@@ -68,6 +69,29 @@ const checkCurrentApp = ctx => {
68
69
  }
69
70
  }
70
71
 
72
+ /**
73
+ * Add the attributes that are session based to the current user.
74
+ */
75
+ const addSessionAttributesToUser = ctx => {
76
+ ctx.body.account = ctx.user.account
77
+ ctx.body.license = ctx.user.license
78
+ ctx.body.budibaseAccess = ctx.user.budibaseAccess
79
+ ctx.body.accountPortalAccess = ctx.user.accountPortalAccess
80
+ ctx.body.csrfToken = ctx.user.csrfToken
81
+ }
82
+
83
+ /**
84
+ * Remove the attributes that are session based from the current user,
85
+ * so that stale values are not written to the db
86
+ */
87
+ const removeSessionAttributesFromUser = ctx => {
88
+ delete ctx.request.body.csrfToken
89
+ delete ctx.request.body.account
90
+ delete ctx.request.body.accountPortalAccess
91
+ delete ctx.request.body.budibaseAccess
92
+ delete ctx.request.body.license
93
+ }
94
+
71
95
  exports.getSelf = async ctx => {
72
96
  if (!ctx.user) {
73
97
  ctx.throw(403, "User not logged in")
@@ -81,11 +105,12 @@ exports.getSelf = async ctx => {
81
105
 
82
106
  // get the main body of the user
83
107
  ctx.body = await getUser(userId)
84
- // forward session information not found in db
85
- ctx.body.account = ctx.user.account
86
- ctx.body.budibaseAccess = ctx.user.budibaseAccess
87
- ctx.body.accountPortalAccess = ctx.user.accountPortalAccess
88
- ctx.body.csrfToken = ctx.user.csrfToken
108
+
109
+ // add the feature flags for this tenant
110
+ const tenantId = getTenantId()
111
+ ctx.body.featureFlags = featureFlags.getTenantFeatureFlags(tenantId)
112
+
113
+ addSessionAttributesToUser(ctx)
89
114
  }
90
115
 
91
116
  exports.updateSelf = async ctx => {
@@ -104,8 +129,8 @@ exports.updateSelf = async ctx => {
104
129
  // don't allow sending up an ID/Rev, always use the existing one
105
130
  delete ctx.request.body._id
106
131
  delete ctx.request.body._rev
107
- // don't allow setting the csrf token
108
- delete ctx.request.body.csrfToken
132
+ removeSessionAttributesFromUser(ctx)
133
+
109
134
  const response = await db.put({
110
135
  ...user,
111
136
  ...ctx.request.body,
@@ -1,15 +1,11 @@
1
1
  const {
2
2
  getGlobalUserParams,
3
3
  StaticDatabases,
4
- generateNewUsageQuotaDoc,
5
4
  } = require("@budibase/backend-core/db")
6
- const {
7
- getGlobalUserByEmail,
8
- saveUser,
9
- } = require("@budibase/backend-core/utils")
10
- const { EmailTemplatePurpose } = require("../../../constants")
11
- const { checkInviteCode } = require("../../../utilities/redis")
12
- const { sendEmail } = require("../../../utilities/email")
5
+ const { getGlobalUserByEmail } = require("@budibase/backend-core/utils")
6
+ import { EmailTemplatePurpose } from "../../../constants"
7
+ import { checkInviteCode } from "../../../utilities/redis"
8
+ import { sendEmail } from "../../../utilities/email"
13
9
  const { user: userCache } = require("@budibase/backend-core/cache")
14
10
  const { invalidateSessions } = require("@budibase/backend-core/sessions")
15
11
  const accounts = require("@budibase/backend-core/accounts")
@@ -20,26 +16,28 @@ const {
20
16
  doesTenantExist,
21
17
  } = require("@budibase/backend-core/tenancy")
22
18
  const { removeUserFromInfoDB } = require("@budibase/backend-core/deprovision")
23
- const env = require("../../../environment")
24
- const { syncUserInApps } = require("../../../utilities/appService")
25
- const { allUsers, getUser } = require("../../utilities")
19
+ import env from "../../../environment"
20
+ import { syncUserInApps } from "../../../utilities/appService"
21
+ import { quotas, users } from "@budibase/pro"
22
+ const { errors } = require("@budibase/backend-core")
23
+ import { allUsers, getUser } from "../../utilities"
26
24
 
27
- exports.save = async ctx => {
25
+ export const save = async (ctx: any) => {
28
26
  try {
29
- const user = await saveUser(ctx.request.body, getTenantId())
27
+ const user: any = await users.save(ctx.request.body, getTenantId())
30
28
  // let server know to sync user
31
29
  await syncUserInApps(user._id)
32
30
  ctx.body = user
33
- } catch (err) {
31
+ } catch (err: any) {
34
32
  ctx.throw(err.status || 400, err)
35
33
  }
36
34
  }
37
35
 
38
- const parseBooleanParam = param => {
36
+ const parseBooleanParam = (param: any) => {
39
37
  return !(param && param === "false")
40
38
  }
41
39
 
42
- exports.adminUser = async ctx => {
40
+ export const adminUser = async (ctx: any) => {
43
41
  const { email, password, tenantId } = ctx.request.body
44
42
 
45
43
  // account portal sends a pre-hashed password - honour param to prevent double hashing
@@ -69,10 +67,10 @@ exports.adminUser = async ctx => {
69
67
  } catch (err) {
70
68
  // don't worry about errors
71
69
  }
72
- await db.put(generateNewUsageQuotaDoc())
70
+ await db.put(quotas.generateNewQuotaUsage())
73
71
  }
74
72
 
75
- if (response.rows.some(row => row.doc.admin)) {
73
+ if (response.rows.some((row: any) => row.doc.admin)) {
76
74
  ctx.throw(
77
75
  403,
78
76
  "You cannot initialise once an global user has been created."
@@ -93,13 +91,13 @@ exports.adminUser = async ctx => {
93
91
  tenantId,
94
92
  }
95
93
  try {
96
- ctx.body = await saveUser(user, tenantId, hashPassword, requirePassword)
97
- } catch (err) {
94
+ ctx.body = await users.save(user, tenantId, hashPassword, requirePassword)
95
+ } catch (err: any) {
98
96
  ctx.throw(err.status || 400, err)
99
97
  }
100
98
  }
101
99
 
102
- exports.destroy = async ctx => {
100
+ export const destroy = async (ctx: any) => {
103
101
  const db = getGlobalDB()
104
102
  const dbUser = await db.get(ctx.params.id)
105
103
 
@@ -118,6 +116,7 @@ exports.destroy = async ctx => {
118
116
 
119
117
  await removeUserFromInfoDB(dbUser)
120
118
  await db.remove(dbUser._id, dbUser._rev)
119
+ await quotas.removeUser(dbUser)
121
120
  await userCache.invalidateUser(dbUser._id)
122
121
  await invalidateSessions(dbUser._id)
123
122
  // let server know to sync user
@@ -128,23 +127,23 @@ exports.destroy = async ctx => {
128
127
  }
129
128
 
130
129
  // called internally by app server user fetch
131
- exports.fetch = async ctx => {
132
- const users = await allUsers(ctx)
130
+ export const fetch = async (ctx: any) => {
131
+ const all = await allUsers()
133
132
  // user hashed password shouldn't ever be returned
134
- for (let user of users) {
133
+ for (let user of all) {
135
134
  if (user) {
136
135
  delete user.password
137
136
  }
138
137
  }
139
- ctx.body = users
138
+ ctx.body = all
140
139
  }
141
140
 
142
141
  // called internally by app server user find
143
- exports.find = async ctx => {
142
+ export const find = async (ctx: any) => {
144
143
  ctx.body = await getUser(ctx.params.id)
145
144
  }
146
145
 
147
- exports.tenantUserLookup = async ctx => {
146
+ export const tenantUserLookup = async (ctx: any) => {
148
147
  const id = ctx.params.id
149
148
  const user = await getTenantUser(id)
150
149
  if (user) {
@@ -154,7 +153,7 @@ exports.tenantUserLookup = async ctx => {
154
153
  }
155
154
  }
156
155
 
157
- exports.invite = async ctx => {
156
+ export const invite = async (ctx: any) => {
158
157
  let { email, userInfo } = ctx.request.body
159
158
  const existing = await getGlobalUserByEmail(email)
160
159
  if (existing) {
@@ -164,21 +163,22 @@ exports.invite = async ctx => {
164
163
  userInfo = {}
165
164
  }
166
165
  userInfo.tenantId = getTenantId()
167
- await sendEmail(email, EmailTemplatePurpose.INVITATION, {
166
+ const opts: any = {
168
167
  subject: "{{ company }} platform invitation",
169
168
  info: userInfo,
170
- })
169
+ }
170
+ await sendEmail(email, EmailTemplatePurpose.INVITATION, opts)
171
171
  ctx.body = {
172
172
  message: "Invitation has been sent.",
173
173
  }
174
174
  }
175
175
 
176
- exports.inviteAccept = async ctx => {
176
+ export const inviteAccept = async (ctx: any) => {
177
177
  const { inviteCode, password, firstName, lastName } = ctx.request.body
178
178
  try {
179
179
  // info is an extension of the user object that was stored by global
180
- const { email, info } = await checkInviteCode(inviteCode)
181
- ctx.body = await saveUser(
180
+ const { email, info }: any = await checkInviteCode(inviteCode)
181
+ ctx.body = await users.save(
182
182
  {
183
183
  firstName,
184
184
  lastName,
@@ -188,7 +188,11 @@ exports.inviteAccept = async ctx => {
188
188
  },
189
189
  info.tenantId
190
190
  )
191
- } catch (err) {
191
+ } catch (err: any) {
192
+ if (err.code === errors.codes.USAGE_LIMIT_EXCEEDED) {
193
+ // explicitly re-throw limit exceeded errors
194
+ ctx.throw(400, err)
195
+ }
192
196
  ctx.throw(400, "Unable to create new user, invitation invalid.")
193
197
  }
194
198
  }
package/src/api/index.js CHANGED
@@ -8,6 +8,8 @@ const {
8
8
  buildTenancyMiddleware,
9
9
  buildCsrfMiddleware,
10
10
  } = require("@budibase/backend-core/auth")
11
+ const { middleware: pro } = require("@budibase/pro")
12
+ const { errors } = require("@budibase/backend-core")
11
13
 
12
14
  const PUBLIC_ENDPOINTS = [
13
15
  // old deprecated endpoints kept for backwards compat
@@ -98,6 +100,7 @@ router
98
100
  .use(buildAuthMiddleware(PUBLIC_ENDPOINTS))
99
101
  .use(buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS))
100
102
  .use(buildCsrfMiddleware({ noCsrfPatterns: NO_CSRF_ENDPOINTS }))
103
+ .use(pro.licensing())
101
104
  // for now no public access is allowed to worker (bar health check)
102
105
  .use((ctx, next) => {
103
106
  if (ctx.publicEndpoint) {
@@ -110,16 +113,18 @@ router
110
113
  })
111
114
  .use(auditLog)
112
115
 
113
- // error handling middleware
116
+ // error handling middleware - TODO: This could be moved to backend-core
114
117
  router.use(async (ctx, next) => {
115
118
  try {
116
119
  await next()
117
120
  } catch (err) {
118
121
  ctx.log.error(err)
119
122
  ctx.status = err.status || err.statusCode || 500
123
+ const error = errors.getPublicError(err)
120
124
  ctx.body = {
121
125
  message: err.message,
122
126
  status: ctx.status,
127
+ error,
123
128
  }
124
129
  }
125
130
  })
@@ -0,0 +1,12 @@
1
+ import Router from "@koa/router"
2
+ import * as controller from "../../controllers/global/license"
3
+
4
+ const router = new Router()
5
+
6
+ router
7
+ .post("/api/global/license/activate", controller.activate)
8
+ .post("/api/global/license/refresh", controller.refresh)
9
+ .get("/api/global/license/info", controller.getInfo)
10
+ .get("/api/global/license/usage", controller.getQuotaUsage)
11
+
12
+ export = router
@@ -10,6 +10,7 @@ const environmentRoutes = require("./system/environment")
10
10
  const tenantsRoutes = require("./system/tenants")
11
11
  const statusRoutes = require("./system/status")
12
12
  const selfRoutes = require("./global/self")
13
+ const licenseRoutes = require("./global/license")
13
14
 
14
15
  exports.routes = [
15
16
  configRoutes,
@@ -24,4 +25,5 @@ exports.routes = [
24
25
  environmentRoutes,
25
26
  statusRoutes,
26
27
  selfRoutes,
28
+ licenseRoutes,
27
29
  ]
@@ -75,7 +75,8 @@ describe("/api/global/auth", () => {
75
75
  afterEach(() => {
76
76
  expect(strategyFactory).toBeCalledWith(
77
77
  chosenConfig,
78
- `http://localhost:10000/api/global/auth/${TENANT_ID}/oidc/callback`
78
+ `http://localhost:10000/api/global/auth/${TENANT_ID}/oidc/callback`,
79
+ expect.any(Function)
79
80
  )
80
81
  })
81
82
 
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  import { Scope } from "@sentry/node"
3
3
  import { Event } from "@sentry/types/dist/event"
4
4
  import Application from "koa"
5
+ import { bootstrap } from "global-agent"
5
6
 
6
7
  const env = require("./environment")
7
8
  const CouchDB = require("./db")
@@ -17,6 +18,9 @@ const api = require("./api")
17
18
  const redis = require("./utilities/redis")
18
19
  const Sentry = require("@sentry/node")
19
20
 
21
+ // this will setup http and https proxies form env variables
22
+ bootstrap()
23
+
20
24
  const app: Application = new Koa()
21
25
 
22
26
  app.keys = ["secret", "key"]