@volcanicminds/backend 0.8.1 → 0.9.0

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 (76) hide show
  1. package/README.md +129 -0
  2. package/dist/index.js +14 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/api/auth/controller/auth.js +14 -15
  5. package/dist/lib/api/auth/controller/auth.js.map +1 -1
  6. package/dist/lib/api/health/controller/health.js +1 -2
  7. package/dist/lib/api/health/controller/health.js.map +1 -1
  8. package/dist/lib/api/token/controller/token.js +9 -10
  9. package/dist/lib/api/token/controller/token.js.map +1 -1
  10. package/dist/lib/api/tool/controller/tool.js +1 -2
  11. package/dist/lib/api/tool/controller/tool.js.map +1 -1
  12. package/dist/lib/api/users/controller/user.js +2 -3
  13. package/dist/lib/api/users/controller/user.js.map +1 -1
  14. package/dist/lib/config/general.js +2 -1
  15. package/dist/lib/config/general.js.map +1 -1
  16. package/dist/lib/config/plugins.js +5 -0
  17. package/dist/lib/config/plugins.js.map +1 -1
  18. package/dist/lib/hooks/onRequest.js +48 -44
  19. package/dist/lib/hooks/onRequest.js.map +1 -1
  20. package/dist/lib/hooks/onResponse.js +2 -2
  21. package/dist/lib/hooks/onResponse.js.map +1 -1
  22. package/dist/lib/loader/general.js +3 -3
  23. package/dist/lib/loader/general.js.map +1 -1
  24. package/dist/lib/loader/hooks.js +1 -2
  25. package/dist/lib/loader/hooks.js.map +1 -1
  26. package/dist/lib/loader/plugins.js +1 -2
  27. package/dist/lib/loader/plugins.js.map +1 -1
  28. package/dist/lib/loader/roles.js +1 -2
  29. package/dist/lib/loader/roles.js.map +1 -1
  30. package/dist/lib/loader/router.js +49 -19
  31. package/dist/lib/loader/router.js.map +1 -1
  32. package/dist/lib/loader/schedules.js +2 -3
  33. package/dist/lib/loader/schedules.js.map +1 -1
  34. package/dist/lib/loader/schemas.js +1 -2
  35. package/dist/lib/loader/schemas.js.map +1 -1
  36. package/dist/lib/loader/tracking.js +1 -2
  37. package/dist/lib/loader/tracking.js.map +1 -1
  38. package/dist/lib/loader/translation.js +1 -2
  39. package/dist/lib/loader/translation.js.map +1 -1
  40. package/dist/lib/middleware/dispatchForgotPasswordLink.js +1 -2
  41. package/dist/lib/middleware/dispatchForgotPasswordLink.js.map +1 -1
  42. package/dist/lib/middleware/isAdmin.js +1 -2
  43. package/dist/lib/middleware/isAdmin.js.map +1 -1
  44. package/dist/lib/middleware/isAuthenticated.js +1 -2
  45. package/dist/lib/middleware/isAuthenticated.js.map +1 -1
  46. package/dist/lib/middleware/postAuth.js +1 -2
  47. package/dist/lib/middleware/postAuth.js.map +1 -1
  48. package/dist/lib/middleware/preAuth.js +1 -2
  49. package/dist/lib/middleware/preAuth.js.map +1 -1
  50. package/dist/lib/middleware/preForgotPasswordHandler.js +1 -2
  51. package/dist/lib/middleware/preForgotPasswordHandler.js.map +1 -1
  52. package/dist/lib/schedules/test.job.js +2 -2
  53. package/dist/lib/schedules/test.job.js.map +1 -1
  54. package/dist/lib/util/common.js +2 -3
  55. package/dist/lib/util/common.js.map +1 -1
  56. package/dist/lib/util/generate.js +1 -2
  57. package/dist/lib/util/generate.js.map +1 -1
  58. package/dist/lib/util/mark.js +1 -2
  59. package/dist/lib/util/mark.js.map +1 -1
  60. package/dist/lib/util/path.js +1 -2
  61. package/dist/lib/util/path.js.map +1 -1
  62. package/dist/lib/util/tracker.js +3 -4
  63. package/dist/lib/util/tracker.js.map +1 -1
  64. package/dist/lib/util/yn.js +1 -1
  65. package/dist/lib/util/yn.js.map +1 -1
  66. package/dist/package-lock.json +7687 -0
  67. package/dist/package.json +22 -21
  68. package/index.ts +18 -1
  69. package/lib/config/general.ts +2 -1
  70. package/lib/config/plugins.ts +5 -0
  71. package/lib/hooks/onRequest.ts +55 -49
  72. package/lib/hooks/onResponse.ts +2 -2
  73. package/lib/loader/general.ts +2 -1
  74. package/lib/loader/router.ts +16 -7
  75. package/package.json +22 -21
  76. package/types/global.d.ts +6 -1
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volcanicminds/backend",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "codename": "turin",
5
5
  "license": "MIT",
