@volcanicminds/backend 0.8.2 → 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.
- package/README.md +129 -0
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/config/general.js +2 -1
- package/dist/lib/config/general.js.map +1 -1
- package/dist/lib/hooks/onRequest.js +48 -44
- package/dist/lib/hooks/onRequest.js.map +1 -1
- package/dist/lib/hooks/onResponse.js +2 -2
- package/dist/lib/hooks/onResponse.js.map +1 -1
- package/dist/lib/loader/general.js +2 -1
- package/dist/lib/loader/general.js.map +1 -1
- package/dist/lib/loader/router.js +40 -11
- package/dist/lib/loader/router.js.map +1 -1
- package/dist/package-lock.json +7687 -0
- package/dist/package.json +21 -21
- package/index.ts +16 -1
- package/lib/config/general.ts +2 -1
- package/lib/hooks/onRequest.ts +55 -49
- package/lib/hooks/onResponse.ts +2 -2
- package/lib/loader/general.ts +2 -1
- package/lib/loader/router.ts +12 -6
- package/package.json +21 -21
- package/types/global.d.ts +4 -1
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volcanicminds/backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"codename": "turin",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "The volcanic (minds) backend",
|
|
@@ -51,43 +51,43 @@
|
|
|
51
51
|
"upgrade-pkg": "yarn npm-upgrade"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@apollo/server": "^4.
|
|
54
|
+
"@apollo/server": "^4.11.0",
|
|
55
55
|
"@as-integrations/fastify": "^2.1.1",
|
|
56
|
-
"@fastify/compress": "^7.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.
|
|
60
|
-
"@fastify/multipart": "^8.
|
|
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.
|
|
64
|
-
"@fastify/swagger-ui": "^3.
|
|
65
|
-
"@types/node": "^20.
|
|
66
|
-
"dayjs": "^1.11.
|
|
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.
|
|
68
|
+
"fastify": "^4.28.1",
|
|
69
69
|
"fastify-raw-body": "^4.3.0",
|
|
70
|
-
"glob": "^10.
|
|
71
|
-
"graphql": "^16.
|
|
70
|
+
"glob": "^10.4.5",
|
|
71
|
+
"graphql": "^16.9.0",
|
|
72
72
|
"i18n": "^0.15.1",
|
|
73
73
|
"lodash": "^4.17.21",
|
|
74
|
-
"nanoid": "^5.0.
|
|
75
|
-
"object-sizeof": "^2.6.
|
|
76
|
-
"pino": "^
|
|
77
|
-
"pino-pretty": "^
|
|
74
|
+
"nanoid": "^5.0.7",
|
|
75
|
+
"object-sizeof": "^2.6.5",
|
|
76
|
+
"pino": "^9.3.2",
|
|
77
|
+
"pino-pretty": "^11.2.2",
|
|
78
78
|
"root-require": "^0.3.1",
|
|
79
|
-
"semver": "^7.6.
|
|
79
|
+
"semver": "^7.6.3",
|
|
80
80
|
"toad-scheduler": "^3.0.1"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
|
-
"@types/mocha": "^10.0.
|
|
83
|
+
"@types/mocha": "^10.0.7",
|
|
84
84
|
"cross-env": "^7.0.3",
|
|
85
85
|
"expect": "^29.7.0",
|
|
86
|
-
"mocha": "^10.3
|
|
87
|
-
"nodemon": "^3.1.
|
|
86
|
+
"mocha": "^10.7.3",
|
|
87
|
+
"nodemon": "^3.1.4",
|
|
88
88
|
"npm-upgrade": "^3.1.0",
|
|
89
89
|
"ts-node": "^10.9.2",
|
|
90
|
-
"typescript": "^5.
|
|
90
|
+
"typescript": "^5.5.4"
|
|
91
91
|
},
|
|
92
92
|
"repository": {
|
|
93
93
|
"type": "git",
|
package/index.ts
CHANGED
|
@@ -181,7 +181,22 @@ const start = async (decorators) => {
|
|
|
181
181
|
// Helmet is not usable with Apollo Server
|
|
182
182
|
plugins?.rawBody && (await server.register(rawBody, plugins.rawBody || {}))
|
|
183
183
|
!loadApollo && plugins?.helmet && (await server.register(helmet, plugins.helmet || {}))
|
|
184
|
-
|
|
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
|
+
|
|
185
200
|
plugins?.multipart && (await server.register(multipart, plugins.multipart || {}))
|
|
186
201
|
plugins?.cors && (await server.register(cors, plugins.cors || {}))
|
|
187
202
|
plugins?.compress && (await server.register(compress, plugins.compress || {}))
|
package/lib/config/general.ts
CHANGED
package/lib/hooks/onRequest.ts
CHANGED
|
@@ -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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
}
|
package/lib/hooks/onResponse.ts
CHANGED
|
@@ -10,6 +10,6 @@ module.exports = async (req, reply) => {
|
|
|
10
10
|
extraMessage += `[${reqSize}${replySize} bytes]`
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const message
|
|
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
|
}
|
package/lib/loader/general.ts
CHANGED
package/lib/loader/router.ts
CHANGED
|
@@ -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
|
-
|
|
37
|
+
rateLimit
|
|
37
38
|
} = route
|
|
38
39
|
|
|
39
40
|
const rsp = !rs.length ? [roles.public] : rs
|
|
@@ -147,6 +148,7 @@ export function load(): ConfiguredRoute[] {
|
|
|
147
148
|
roles: requiredRoles,
|
|
148
149
|
enable,
|
|
149
150
|
rawBody,
|
|
151
|
+
rateLimit,
|
|
150
152
|
base,
|
|
151
153
|
file: path.join(base, defaultConfig.controller || 'controller', handlerParts[0]),
|
|
152
154
|
func: handlerParts[1],
|
|
@@ -210,8 +212,10 @@ async function loadMiddlewares(base: string, middlewares: string[] = []) {
|
|
|
210
212
|
export function apply(server: any, routes: ConfiguredRoute[]): void {
|
|
211
213
|
log.t && log.trace(`Apply ${routes.length} routes to server with pid ${process.pid}`)
|
|
212
214
|
|
|
213
|
-
routes.forEach(async (
|
|
214
|
-
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
|
+
|
|
215
219
|
log.t && log.trace(`* Add path ${method} ${path} on handle ${handler}`)
|
|
216
220
|
const midds = await loadMiddlewares(base, middlewares)
|
|
217
221
|
|
|
@@ -222,11 +226,13 @@ export function apply(server: any, routes: ConfiguredRoute[]): void {
|
|
|
222
226
|
...midds,
|
|
223
227
|
config: {
|
|
224
228
|
requiredRoles: roles || [],
|
|
225
|
-
rawBody: rawBody || false
|
|
229
|
+
rawBody: rawBody || false,
|
|
230
|
+
rateLimit: rateLimit || undefined
|
|
226
231
|
},
|
|
227
|
-
handler: function (req: FastifyRequest, reply: FastifyReply) {
|
|
232
|
+
handler: async function (req: FastifyRequest, reply: FastifyReply) {
|
|
228
233
|
try {
|
|
229
|
-
|
|
234
|
+
const module = await import(file)
|
|
235
|
+
return await module[func](req, reply)
|
|
230
236
|
} catch (err) {
|
|
231
237
|
log.e && log.error(`Cannot find ${file} or method ${func}: ${err}`)
|
|
232
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.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"codename": "turin",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "The volcanic (minds) backend",
|
|
@@ -51,43 +51,43 @@
|
|
|
51
51
|
"upgrade-pkg": "yarn npm-upgrade"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@apollo/server": "^4.
|
|
54
|
+
"@apollo/server": "^4.11.0",
|
|
55
55
|
"@as-integrations/fastify": "^2.1.1",
|
|
56
|
-
"@fastify/compress": "^7.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.
|
|
60
|
-
"@fastify/multipart": "^8.
|
|
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.
|
|
64
|
-
"@fastify/swagger-ui": "^3.
|
|
65
|
-
"@types/node": "^20.
|
|
66
|
-
"dayjs": "^1.11.
|
|
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.
|
|
68
|
+
"fastify": "^4.28.1",
|
|
69
69
|
"fastify-raw-body": "^4.3.0",
|
|
70
|
-
"glob": "^10.
|
|
71
|
-
"graphql": "^16.
|
|
70
|
+
"glob": "^10.4.5",
|
|
71
|
+
"graphql": "^16.9.0",
|
|
72
72
|
"i18n": "^0.15.1",
|
|
73
73
|
"lodash": "^4.17.21",
|
|
74
|
-
"nanoid": "^5.0.
|
|
75
|
-
"object-sizeof": "^2.6.
|
|
76
|
-
"pino": "^
|
|
77
|
-
"pino-pretty": "^
|
|
74
|
+
"nanoid": "^5.0.7",
|
|
75
|
+
"object-sizeof": "^2.6.5",
|
|
76
|
+
"pino": "^9.3.2",
|
|
77
|
+
"pino-pretty": "^11.2.2",
|
|
78
78
|
"root-require": "^0.3.1",
|
|
79
|
-
"semver": "^7.6.
|
|
79
|
+
"semver": "^7.6.3",
|
|
80
80
|
"toad-scheduler": "^3.0.1"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
|
-
"@types/mocha": "^10.0.
|
|
83
|
+
"@types/mocha": "^10.0.7",
|
|
84
84
|
"cross-env": "^7.0.3",
|
|
85
85
|
"expect": "^29.7.0",
|
|
86
|
-
"mocha": "^10.3
|
|
87
|
-
"nodemon": "^3.1.
|
|
86
|
+
"mocha": "^10.7.3",
|
|
87
|
+
"nodemon": "^3.1.4",
|
|
88
88
|
"npm-upgrade": "^3.1.0",
|
|
89
89
|
"ts-node": "^10.9.2",
|
|
90
|
-
"typescript": "^5.
|
|
90
|
+
"typescript": "^5.5.4"
|
|
91
91
|
},
|
|
92
92
|
"repository": {
|
|
93
93
|
"type": "git",
|
package/types/global.d.ts
CHANGED
|
@@ -48,8 +48,9 @@ export interface Route {
|
|
|
48
48
|
path: string
|
|
49
49
|
handler: string
|
|
50
50
|
roles: Role[]
|
|
51
|
-
config?: RouteConfig
|
|
52
51
|
middlewares: string[]
|
|
52
|
+
config?: RouteConfig
|
|
53
|
+
rateLimit?: any
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
export interface GeneralConfig {
|
|
@@ -58,6 +59,7 @@ export interface GeneralConfig {
|
|
|
58
59
|
options: {
|
|
59
60
|
reset_external_id_on_login: boolean
|
|
60
61
|
scheduler: boolean
|
|
62
|
+
embedded_auth: boolean
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
|
|
@@ -88,6 +90,7 @@ export interface ConfiguredRoute {
|
|
|
88
90
|
path: string
|
|
89
91
|
handler: any
|
|
90
92
|
rawBody: boolean
|
|
93
|
+
rateLimit: any
|
|
91
94
|
file: string
|
|
92
95
|
func: any
|
|
93
96
|
base: string
|