@volcanicminds/backend 0.2.43 → 0.3.1
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 +13 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/api/auth/controller/auth.js +15 -17
- package/dist/lib/api/auth/controller/auth.js.map +1 -1
- package/dist/lib/api/auth/routes.js +1 -1
- package/dist/lib/api/auth/routes.js.map +1 -1
- package/dist/lib/api/token/controller/token.js +1 -1
- package/dist/lib/api/token/controller/token.js.map +1 -1
- package/dist/lib/api/token/routes.js +2 -2
- package/dist/lib/api/token/routes.js.map +1 -1
- package/dist/lib/loader/hooks.js +2 -1
- package/dist/lib/loader/hooks.js.map +1 -1
- package/dist/lib/loader/plugins.js +2 -1
- package/dist/lib/loader/plugins.js.map +1 -1
- package/dist/lib/loader/roles.js +2 -1
- package/dist/lib/loader/roles.js.map +1 -1
- package/dist/lib/loader/router.js +2 -1
- package/dist/lib/loader/router.js.map +1 -1
- package/dist/lib/loader/schemas.js +2 -1
- package/dist/lib/loader/schemas.js.map +1 -1
- package/dist/lib/middleware/isAdmin.js +1 -1
- package/dist/lib/middleware/isAdmin.js.map +1 -1
- package/dist/lib/middleware/isAuthenticated.js +1 -1
- package/dist/lib/middleware/isAuthenticated.js.map +1 -1
- package/dist/lib/middleware/preForgotPasswordHandler.js +17 -0
- package/dist/lib/middleware/preForgotPasswordHandler.js.map +1 -0
- package/dist/lib/schemas/token.js +13 -3
- package/dist/lib/schemas/token.js.map +1 -1
- package/dist/lib/util/path.js +12 -0
- package/dist/lib/util/path.js.map +1 -0
- package/index.ts +35 -14
- package/lib/api/auth/controller/auth.ts +76 -16
- package/lib/api/auth/routes.ts +17 -14
- package/lib/api/token/controller/token.ts +1 -1
- package/lib/api/token/routes.ts +2 -2
- package/lib/config/plugins.ts +35 -2
- package/lib/loader/hooks.ts +3 -1
- package/lib/loader/plugins.ts +2 -1
- package/lib/loader/roles.ts +2 -1
- package/lib/loader/router.ts +2 -1
- package/lib/loader/schemas.ts +3 -1
- package/lib/middleware/isAdmin.ts +1 -1
- package/lib/middleware/isAuthenticated.ts +1 -1
- package/lib/middleware/preForgotPasswordHandler.ts +3 -0
- package/lib/schemas/auth.ts +36 -0
- package/lib/schemas/token.ts +13 -2
- package/lib/util/path.ts +8 -0
- package/logo-dark.png +0 -0
- package/nodemon.json +7 -0
- package/package.json +1 -2
- package/types/global.d.ts +2 -0
- package/jest.config.js +0 -188
- package/nodemon.json +0 -15
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.js","sourceRoot":"","sources":["../../../lib/util/path.ts"],"names":[],"mappings":";;;AAAA,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AAC5B,SAAgB,iBAAiB,CAAC,KAAoB,EAAE,KAAoB;IAE1E,OAAO;QACL,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC;QACpD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC;KACzD,CAAA;AACH,CAAC;AAND,8CAMC"}
|
package/index.ts
CHANGED
|
@@ -78,6 +78,9 @@ async function addFastifySwagger(fastify: FastifyInstance) {
|
|
|
78
78
|
if (loadSwagger) {
|
|
79
79
|
log.trace('Add swagger plugin')
|
|
80
80
|
|
|
81
|
+
const fs = require('fs')
|
|
82
|
+
const contents = fs.readFileSync('logo-dark.png', { encoding: 'base64' })
|
|
83
|
+
|
|
81
84
|
await fastify.register(swagger, {
|
|
82
85
|
swagger: {
|
|
83
86
|
info: {
|
|
@@ -118,17 +121,14 @@ async function addFastifySwagger(fastify: FastifyInstance) {
|
|
|
118
121
|
docExpansion: 'list',
|
|
119
122
|
deepLinking: true,
|
|
120
123
|
defaultModelsExpandDepth: 1
|
|
124
|
+
},
|
|
125
|
+
logo: {
|
|
126
|
+
type: 'image/png',
|
|
127
|
+
content: Buffer.from(contents, 'base64')
|
|
128
|
+
},
|
|
129
|
+
theme: {
|
|
130
|
+
title: SWAGGER_TITLE
|
|
121
131
|
}
|
|
122
|
-
// uiHooks: {
|
|
123
|
-
// onRequest: function (request, reply, next) {
|
|
124
|
-
// next()
|
|
125
|
-
// },
|
|
126
|
-
// preHandler: function (request, reply, next) {
|
|
127
|
-
// next()
|
|
128
|
-
// }
|
|
129
|
-
// }
|
|
130
|
-
// staticCSP: true,
|
|
131
|
-
// transformStaticCSP: (header) => header
|
|
132
132
|
})
|
|
133
133
|
}
|
|
134
134
|
}
|
|
@@ -142,8 +142,15 @@ const start = async (decorators) => {
|
|
|
142
142
|
const fastify = await Fastify(opts)
|
|
143
143
|
|
|
144
144
|
const { HOST: host = '0.0.0.0', PORT: port = '2230', GRAPHQL } = process.env
|
|
145
|
-
const {
|
|
146
|
-
|
|
145
|
+
const {
|
|
146
|
+
JWT_SECRET = '',
|
|
147
|
+
JWT_EXPIRES_IN = '15d',
|
|
148
|
+
JWT_REFRESH = 'true',
|
|
149
|
+
JWT_REFRESH_SECRET = '',
|
|
150
|
+
JWT_REFRESH_EXPIRES_IN = '180d'
|
|
151
|
+
} = process.env
|
|
152
|
+
|
|
153
|
+
const loadRefreshJWT = yn(JWT_REFRESH, true)
|
|
147
154
|
const loadApollo = yn(GRAPHQL, false)
|
|
148
155
|
const plugins = loaderPlugins.load()
|
|
149
156
|
|
|
@@ -156,10 +163,18 @@ const start = async (decorators) => {
|
|
|
156
163
|
// JWT Validator
|
|
157
164
|
log.t && log.trace(`Add JWT - expiresIn: ${JWT_EXPIRES_IN}`)
|
|
158
165
|
await fastify.register(jwtValidator, {
|
|
159
|
-
secret: JWT_SECRET
|
|
166
|
+
secret: JWT_SECRET,
|
|
160
167
|
sign: { expiresIn: JWT_EXPIRES_IN }
|
|
161
168
|
})
|
|
162
169
|
|
|
170
|
+
if (loadRefreshJWT) {
|
|
171
|
+
await fastify.register(jwtValidator, {
|
|
172
|
+
namespace: 'refreshToken',
|
|
173
|
+
secret: JWT_REFRESH_SECRET || JWT_SECRET,
|
|
174
|
+
sign: { expiresIn: JWT_REFRESH_EXPIRES_IN }
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
|
|
163
178
|
const apollo = loadApollo ? await attachApollo(fastify) : null
|
|
164
179
|
await addFastifySwagger(fastify)
|
|
165
180
|
await addApolloRouting(fastify, apollo)
|
|
@@ -168,6 +183,9 @@ const start = async (decorators) => {
|
|
|
168
183
|
// defaults
|
|
169
184
|
decorators = {
|
|
170
185
|
userManager: {
|
|
186
|
+
isImplemented() {
|
|
187
|
+
return false
|
|
188
|
+
},
|
|
171
189
|
isValidUser(data: any) {
|
|
172
190
|
throw Error('Not implemented')
|
|
173
191
|
},
|
|
@@ -207,7 +225,7 @@ const start = async (decorators) => {
|
|
|
207
225
|
forgotPassword(email: string) {
|
|
208
226
|
throw Error('Not implemented')
|
|
209
227
|
},
|
|
210
|
-
userConfirmation(
|
|
228
|
+
userConfirmation(user: any) {
|
|
211
229
|
throw Error('Not implemented')
|
|
212
230
|
},
|
|
213
231
|
resetPassword(user: any, password: string) {
|
|
@@ -227,6 +245,9 @@ const start = async (decorators) => {
|
|
|
227
245
|
}
|
|
228
246
|
} as UserManagement,
|
|
229
247
|
tokenManager: {
|
|
248
|
+
isImplemented() {
|
|
249
|
+
return false
|
|
250
|
+
},
|
|
230
251
|
isValidToken(data: any) {
|
|
231
252
|
throw Error('Not implemented')
|
|
232
253
|
},
|
|
@@ -4,31 +4,32 @@ import * as regExp from '../../../util/regexp'
|
|
|
4
4
|
export async function register(req: FastifyRequest, reply: FastifyReply) {
|
|
5
5
|
const { password1: password, password2, ...data } = req.data()
|
|
6
6
|
|
|
7
|
+
if (!req.server['userManager'].isImplemented()) {
|
|
8
|
+
throw Error('Not implemented')
|
|
9
|
+
}
|
|
10
|
+
|
|
7
11
|
if (!data.username) {
|
|
8
|
-
return reply.status(
|
|
12
|
+
return reply.status(400).send(Error('Username not valid'))
|
|
9
13
|
}
|
|
10
14
|
if (!data.email || !regExp.email.test(data.email)) {
|
|
11
|
-
return reply.status(
|
|
15
|
+
return reply.status(400).send(Error('Email not valid'))
|
|
12
16
|
}
|
|
13
17
|
if (!password || !regExp.password.test(password)) {
|
|
14
|
-
return reply.status(
|
|
18
|
+
return reply.status(400).send(Error('Password not valid'))
|
|
15
19
|
}
|
|
16
20
|
if (!password2 || password2 !== password) {
|
|
17
|
-
return reply.status(
|
|
21
|
+
return reply.status(400).send(Error('Repeated password not match'))
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
let existings = await req.server['userManager'].retrieveUserByEmail(data.email)
|
|
21
25
|
if (existings) {
|
|
22
|
-
return reply.status(
|
|
26
|
+
return reply.status(400).send(Error('Email already registered'))
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
console.log('role ' + data.requiredRoles)
|
|
26
29
|
if ((data.requiredRoles || []).includes('admin')) {
|
|
27
|
-
console.log('requiredRoles ' + data.requiredRoles)
|
|
28
30
|
existings = await req.server['userManager'].findQuery({ 'roles:in': 'admin' })
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return reply.status(404).send(Error('User admin already registered'))
|
|
31
|
+
if (existings?.records?.length) {
|
|
32
|
+
return reply.status(400).send(Error('User admin already registered'))
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -89,6 +90,10 @@ export async function validatePassword(req: FastifyRequest, reply: FastifyReply)
|
|
|
89
90
|
export async function changePassword(req: FastifyRequest, reply: FastifyReply) {
|
|
90
91
|
const { email, oldPassword, newPassword1, newPassword2 } = req.data()
|
|
91
92
|
|
|
93
|
+
if (!req.server['userManager'].isImplemented()) {
|
|
94
|
+
throw Error('Not implemented')
|
|
95
|
+
}
|
|
96
|
+
|
|
92
97
|
if (!newPassword1 || !regExp.password.test(newPassword1)) {
|
|
93
98
|
return reply.status(400).send(Error('New password is not valid'))
|
|
94
99
|
}
|
|
@@ -116,6 +121,10 @@ export async function changePassword(req: FastifyRequest, reply: FastifyReply) {
|
|
|
116
121
|
export async function forgotPassword(req: FastifyRequest, reply: FastifyReply) {
|
|
117
122
|
const { username, email } = req.data()
|
|
118
123
|
|
|
124
|
+
if (!req.server['userManager'].isImplemented()) {
|
|
125
|
+
throw Error('Not implemented')
|
|
126
|
+
}
|
|
127
|
+
|
|
119
128
|
if (!username && (!email || (email && !regExp.email.test(email)))) {
|
|
120
129
|
return reply.status(400).send(Error('Missing a valid user identifier'))
|
|
121
130
|
}
|
|
@@ -170,6 +179,10 @@ export async function confirmEmail(req: FastifyRequest, reply: FastifyReply) {
|
|
|
170
179
|
export async function resetPassword(req: FastifyRequest, reply: FastifyReply) {
|
|
171
180
|
const { code, newPassword1, newPassword2 } = req.data()
|
|
172
181
|
|
|
182
|
+
if (!req.server['userManager'].isImplemented()) {
|
|
183
|
+
throw Error('Not implemented')
|
|
184
|
+
}
|
|
185
|
+
|
|
173
186
|
if (!newPassword1 || !regExp.password.test(newPassword1)) {
|
|
174
187
|
return reply.status(400).send(Error('New password not valid'))
|
|
175
188
|
}
|
|
@@ -197,15 +210,21 @@ export async function resetPassword(req: FastifyRequest, reply: FastifyReply) {
|
|
|
197
210
|
export async function login(req: FastifyRequest, reply: FastifyReply) {
|
|
198
211
|
const { email, password } = req.data()
|
|
199
212
|
|
|
213
|
+
if (!req.server['userManager'].isImplemented()) {
|
|
214
|
+
throw Error('Not implemented')
|
|
215
|
+
}
|
|
216
|
+
|
|
200
217
|
if (!email || !regExp.email.test(email)) {
|
|
201
|
-
return reply.status(
|
|
218
|
+
return reply.status(400).send(Error('Email not valid'))
|
|
202
219
|
}
|
|
203
220
|
if (!password || !regExp.password.test(password)) {
|
|
204
|
-
return reply.status(
|
|
221
|
+
return reply.status(400).send(Error('Password not valid'))
|
|
205
222
|
}
|
|
206
223
|
|
|
207
224
|
const user = await req.server['userManager'].retrieveUserByPassword(email, password)
|
|
208
225
|
const isValid = await req.server['userManager'].isValidUser(user)
|
|
226
|
+
// const user = { confirmed: true, blocked: false, externalId: 123456, roles: [{ code: 'admin' }] }
|
|
227
|
+
// const isValid = true
|
|
209
228
|
|
|
210
229
|
if (!isValid) {
|
|
211
230
|
return reply.status(403).send(Error('Wrong credentials'))
|
|
@@ -219,13 +238,46 @@ export async function login(req: FastifyRequest, reply: FastifyReply) {
|
|
|
219
238
|
return reply.status(403).send(Error('User blocked'))
|
|
220
239
|
}
|
|
221
240
|
|
|
222
|
-
// log.trace('User: ' + JSON.stringify(user) + ' ' + roles)
|
|
223
241
|
// https://www.iana.org/assignments/jwt/jwt.xhtml
|
|
224
|
-
const token =
|
|
242
|
+
const token = await reply.jwtSign({ sub: user.externalId })
|
|
243
|
+
const refreshToken = reply.server.jwt['refreshToken']
|
|
244
|
+
? await reply.server.jwt['refreshToken'].sign({ sub: user.externalId })
|
|
245
|
+
: undefined
|
|
246
|
+
|
|
225
247
|
return {
|
|
226
248
|
...user,
|
|
227
|
-
|
|
228
|
-
|
|
249
|
+
roles: (user.roles || [global.role?.public?.code || 'public']).map((r) => r?.code || r),
|
|
250
|
+
token: token,
|
|
251
|
+
refreshToken
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export async function refreshToken(req: FastifyRequest, reply: FastifyReply) {
|
|
256
|
+
const { token, refreshToken } = req.data()
|
|
257
|
+
|
|
258
|
+
if (!req.server['userManager'].isImplemented()) {
|
|
259
|
+
throw Error('Not implemented')
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const tokenData = (await reply.server.jwt.decode(token)) as any
|
|
263
|
+
const refreshTokenData = await reply.server.jwt['refreshToken'].verify(refreshToken)
|
|
264
|
+
|
|
265
|
+
if (tokenData?.sub && tokenData?.sub !== refreshTokenData?.sub) {
|
|
266
|
+
return reply.status(403).send(Error('Mismatched tokens'))
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const user = await req.server['userManager'].retrieveUserByExternalId(tokenData?.sub)
|
|
270
|
+
const isValid = await req.server['userManager'].isValidUser(user)
|
|
271
|
+
// const user = { confirmed: true, blocked: false, externalId: 123456, roles: [{ code: 'admin' }] }
|
|
272
|
+
// const isValid = true
|
|
273
|
+
|
|
274
|
+
if (!isValid) {
|
|
275
|
+
return reply.status(403).send(Error('Wrong refresh token'))
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const newToken = await reply.jwtSign({ sub: user.externalId })
|
|
279
|
+
return {
|
|
280
|
+
token: newToken
|
|
229
281
|
}
|
|
230
282
|
}
|
|
231
283
|
|
|
@@ -241,6 +293,10 @@ export async function invalidateTokens(req: FastifyRequest, reply: FastifyReply)
|
|
|
241
293
|
}
|
|
242
294
|
|
|
243
295
|
export async function block(req: FastifyRequest, reply: FastifyReply) {
|
|
296
|
+
if (!req.server['userManager'].isImplemented()) {
|
|
297
|
+
throw Error('Not implemented')
|
|
298
|
+
}
|
|
299
|
+
|
|
244
300
|
if (!req.hasRole(roles.admin) && !req.hasRole(roles.backoffice)) {
|
|
245
301
|
return reply.status(403).send({ statusCode: 403, code: 'ROLE_NOT_ALLOWED', message: 'Not allowed to block a user' })
|
|
246
302
|
}
|
|
@@ -253,6 +309,10 @@ export async function block(req: FastifyRequest, reply: FastifyReply) {
|
|
|
253
309
|
}
|
|
254
310
|
|
|
255
311
|
export async function unblock(req: FastifyRequest, reply: FastifyReply) {
|
|
312
|
+
if (!req.server['userManager'].isImplemented()) {
|
|
313
|
+
throw Error('Not implemented')
|
|
314
|
+
}
|
|
315
|
+
|
|
256
316
|
if (!req.hasRole(roles.admin) && !req.hasRole(roles.backoffice)) {
|
|
257
317
|
return reply
|
|
258
318
|
.status(403)
|
package/lib/api/auth/routes.ts
CHANGED
|
@@ -74,7 +74,7 @@ module.exports = {
|
|
|
74
74
|
path: '/forgot-password',
|
|
75
75
|
roles: [],
|
|
76
76
|
handler: 'auth.forgotPassword',
|
|
77
|
-
middlewares: ['global.dispatchForgotPasswordLink'],
|
|
77
|
+
middlewares: ['global.preForgotPasswordHandler', 'global.dispatchForgotPasswordLink'],
|
|
78
78
|
config: {
|
|
79
79
|
title: 'Forgot password',
|
|
80
80
|
description: 'Forgot password for an existing user given the email or username',
|
|
@@ -125,19 +125,22 @@ module.exports = {
|
|
|
125
125
|
description: 'Basic login authentication',
|
|
126
126
|
body: { $ref: 'authLoginBodySchema#' },
|
|
127
127
|
response: {
|
|
128
|
-
200: {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
128
|
+
200: { $ref: 'authLoginResponseSchema#' }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
method: 'POST',
|
|
134
|
+
path: '/refresh-token',
|
|
135
|
+
roles: [],
|
|
136
|
+
handler: 'auth.refreshToken',
|
|
137
|
+
middlewares: [],
|
|
138
|
+
config: {
|
|
139
|
+
title: 'Refresh authentication token',
|
|
140
|
+
description: 'Refresh login authentication token',
|
|
141
|
+
body: { $ref: 'authRefreshTokenBodySchema#' },
|
|
142
|
+
response: {
|
|
143
|
+
200: { $ref: 'authRefreshTokenResponseSchema#' }
|
|
141
144
|
}
|
|
142
145
|
}
|
|
143
146
|
},
|
|
@@ -38,7 +38,7 @@ export async function create(req: FastifyRequest, reply: FastifyReply) {
|
|
|
38
38
|
const bearerToken = await reply.jwtSign(
|
|
39
39
|
{ sub: token.externalId },
|
|
40
40
|
{
|
|
41
|
-
sign: { expiresIn: undefined }
|
|
41
|
+
sign: { expiresIn: data?.expiresIn || undefined }
|
|
42
42
|
}
|
|
43
43
|
)
|
|
44
44
|
if (!bearerToken) {
|
package/lib/api/token/routes.ts
CHANGED
|
@@ -72,7 +72,7 @@ module.exports = {
|
|
|
72
72
|
config: {
|
|
73
73
|
title: 'Create new token',
|
|
74
74
|
description: 'Create a new token',
|
|
75
|
-
body: { $ref: '
|
|
75
|
+
body: { $ref: 'tokenCreateBodySchema' },
|
|
76
76
|
response: {
|
|
77
77
|
200: {
|
|
78
78
|
description: 'Default response',
|
|
@@ -91,7 +91,7 @@ module.exports = {
|
|
|
91
91
|
title: 'Update existing token',
|
|
92
92
|
description: 'Update an existing token',
|
|
93
93
|
params: { $ref: 'onlyIdSchema#' },
|
|
94
|
-
body: { $ref: '
|
|
94
|
+
body: { $ref: 'tokenUpdateBodySchema' },
|
|
95
95
|
response: {
|
|
96
96
|
200: {
|
|
97
97
|
description: 'Default response',
|
package/lib/config/plugins.ts
CHANGED
|
@@ -3,8 +3,41 @@
|
|
|
3
3
|
module.exports = [
|
|
4
4
|
{
|
|
5
5
|
name: 'cors',
|
|
6
|
-
enable:
|
|
7
|
-
options: {
|
|
6
|
+
enable: true,
|
|
7
|
+
options: {
|
|
8
|
+
origin: '*',
|
|
9
|
+
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD'],
|
|
10
|
+
maxAge: 31536000,
|
|
11
|
+
credentials: true,
|
|
12
|
+
allowedHeaders: [
|
|
13
|
+
'Accept',
|
|
14
|
+
'Accept-Language',
|
|
15
|
+
'Content-Language',
|
|
16
|
+
'Content-Type',
|
|
17
|
+
'Content-Length',
|
|
18
|
+
'Authorization',
|
|
19
|
+
'Origin',
|
|
20
|
+
'v-total',
|
|
21
|
+
'v-count',
|
|
22
|
+
'v-page',
|
|
23
|
+
'v-pageSize',
|
|
24
|
+
'v-pageCount'
|
|
25
|
+
],
|
|
26
|
+
exposedHeaders: [
|
|
27
|
+
'Accept',
|
|
28
|
+
'Accept-Language',
|
|
29
|
+
'Content-Language',
|
|
30
|
+
'Content-Type',
|
|
31
|
+
'Content-Length',
|
|
32
|
+
'Authorization',
|
|
33
|
+
'Origin',
|
|
34
|
+
'v-total',
|
|
35
|
+
'v-count',
|
|
36
|
+
'v-page',
|
|
37
|
+
'v-pageSize',
|
|
38
|
+
'v-pageCount'
|
|
39
|
+
]
|
|
40
|
+
}
|
|
8
41
|
},
|
|
9
42
|
{
|
|
10
43
|
name: 'rateLimit',
|
package/lib/loader/hooks.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { normalizePatterns } from '../util/path'
|
|
2
|
+
|
|
1
3
|
const hooks = [
|
|
2
4
|
'onRequest',
|
|
3
5
|
'onError',
|
|
@@ -18,7 +20,7 @@ const glob = require('glob')
|
|
|
18
20
|
const path = require('path')
|
|
19
21
|
|
|
20
22
|
export function apply(server: any): void {
|
|
21
|
-
const patterns = [
|
|
23
|
+
const patterns = normalizePatterns(['..', 'hooks', '*.{ts,js}'], ['src', 'hooks', '*.{ts,js}'])
|
|
22
24
|
const allHooks: any = hooks.reduce((acc, v) => ({ ...acc, [v]: [] as Function[] }), {})
|
|
23
25
|
|
|
24
26
|
patterns.forEach((pattern) => {
|
package/lib/loader/plugins.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { config } from 'dotenv'
|
|
2
|
+
import { normalizePatterns } from '../util/path'
|
|
2
3
|
|
|
3
4
|
const glob = require('glob')
|
|
4
5
|
|
|
5
6
|
export function load() {
|
|
6
7
|
const plugins: any = {}
|
|
7
8
|
|
|
8
|
-
const patterns = [
|
|
9
|
+
const patterns = normalizePatterns(['..', 'config', 'plugins.{ts,js}'], ['src', 'config', 'plugins.{ts,js}'])
|
|
9
10
|
patterns.forEach((pattern) => {
|
|
10
11
|
log.t && log.trace('Looking for ' + pattern)
|
|
11
12
|
glob.sync(pattern).forEach((f: string) => {
|
package/lib/loader/roles.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Role, Roles } from '../../types/global'
|
|
2
|
+
import { normalizePatterns } from '../util/path'
|
|
2
3
|
const glob = require('glob')
|
|
3
4
|
|
|
4
5
|
export function load() {
|
|
5
6
|
const roles: Roles = {}
|
|
6
7
|
|
|
7
|
-
const patterns = [
|
|
8
|
+
const patterns = normalizePatterns(['..', 'config', 'roles.{ts,js}'], ['src', 'config', 'roles.{ts,js}'])
|
|
8
9
|
patterns.forEach((pattern) => {
|
|
9
10
|
log.t && log.trace('Looking for ' + pattern)
|
|
10
11
|
glob.sync(pattern).forEach((f: string) => {
|
package/lib/loader/router.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import yn from '../util/yn'
|
|
2
2
|
import { Route, ConfiguredRoute, RouteConfig } from '../../types/global'
|
|
3
3
|
import { FastifyReply, FastifyRequest } from 'fastify'
|
|
4
|
+
import { normalizePatterns } from '../util/path'
|
|
4
5
|
|
|
5
6
|
const glob = require('glob')
|
|
6
7
|
const path = require('path')
|
|
@@ -8,7 +9,7 @@ const methods = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'PATCH', 'OPTIONS']
|
|
|
8
9
|
|
|
9
10
|
export function load(): ConfiguredRoute[] {
|
|
10
11
|
const validRoutes: ConfiguredRoute[] = []
|
|
11
|
-
const patterns = [
|
|
12
|
+
const patterns = normalizePatterns(['..', 'api', '**', 'routes.{ts,js}'], ['src', 'api', '**', 'routes.{ts,js}'])
|
|
12
13
|
const authMiddlewares = ['global.isAuthenticated', 'global.isAdmin']
|
|
13
14
|
|
|
14
15
|
patterns.forEach((pattern) => {
|
package/lib/loader/schemas.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { normalizePatterns } from '../util/path'
|
|
2
|
+
|
|
1
3
|
const glob = require('glob')
|
|
2
4
|
const path = require('path')
|
|
3
5
|
|
|
4
6
|
export function apply(server: any): void {
|
|
5
|
-
const patterns = [
|
|
7
|
+
const patterns = normalizePatterns(['..', 'schemas', '*.{ts,js}'], ['src', 'schemas', '*.{ts,js}'])
|
|
6
8
|
|
|
7
9
|
let schemaCount = 0
|
|
8
10
|
patterns.forEach((pattern) => {
|
|
@@ -9,6 +9,6 @@ export function preHandler(req: FastifyRequest, res: FastifyReply, done: any) {
|
|
|
9
9
|
throw new Error('User without this privilege')
|
|
10
10
|
} catch (err) {
|
|
11
11
|
log.e && log.error(`Upps, something just happened ${err}`)
|
|
12
|
-
res.code(403).send(
|
|
12
|
+
res.code(403).send(new Error('User without this privilege'))
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -9,6 +9,6 @@ export function preHandler(req: FastifyRequest, res: FastifyReply, done: any) {
|
|
|
9
9
|
throw new Error('Unauthorized')
|
|
10
10
|
} catch (err) {
|
|
11
11
|
log.e && log.error(`Upps, something just happened ${err}`)
|
|
12
|
-
return res.code(401).send(
|
|
12
|
+
return res.code(401).send(new Error('Unauthorized')) // must be authorized first
|
|
13
13
|
}
|
|
14
14
|
}
|
package/lib/schemas/auth.ts
CHANGED
|
@@ -7,6 +7,7 @@ export const authLoginBodySchema = {
|
|
|
7
7
|
password: { type: 'string' }
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
+
|
|
10
11
|
export const authForgotPasswordBodySchema = {
|
|
11
12
|
$id: 'authForgotPasswordBodySchema',
|
|
12
13
|
type: 'object',
|
|
@@ -30,6 +31,41 @@ export const authRegisterBodySchema = {
|
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
export const authLoginResponseSchema = {
|
|
35
|
+
$id: 'authLoginResponseSchema',
|
|
36
|
+
type: 'object',
|
|
37
|
+
nullable: true,
|
|
38
|
+
properties: {
|
|
39
|
+
id: { type: 'string' },
|
|
40
|
+
_id: { type: 'string' },
|
|
41
|
+
externalId: { type: 'string' },
|
|
42
|
+
username: { type: 'string' },
|
|
43
|
+
email: { type: 'string' },
|
|
44
|
+
roles: { type: 'array', items: { type: 'string' } },
|
|
45
|
+
token: { type: 'string' },
|
|
46
|
+
refreshToken: { type: 'string' }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const authRefreshTokenBodySchema = {
|
|
51
|
+
$id: 'authRefreshTokenBodySchema',
|
|
52
|
+
type: 'object',
|
|
53
|
+
nullable: true,
|
|
54
|
+
properties: {
|
|
55
|
+
token: { type: 'string' },
|
|
56
|
+
refreshToken: { type: 'string' }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const authRefreshTokenResponseSchema = {
|
|
61
|
+
$id: 'authRefreshTokenResponseSchema',
|
|
62
|
+
type: 'object',
|
|
63
|
+
nullable: true,
|
|
64
|
+
properties: {
|
|
65
|
+
token: { type: 'string' }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
33
69
|
export const authRegisterResponseSchema = {
|
|
34
70
|
$id: 'authRegisterResponseSchema',
|
|
35
71
|
type: 'object',
|
package/lib/schemas/token.ts
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
|
-
export const
|
|
2
|
-
$id: '
|
|
1
|
+
export const tokenCreateBodySchema = {
|
|
2
|
+
$id: 'tokenCreateBodySchema',
|
|
3
3
|
type: 'object',
|
|
4
4
|
nullable: true,
|
|
5
5
|
properties: {
|
|
6
6
|
name: { type: 'string' },
|
|
7
7
|
description: { type: 'string' },
|
|
8
|
+
expiresIn: { type: 'string', default: undefined },
|
|
8
9
|
requiredRoles: { type: 'array', items: { type: 'string' } }
|
|
9
10
|
}
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
export const tokenUpdateBodySchema = {
|
|
14
|
+
$id: 'tokenUpdateBodySchema',
|
|
15
|
+
type: 'object',
|
|
16
|
+
nullable: true,
|
|
17
|
+
properties: {
|
|
18
|
+
name: { type: 'string' },
|
|
19
|
+
description: { type: 'string' }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
export const tokenSchema = {
|
|
13
24
|
$id: 'tokenSchema',
|
|
14
25
|
type: 'object',
|
package/lib/util/path.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
export function normalizePatterns(path1: Array<string>, path2: Array<string>): Array<string> {
|
|
3
|
+
// replaceAll is needed for windows
|
|
4
|
+
return [
|
|
5
|
+
path.join(__dirname, ...path1).replaceAll('\\', '/'),
|
|
6
|
+
path.join(process.cwd(), ...path2).replaceAll('\\', '/')
|
|
7
|
+
]
|
|
8
|
+
}
|
package/logo-dark.png
ADDED
|
Binary file
|
package/nodemon.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volcanicminds/backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"codename": "turin",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "The volcanic (minds) backend",
|
|
@@ -71,7 +71,6 @@
|
|
|
71
71
|
"semver": "^7.3.8"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"jest": "^29.3.1",
|
|
75
74
|
"nodemon": "^2.0.20",
|
|
76
75
|
"ts-node": "^10.9.1",
|
|
77
76
|
"typescript": "^4.9.3"
|
package/types/global.d.ts
CHANGED
|
@@ -75,6 +75,7 @@ export interface ConfiguredRoute {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
export interface UserManagement {
|
|
78
|
+
isImplemented(): boolean
|
|
78
79
|
isValidUser(data: any): boolean
|
|
79
80
|
createUser(data: any): any | null
|
|
80
81
|
resetExternalId(data: any): any | null
|
|
@@ -97,6 +98,7 @@ export interface UserManagement {
|
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
export interface TokenManagement {
|
|
101
|
+
isImplemented(): boolean
|
|
100
102
|
isValidToken(data: any): boolean
|
|
101
103
|
createToken(data: any): any | null
|
|
102
104
|
resetExternalId(id: string): any | null
|