6
6
  "description": "The volcanic (minds) backend",
@@ -51,42 +51,43 @@
51
51
  "upgrade-pkg": "yarn npm-upgrade"
52
52
  },
53
53
  "dependencies": {
54
- "@apollo/server": "^4.10.0",
54
+ "@apollo/server": "^4.11.0",
55
55
  "@as-integrations/fastify": "^2.1.1",
56
- "@fastify/compress": "^7.0.0",
56
+ "@fastify/compress": "^7.0.3",
57
57
  "@fastify/cors": "^9.0.1",
58
58
  "@fastify/helmet": "^11.1.1",
59
- "@fastify/jwt": "^8.0.0",
60
- "@fastify/multipart": "^8.1.0",
59
+ "@fastify/jwt": "^8.0.1",
60
+ "@fastify/multipart": "^8.3.0",
61
61
  "@fastify/rate-limit": "^9.1.0",
62
62
  "@fastify/schedule": "^4.1.1",
63
- "@fastify/swagger": "^8.14.0",
64
- "@fastify/swagger-ui": "^3.0.0",
65
- "@types/node": "^20.11.20",
66
- "dayjs": "^1.11.10",
63
+ "@fastify/swagger": "^8.15.0",
64
+ "@fastify/swagger-ui": "^3.1.0",
65
+ "@types/node": "^20.16.2",
66
+ "dayjs": "^1.11.13",
67
67
  "dotenv": "^16.4.5",
68
- "fastify": "^4.26.1",
69
- "glob": "^10.3.10",
70
- "graphql": "^16.8.1",
68
+ "fastify": "^4.28.1",
69
+ "fastify-raw-body": "^4.3.0",
70
+ "glob": "^10.4.5",
71
+ "graphql": "^16.9.0",
71
72
  "i18n": "^0.15.1",
72
73
  "lodash": "^4.17.21",
73
- "nanoid": "^5.0.6",
74
- "object-sizeof": "^2.6.4",
75
- "pino": "^8.19.0",
76
- "pino-pretty": "^10.3.1",
74
+ "nanoid": "^5.0.7",
75
+ "object-sizeof": "^2.6.5",
76
+ "pino": "^9.3.2",
77
+ "pino-pretty": "^11.2.2",
77
78
  "root-require": "^0.3.1",
78
- "semver": "^7.6.0",
79
+ "semver": "^7.6.3",
79
80
  "toad-scheduler": "^3.0.1"
80
81
  },
81
82
  "devDependencies": {
82
- "@types/mocha": "^10.0.6",
83
+ "@types/mocha": "^10.0.7",
83
84
  "cross-env": "^7.0.3",
84
85
  "expect": "^29.7.0",
85
- "mocha": "^10.3.0",
86
- "nodemon": "^3.1.0",
86
+ "mocha": "^10.7.3",
87
+ "nodemon": "^3.1.4",
87
88
  "npm-upgrade": "^3.1.0",
88
89
  "ts-node": "^10.9.2",
89
- "typescript": "^5.3.3"
90
+ "typescript": "^5.5.4"
90
91
  },
91
92
  "repository": {
92
93
  "type": "git",
package/index.ts CHANGED
@@ -28,6 +28,7 @@ import helmet from '@fastify/helmet'
28
28
  import compress from '@fastify/compress'
29
29
  import rateLimit from '@fastify/rate-limit'
30
30
  import multipart from '@fastify/multipart'
31
+ import rawBody from 'fastify-raw-body'
31
32
 
32
33
  import { ApolloServer } from '@apollo/server'
33
34
  import fastifyApollo, { fastifyApolloDrainPlugin } from '@as-integrations/fastify'
@@ -178,8 +179,24 @@ const start = async (decorators) => {
178
179
  const plugins = loaderPlugins.load()
179
180
 
180
181
  // Helmet is not usable with Apollo Server
182
+ plugins?.rawBody && (await server.register(rawBody, plugins.rawBody || {}))
181
183
  !loadApollo && plugins?.helmet && (await server.register(helmet, plugins.helmet || {}))
182
- plugins?.rateLimit && (await server.register(rateLimit, plugins.rateLimit || {}))
184
+
185
+ if (plugins?.rateLimit) {
186
+ await server.register(rateLimit, plugins.rateLimit || {})
187
+ server.setNotFoundHandler(
188
+ {
189
+ preHandler: server.rateLimit({
190
+ max: 30,
191
+ timeWindow: 30000
192
+ })
193
+ },
194
+ function (req, reply) {
195
+ reply.code(404).send()
196
+ }
197
+ )
198
+ }
199
+
183
200
  plugins?.multipart && (await server.register(multipart, plugins.multipart || {}))
184
201
  plugins?.cors && (await server.register(cors, plugins.cors || {}))
185
202
  plugins?.compress && (await server.register(compress, plugins.compress || {}))
@@ -5,6 +5,7 @@ module.exports = {
5
5
  enable: true,
6
6
  options: {
7
7
  reset_external_id_on_login: false,
8
- scheduler: false
8
+ scheduler: false,
9
+ embedded_auth: true
9
10
  }
10
11
  }
@@ -58,5 +58,10 @@ module.exports = [
58
58
  name: 'multipart',
59
59
  enable: false,
60
60
  options: {}
61
+ },
62
+ {
63
+ name: 'rawBody',
64
+ enable: false,
65
+ options: {}
61
66
  }
62
67
  ]
@@ -1,68 +1,74 @@
1
1
  import { getParams, getData } from '../util/common'
2
2
  import { AuthenticatedUser, AuthenticatedToken, Role } from '../../types/global'
3
3
 
4
+ const { embedded_auth = true } = global.config?.options || {}
5
+
4
6
  module.exports = async (req, reply) => {
5
- // request enrichment
6
7
  log.i && (req.startedAt = new Date())
8
+
9
+ // request enrichment
7
10
  req.data = () => getData(req)
8
11
  req.parameters = () => getParams(req)
9
- req.roles = () => (req.user ? req.user.roles : [roles.public])
10
- req.hasRole = (r: Role) => (req.user ? req.user.roles : [roles.public]).some((role) => role === r?.code)
11
12
 
12
- // authorization check
13
- const auth = req.headers?.authorization || ''
14
- const cfg = req.routeOptions?.config || req.routeConfig || {}
15
- const [prefix, bearerToken] = auth.split(' ')
16
- const isRoutePublic = (cfg.requiredRoles || []).some((role: Role) => role.code === roles.public.code)
13
+ if (embedded_auth) {
14
+ req.roles = () => (req.user ? req.user.roles : [roles.public])
15
+ req.hasRole = (r: Role) => (req.user ? req.user.roles : [roles.public]).some((role) => role === r?.code)
17
16
 
18
- if (prefix === 'Bearer' && bearerToken != null) {
19
- let user: null | AuthenticatedUser = null
20
- let token: null | AuthenticatedToken = null
17
+ // authorization check
18
+ const auth = req.headers?.authorization || ''
19
+ const cfg = req.routeOptions?.config || req.routeConfig || {}
20
+ const [prefix, bearerToken] = auth.split(' ')
21
21
 
22
- try {
23
- const tokenData = reply.server.jwt.verify(bearerToken)
24
- user = await req.server['userManager'].retrieveUserByExternalId(tokenData?.sub)
25
- if (!user && req.server['tokenManager'].isImplemented()) {
26
- token = await req.server['tokenManager'].retrieveTokenByExternalId(tokenData?.sub)
27
- }
28
- if (!user && !token) {
29
- return reply.status(404).send({ statusCode: 404, code: 'USER_NOT_FOUND', message: 'User not found' })
30
- }
31
- if (user) {
32
- const isValid = await req.server['userManager'].isValidUser(user)
33
- if (!isValid) {
34
- return reply.status(404).send({ statusCode: 404, code: 'USER_NOT_VALID', message: 'User not valid' })
22
+ if (prefix === 'Bearer' && bearerToken != null) {
23
+ let user: null | AuthenticatedUser = null
24
+ let token: null | AuthenticatedToken = null
25
+
26
+ try {
27
+ const tokenData = reply.server.jwt.verify(bearerToken)
28
+ user = await req.server['userManager'].retrieveUserByExternalId(tokenData?.sub)
29
+ if (!user && req.server['tokenManager'].isImplemented()) {
30
+ token = await req.server['tokenManager'].retrieveTokenByExternalId(tokenData?.sub)
35
31
  }
36
- // ok, we have the full user here
37
- req.user = user
38
- }
39
- if (token) {
40
- const isValid = await req.server['tokenManager'].isValidToken(token)
41
- if (!isValid) {
42
- return reply.status(404).send({ statusCode: 404, code: 'TOKEN_NOT_VALID', message: 'Token not valid' })
32
+ if (!user && !token) {
33
+ return reply.status(404).send({ statusCode: 404, code: 'USER_NOT_FOUND', message: 'User not found' })
34
+ }
35
+ if (user) {
36
+ const isValid = await req.server['userManager'].isValidUser(user)
37
+ if (!isValid) {
38
+ return reply.status(404).send({ statusCode: 404, code: 'USER_NOT_VALID', message: 'User not valid' })
39
+ }
40
+ // ok, we have the full user here
41
+ req.user = user
42
+ }
43
+ if (token) {
44
+ const isValid = await req.server['tokenManager'].isValidToken(token)
45
+ if (!isValid) {
46
+ return reply.status(404).send({ statusCode: 404, code: 'TOKEN_NOT_VALID', message: 'Token not valid' })
47
+ }
48
+ // ok, we have the full user here
49
+ req.token = token
50
+ }
51
+ } catch (error) {
52
+ const isRoutePublic = (cfg.requiredRoles || []).some((role: Role) => role.code === roles.public.code)
53
+ if (!isRoutePublic) {
54
+ throw error
43
55
  }
44
- // ok, we have the full user here
45
- req.token = token
46
- }
47
- } catch (error) {
48
- if (!isRoutePublic) {
49
- throw error
50
56
  }
51
57
  }
52
- }
53
58
 
54
- if (cfg.requiredRoles?.length > 0) {
55
- const { method = '', url = '', requiredRoles } = cfg
56
- const authRoles: string[] = ((req.user?.roles || req.token?.roles)?.map((code) => code) as string[]) || [
57
- roles.public?.code || 'public'
58
- ]
59
- const resolvedRoles = authRoles.length > 0 ? requiredRoles.filter((r) => authRoles.includes(r.code)) : []
59
+ if (cfg.requiredRoles?.length > 0) {
60
+ const { method = '', url = '', requiredRoles } = cfg
61
+ const authRoles: string[] = ((req.user?.roles || req.token?.roles)?.map((code) => code) as string[]) || [
62
+ roles.public?.code || 'public'
63
+ ]
64
+ const resolvedRoles = authRoles.length > 0 ? requiredRoles.filter((r) => authRoles.includes(r.code)) : []
60
65
 
61
- if (!resolvedRoles.length) {
62
- log.w && log.warn(`Not allowed to call ${method.toUpperCase()} ${url}`)
63
- return reply
64
- .status(403)
65
- .send({ statusCode: 403, code: 'ROLE_NOT_ALLOWED', message: 'Not allowed to call this route' })
66
+ if (!resolvedRoles.length) {
67
+ log.w && log.warn(`Not allowed to call ${method.toUpperCase()} ${url}`)
68
+ return reply
69
+ .status(403)
70
+ .send({ statusCode: 403, code: 'ROLE_NOT_ALLOWED', message: 'Not allowed to call this route' })
71
+ }
66
72
  }
67
73
  }
68
74
  }
@@ -10,6 +10,6 @@ module.exports = async (req, reply) => {
10
10
  extraMessage += `[${reqSize}${replySize} bytes]`
11
11
  }
12
12
 
13
- const message: string = `${req.method} ${req.url} ${reply.statusCode} ${extraMessage}`
14
- reply.statusCode < 300 ? log.info(message) : reply.statusCode < 400 ? log.warn(message) : log.error(message)
13
+ const message = () => `${req.method} ${req.url} ${reply.statusCode} ${extraMessage}`.trim()
14
+ reply.statusCode < 300 ? log.info(message()) : reply.statusCode < 400 ? log.warn(message()) : log.error(message())
15
15
  }
@@ -8,7 +8,8 @@ export function load() {
8
8
  enable: true,
9
9
  options: {
10
10
  reset_external_id_on_login: false,
11
- scheduler: false
11
+ scheduler: false,
12
+ embedded_auth: true
12
13
  }
13
14
  }
14
15
 
@@ -31,9 +31,10 @@ export function load(): ConfiguredRoute[] {
31
31
  method: methodCase,
32
32
  path: pathName = '/',
33
33
  handler,
34
+ roles: rs = [],
34
35
  config = {} as RouteConfig,
35
36
  middlewares = [],
36
- roles: rs = []
37
+ rateLimit
37
38
  } = route
38
39
 
39
40
  const rsp = !rs.length ? [roles.public] : rs
@@ -68,7 +69,8 @@ export function load(): ConfiguredRoute[] {
68
69
  params,
69
70
  body,
70
71
  response,
71
- consumes
72
+ consumes,
73
+ rawBody = false
72
74
  } = config || {}
73
75
 
74
76
  // adjust something
@@ -145,6 +147,8 @@ export function load(): ConfiguredRoute[] {
145
147
  middlewares,
146
148
  roles: requiredRoles,
147
149
  enable,
150
+ rawBody,
151
+ rateLimit,
148
152
  base,
149
153
  file: path.join(base, defaultConfig.controller || 'controller', handlerParts[0]),
150
154
  func: handlerParts[1],
@@ -208,8 +212,10 @@ async function loadMiddlewares(base: string, middlewares: string[] = []) {
208
212
  export function apply(server: any, routes: ConfiguredRoute[]): void {
209
213
  log.t && log.trace(`Apply ${routes.length} routes to server with pid ${process.pid}`)
210
214
 
211
- routes.forEach(async ({ handler, method, path, middlewares, roles, enable, base, file, func, doc }) => {
212
- if (enable) {
215
+ routes.forEach(async (route) => {
216
+ if (route?.enable) {
217
+ const { handler, method, path, middlewares, roles, rawBody, rateLimit, base, file, func, doc } = route
218
+
213
219
  log.t && log.trace(`* Add path ${method} ${path} on handle ${handler}`)
214
220
  const midds = await loadMiddlewares(base, middlewares)
215
221
 
@@ -219,11 +225,14 @@ export function apply(server: any, routes: ConfiguredRoute[]): void {
219
225
  schema: doc,
220
226
  ...midds,
221
227
  config: {
222
- requiredRoles: roles || []
228
+ requiredRoles: roles || [],
229
+ rawBody: rawBody || false,
230
+ rateLimit: rateLimit || undefined
223
231
  },
224
- handler: function (req: FastifyRequest, reply: FastifyReply) {
232
+ handler: async function (req: FastifyRequest, reply: FastifyReply) {
225
233
  try {
226
- return require(file)[func](req, reply)
234
+ const module = await import(file)
235
+ return await module[func](req, reply)
227
236
  } catch (err) {
228
237
  log.e && log.error(`Cannot find ${file} or method ${func}: ${err}`)
229
238
  return reply.code(500).send(`Invalid handler ${handler}`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volcanicminds/backend",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "codename": "turin",
5
5
  "license": "MIT",
6
6
  "description": "The volcanic (minds) backend",
@@ -51,42 +51,43 @@
51
51
  "upgrade-pkg": "yarn npm-upgrade"
52
52
  },
53
53
  "dependencies": {
54
- "@apollo/server": "^4.10.0",
54
+ "@apollo/server": "^4.11.0",
55
55
  "@as-integrations/fastify": "^2.1.1",
56
- "@fastify/compress": "^7.0.0",
56
+ "@fastify/compress": "^7.0.3",
57
57
  "@fastify/cors": "^9.0.1",
58
58
  "@fastify/helmet": "^11.1.1",
59
- "@fastify/jwt": "^8.0.0",
60
- "@fastify/multipart": "^8.1.0",
59
+ "@fastify/jwt": "^8.0.1",
60
+ "@fastify/multipart": "^8.3.0",
61
61
  "@fastify/rate-limit": "^9.1.0",
62
62
  "@fastify/schedule": "^4.1.1",
63
- "@fastify/swagger": "^8.14.0",
64
- "@fastify/swagger-ui": "^3.0.0",
65
- "@types/node": "^20.11.20",
66
- "dayjs": "^1.11.10",
63
+ "@fastify/swagger": "^8.15.0",
64
+ "@fastify/swagger-ui": "^3.1.0",
65
+ "@types/node": "^20.16.2",
66
+ "dayjs": "^1.11.13",
67
67
  "dotenv": "^16.4.5",
68
- "fastify": "^4.26.1",
69
- "glob": "^10.3.10",
70
- "graphql": "^16.8.1",
68
+ "fastify": "^4.28.1",
69
+ "fastify-raw-body": "^4.3.0",
70
+ "glob": "^10.4.5",
71
+ "graphql": "^16.9.0",
71
72
  "i18n": "^0.15.1",
72
73
  "lodash": "^4.17.21",
73
- "nanoid": "^5.0.6",
74
- "object-sizeof": "^2.6.4",
75
- "pino": "^8.19.0",
76
- "pino-pretty": "^10.3.1",
74
+ "nanoid": "^5.0.7",
75
+ "object-sizeof": "^2.6.5",
76
+ "pino": "^9.3.2",
77
+ "pino-pretty": "^11.2.2",
77
78
  "root-require": "^0.3.1",
78
- "semver": "^7.6.0",
79
+ "semver": "^7.6.3",
79
80
  "toad-scheduler": "^3.0.1"
80
81
  },
81
82
  "devDependencies": {
82
- "@types/mocha": "^10.0.6",
83
+ "@types/mocha": "^10.0.7",
83
84
  "cross-env": "^7.0.3",
84
85
  "expect": "^29.7.0",
85
- "mocha": "^10.3.0",
86
- "nodemon": "^3.1.0",
86
+ "mocha": "^10.7.3",
87
+ "nodemon": "^3.1.4",
87
88
  "npm-upgrade": "^3.1.0",
88
89
  "ts-node": "^10.9.2",
89
- "typescript": "^5.3.3"
90
+ "typescript": "^5.5.4"
90
91
  },
91
92
  "repository": {
92
93
  "type": "git",
package/types/global.d.ts CHANGED
@@ -40,6 +40,7 @@ export interface RouteConfig {
40
40
  body?: any
41
41
  response?: any
42
42
  consumes?: any
43
+ rawBody?: boolean
43
44
  }
44
45
 
45
46
  export interface Route {
@@ -47,8 +48,9 @@ export interface Route {
47
48
  path: string
48
49
  handler: string
49
50
  roles: Role[]
50
- config?: RouteConfig
51
51
  middlewares: string[]
52
+ config?: RouteConfig
53
+ rateLimit?: any
52
54
  }
53
55
 
54
56
  export interface GeneralConfig {
@@ -57,6 +59,7 @@ export interface GeneralConfig {
57
59
  options: {
58
60
  reset_external_id_on_login: boolean
59
61
  scheduler: boolean
62
+ embedded_auth: boolean
60
63
  }
61
64
  }
62
65
 
@@ -86,6 +89,8 @@ export interface ConfiguredRoute {
86
89
  method: any
87
90
  path: string
88
91
  handler: any
92
+ rawBody: boolean
93
+ rateLimit: any
89
94
  file: string
90
95
  func: any
91
96
  base: string