@volcanicminds/backend 0.3.6 → 0.5.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/.mocharc.json +3 -0
- package/README.md +1 -0
- package/TODO.md +0 -2
- package/dist/index.js +47 -36
- package/dist/index.js.map +1 -1
- package/dist/lib/api/auth/controller/auth.js +49 -50
- package/dist/lib/api/auth/controller/auth.js.map +1 -1
- package/dist/lib/api/token/controller/token.js +4 -4
- package/dist/lib/api/token/controller/token.js.map +1 -1
- package/dist/lib/api/tool/controller/tool.js +25 -0
- package/dist/lib/api/tool/controller/tool.js.map +1 -0
- package/dist/lib/api/tool/routes.js +26 -0
- package/dist/lib/api/tool/routes.js.map +1 -0
- package/dist/lib/hooks/onRequest.js +1 -7
- package/dist/lib/hooks/onRequest.js.map +1 -1
- package/dist/lib/loader/translation.js +53 -0
- package/dist/lib/loader/translation.js.map +1 -0
- package/dist/lib/locales/en.json +12 -0
- package/dist/lib/locales/it.json +12 -0
- package/dist/lib/util/errors.js +19 -0
- package/dist/lib/util/errors.js.map +1 -0
- package/dist/nodemon.json +7 -0
- package/dist/package.json +100 -0
- package/dist/tsconfig.json +32 -0
- package/index.ts +47 -38
- package/lib/api/auth/controller/auth.ts +52 -53
- package/lib/api/token/controller/token.ts +4 -4
- package/lib/api/tool/controller/tool.ts +13 -0
- package/lib/api/tool/routes.ts +24 -0
- package/lib/loader/translation.ts +67 -0
- package/lib/locales/en.json +12 -0
- package/lib/locales/it.json +12 -0
- package/lib/util/errors.ts +20 -0
- package/package.json +13 -7
- package/test/common/api.ts +80 -0
- package/test/common/bootstrap.ts +33 -0
- package/test/demo/demo.ts +9 -0
- package/test/demo/index.ts +14 -0
- package/test/e2e/index.ts +14 -0
- package/test/index.spec.ts +20 -0
- package/test/unit/index.ts +14 -0
- package/test/unit/semver.ts +24 -0
- package/test/unit/translation.ts +77 -0
- package/tsconfig.json +2 -1
- package/types/global.d.ts +5 -0
- package/test/example.test.js +0 -5
- package/test/semver.test.js +0 -16
|
@@ -5,31 +5,31 @@ export async function register(req: FastifyRequest, reply: FastifyReply) {
|
|
|
5
5
|
const { password1: password, password2, ...data } = req.data()
|
|
6
6
|
|
|
7
7
|
if (!req.server['userManager'].isImplemented()) {
|
|
8
|
-
throw Error('Not implemented')
|
|
8
|
+
throw new Error('Not implemented')
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
if (!data.username) {
|
|
12
|
-
return reply.status(400).send(Error('Username not valid'))
|
|
12
|
+
return reply.status(400).send(new Error('Username not valid'))
|
|
13
13
|
}
|
|
14
14
|
if (!data.email || !regExp.email.test(data.email)) {
|
|
15
|
-
return reply.status(400).send(Error('Email not valid'))
|
|
15
|
+
return reply.status(400).send(new Error('Email not valid'))
|
|
16
16
|
}
|
|
17
17
|
if (!password || !regExp.password.test(password)) {
|
|
18
|
-
return reply.status(400).send(Error('Password not valid'))
|
|
18
|
+
return reply.status(400).send(new Error('Password not valid'))
|
|
19
19
|
}
|
|
20
20
|
if (!password2 || password2 !== password) {
|
|
21
|
-
return reply.status(400).send(Error('Repeated password not match'))
|
|
21
|
+
return reply.status(400).send(new Error('Repeated password not match'))
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
let existings = await req.server['userManager'].retrieveUserByEmail(data.email)
|
|
25
25
|
if (existings) {
|
|
26
|
-
return reply.status(400).send(Error('Email already registered'))
|
|
26
|
+
return reply.status(400).send(new Error('Email already registered'))
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
if ((data.requiredRoles || []).includes('admin')) {
|
|
30
30
|
existings = await req.server['userManager'].findQuery({ 'roles:in': 'admin' })
|
|
31
31
|
if (existings?.records?.length) {
|
|
32
|
-
return reply.status(400).send(Error('User admin already registered'))
|
|
32
|
+
return reply.status(400).send(new Error('User admin already registered'))
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -42,7 +42,7 @@ export async function register(req: FastifyRequest, reply: FastifyReply) {
|
|
|
42
42
|
|
|
43
43
|
const user = await req.server['userManager'].createUser({ ...data, password: password })
|
|
44
44
|
if (!user) {
|
|
45
|
-
return reply.status(400).send(Error('User not registered'))
|
|
45
|
+
return reply.status(400).send(new Error('User not registered'))
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
return user
|
|
@@ -55,18 +55,18 @@ export async function unregister(req: FastifyRequest, reply: FastifyReply) {
|
|
|
55
55
|
let isValid = await req.server['userManager'].isValidUser(user)
|
|
56
56
|
|
|
57
57
|
if (!isValid) {
|
|
58
|
-
return reply.status(403).send(Error('Wrong credentials'))
|
|
58
|
+
return reply.status(403).send(new Error('Wrong credentials'))
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
if (user.blocked) {
|
|
62
|
-
return reply.status(403).send(Error('User blocked'))
|
|
62
|
+
return reply.status(403).send(new Error('User blocked'))
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
user = await req.server['userManager'].disableUserById(user.getId())
|
|
66
66
|
isValid = await req.server['userManager'].isValidUser(user)
|
|
67
67
|
|
|
68
68
|
if (!isValid) {
|
|
69
|
-
return reply.status(400).send(Error('User not valid'))
|
|
69
|
+
return reply.status(400).send(new Error('User not valid'))
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
return { ok: true }
|
|
@@ -76,12 +76,12 @@ export async function validatePassword(req: FastifyRequest, reply: FastifyReply)
|
|
|
76
76
|
const { password } = req.data()
|
|
77
77
|
|
|
78
78
|
if (!password) {
|
|
79
|
-
return reply.status(400).send(Error('Password cannot be null'))
|
|
79
|
+
return reply.status(400).send(new Error('Password cannot be null'))
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
const match = regExp.password.test(password)
|
|
83
83
|
if (!match) {
|
|
84
|
-
return reply.status(400).send(Error('Password is not valid'))
|
|
84
|
+
return reply.status(400).send(new Error('Password is not valid'))
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
return { ok: match }
|
|
@@ -91,26 +91,26 @@ export async function changePassword(req: FastifyRequest, reply: FastifyReply) {
|
|
|
91
91
|
const { email, oldPassword, newPassword1, newPassword2 } = req.data()
|
|
92
92
|
|
|
93
93
|
if (!req.server['userManager'].isImplemented()) {
|
|
94
|
-
throw Error('Not implemented')
|
|
94
|
+
throw new Error('Not implemented')
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
if (!newPassword1 || !regExp.password.test(newPassword1)) {
|
|
98
|
-
return reply.status(400).send(Error('New password is not valid'))
|
|
98
|
+
return reply.status(400).send(new Error('New password is not valid'))
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
if (!newPassword2 || newPassword2 !== newPassword1) {
|
|
102
|
-
return reply.status(400).send(Error('Repeated new password not match'))
|
|
102
|
+
return reply.status(400).send(new Error('Repeated new password not match'))
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
let user = await req.server['userManager'].retrieveUserByPassword(email, oldPassword)
|
|
106
106
|
let isValid = await req.server['userManager'].isValidUser(user)
|
|
107
107
|
|
|
108
108
|
if (!isValid) {
|
|
109
|
-
return reply.status(403).send(Error('Wrong credentials'))
|
|
109
|
+
return reply.status(403).send(new Error('Wrong credentials'))
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
if (user.blocked) {
|
|
113
|
-
return reply.status(403).send(Error('User blocked'))
|
|
113
|
+
return reply.status(403).send(new Error('User blocked'))
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
user = await req.server['userManager'].changePassword(email, newPassword1, oldPassword)
|
|
@@ -122,11 +122,11 @@ export async function forgotPassword(req: FastifyRequest, reply: FastifyReply) {
|
|
|
122
122
|
const { username, email } = req.data()
|
|
123
123
|
|
|
124
124
|
if (!req.server['userManager'].isImplemented()) {
|
|
125
|
-
throw Error('Not implemented')
|
|
125
|
+
throw new Error('Not implemented')
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
if (!username && (!email || (email && !regExp.email.test(email)))) {
|
|
129
|
-
return reply.status(400).send(Error('Missing a valid user identifier'))
|
|
129
|
+
return reply.status(400).send(new Error('Missing a valid user identifier'))
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
let user = null as any
|
|
@@ -139,11 +139,11 @@ export async function forgotPassword(req: FastifyRequest, reply: FastifyReply) {
|
|
|
139
139
|
let isValid = await req.server['userManager'].isValidUser(user)
|
|
140
140
|
|
|
141
141
|
if (!isValid) {
|
|
142
|
-
return reply.status(403).send(Error('Wrong credentials'))
|
|
142
|
+
return reply.status(403).send(new Error('Wrong credentials'))
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
if (user?.blocked) {
|
|
146
|
-
return reply.status(403).send(Error('User blocked'))
|
|
146
|
+
return reply.status(403).send(new Error('User blocked'))
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
user = await req.server['userManager'].forgotPassword(user.email)
|
|
@@ -156,18 +156,18 @@ export async function confirmEmail(req: FastifyRequest, reply: FastifyReply) {
|
|
|
156
156
|
const { code } = req.data()
|
|
157
157
|
|
|
158
158
|
if (!code) {
|
|
159
|
-
return reply.status(400).send(Error('Missing the confirm email token'))
|
|
159
|
+
return reply.status(400).send(new Error('Missing the confirm email token'))
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
let user = await req.server['userManager'].retrieveUserByConfirmationToken(code)
|
|
163
163
|
let isValid = await req.server['userManager'].isValidUser(user)
|
|
164
164
|
|
|
165
165
|
if (!isValid) {
|
|
166
|
-
return reply.status(403).send(Error('Wrong credentials'))
|
|
166
|
+
return reply.status(403).send(new Error('Wrong credentials'))
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
if (user.blocked) {
|
|
170
|
-
return reply.status(403).send(Error('User blocked'))
|
|
170
|
+
return reply.status(403).send(new Error('User blocked'))
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
user = await req.server['userManager'].userConfirmation(user)
|
|
@@ -180,26 +180,26 @@ export async function resetPassword(req: FastifyRequest, reply: FastifyReply) {
|
|
|
180
180
|
const { code, newPassword1, newPassword2 } = req.data()
|
|
181
181
|
|
|
182
182
|
if (!req.server['userManager'].isImplemented()) {
|
|
183
|
-
throw Error('Not implemented')
|
|
183
|
+
throw new Error('Not implemented')
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
if (!newPassword1 || !regExp.password.test(newPassword1)) {
|
|
187
|
-
return reply.status(400).send(Error('New password not valid'))
|
|
187
|
+
return reply.status(400).send(new Error('New password not valid'))
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
if (!newPassword2 || newPassword2 !== newPassword1) {
|
|
191
|
-
return reply.status(400).send(Error('Repeated new password not match'))
|
|
191
|
+
return reply.status(400).send(new Error('Repeated new password not match'))
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
let user = await req.server['userManager'].retrieveUserByResetPasswordToken(code)
|
|
195
195
|
let isValid = await req.server['userManager'].isValidUser(user)
|
|
196
196
|
|
|
197
197
|
if (!isValid) {
|
|
198
|
-
return reply.status(403).send(Error('Wrong credentials'))
|
|
198
|
+
return reply.status(403).send(new Error('Wrong credentials'))
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
if (user.blocked) {
|
|
202
|
-
return reply.status(403).send(Error('User blocked'))
|
|
202
|
+
return reply.status(403).send(new Error('User blocked'))
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
user = await req.server['userManager'].resetPassword(user, newPassword1)
|
|
@@ -211,14 +211,14 @@ export async function login(req: FastifyRequest, reply: FastifyReply) {
|
|
|
211
211
|
const { email, password } = req.data()
|
|
212
212
|
|
|
213
213
|
if (!req.server['userManager'].isImplemented()) {
|
|
214
|
-
throw Error('Not implemented')
|
|
214
|
+
throw new Error('Not implemented')
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
if (!email || !regExp.email.test(email)) {
|
|
218
|
-
return reply.status(400).send(Error('Email not valid'))
|
|
218
|
+
return reply.status(400).send(new Error('Email not valid'))
|
|
219
219
|
}
|
|
220
220
|
if (!password || !regExp.password.test(password)) {
|
|
221
|
-
return reply.status(400).send(Error('Password not valid'))
|
|
221
|
+
return reply.status(400).send(new Error('Password not valid'))
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
const user = await req.server['userManager'].retrieveUserByPassword(email, password)
|
|
@@ -227,22 +227,15 @@ export async function login(req: FastifyRequest, reply: FastifyReply) {
|
|
|
227
227
|
// const isValid = true
|
|
228
228
|
|
|
229
229
|
if (!isValid) {
|
|
230
|
-
return reply.status(403).send(Error('Wrong credentials'))
|
|
230
|
+
return reply.status(403).send(new Error('Wrong credentials'))
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
if (!(user.confirmed === true)) {
|
|
234
|
-
return reply.status(403).send(Error('User email unconfirmed'))
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const isPasswordToBeChanged = req.server['userManager'].isPasswordToBeChanged(user)
|
|
238
|
-
if (isPasswordToBeChanged) {
|
|
239
|
-
return reply
|
|
240
|
-
.status(403)
|
|
241
|
-
.send({ statusCode: 403, code: 'PASSWORD_TO_BE_CHANGED', message: 'The password is expired' })
|
|
234
|
+
return reply.status(403).send(new Error('User email unconfirmed'))
|
|
242
235
|
}
|
|
243
236
|
|
|
244
237
|
if (user.blocked) {
|
|
245
|
-
return reply.status(403).send(Error('User blocked'))
|
|
238
|
+
return reply.status(403).send(new Error('User blocked'))
|
|
246
239
|
}
|
|
247
240
|
|
|
248
241
|
// https://www.iana.org/assignments/jwt/jwt.xhtml
|
|
@@ -263,23 +256,28 @@ export async function refreshToken(req: FastifyRequest, reply: FastifyReply) {
|
|
|
263
256
|
const { token, refreshToken } = req.data()
|
|
264
257
|
|
|
265
258
|
if (!req.server['userManager'].isImplemented()) {
|
|
266
|
-
throw Error('Not implemented')
|
|
259
|
+
throw new Error('Not implemented')
|
|
267
260
|
}
|
|
268
261
|
|
|
269
|
-
const tokenData = (await reply.server.jwt.decode(token)) as
|
|
270
|
-
const
|
|
262
|
+
const tokenData = (await reply.server.jwt.decode(token)) as { sub: number }
|
|
263
|
+
const minAccettable = Math.floor(Date.now() / 1000) - 2592000 // 30 days
|
|
264
|
+
|
|
265
|
+
if (tokenData?.sub > 0 && tokenData?.sub > minAccettable) {
|
|
266
|
+
return reply.status(403).send(new Error('Token too old'))
|
|
267
|
+
}
|
|
271
268
|
|
|
269
|
+
const refreshTokenData = await reply.server.jwt['refreshToken'].verify(refreshToken)
|
|
272
270
|
if (tokenData?.sub && tokenData?.sub !== refreshTokenData?.sub) {
|
|
273
|
-
return reply.status(403).send(Error('Mismatched tokens'))
|
|
271
|
+
return reply.status(403).send(new Error('Mismatched tokens'))
|
|
274
272
|
}
|
|
275
273
|
|
|
276
|
-
const user = await req.server['userManager'].retrieveUserByExternalId(tokenData
|
|
274
|
+
const user = await req.server['userManager'].retrieveUserByExternalId(tokenData.sub)
|
|
277
275
|
const isValid = await req.server['userManager'].isValidUser(user)
|
|
278
276
|
// const user = { confirmed: true, blocked: false, externalId: 123456, roles: [{ code: 'admin' }] }
|
|
279
277
|
// const isValid = true
|
|
280
278
|
|
|
281
279
|
if (!isValid) {
|
|
282
|
-
return reply.status(403).send(Error('Wrong refresh token'))
|
|
280
|
+
return reply.status(403).send(new Error('Wrong refresh token'))
|
|
283
281
|
}
|
|
284
282
|
|
|
285
283
|
const newToken = await reply.jwtSign({ sub: user.externalId })
|
|
@@ -291,7 +289,7 @@ export async function refreshToken(req: FastifyRequest, reply: FastifyReply) {
|
|
|
291
289
|
export async function invalidateTokens(req: FastifyRequest, reply: FastifyReply) {
|
|
292
290
|
let isValid = await req.server['userManager'].isValidUser(req.user)
|
|
293
291
|
if (!req.user || !isValid) {
|
|
294
|
-
return reply.status(403).send(Error('User not linked'))
|
|
292
|
+
return reply.status(403).send(new Error('User not linked'))
|
|
295
293
|
}
|
|
296
294
|
|
|
297
295
|
const user = await req.server['userManager'].resetExternalId(req.user.getId())
|
|
@@ -301,7 +299,7 @@ export async function invalidateTokens(req: FastifyRequest, reply: FastifyReply)
|
|
|
301
299
|
|
|
302
300
|
export async function block(req: FastifyRequest, reply: FastifyReply) {
|
|
303
301
|
if (!req.server['userManager'].isImplemented()) {
|
|
304
|
-
throw Error('Not implemented')
|
|
302
|
+
throw new Error('Not implemented')
|
|
305
303
|
}
|
|
306
304
|
|
|
307
305
|
if (!req.hasRole(roles.admin) && !req.hasRole(roles.backoffice)) {
|
|
@@ -311,13 +309,14 @@ export async function block(req: FastifyRequest, reply: FastifyReply) {
|
|
|
311
309
|
const { id: userId } = req.parameters()
|
|
312
310
|
const { reason } = req.data()
|
|
313
311
|
|
|
314
|
-
|
|
312
|
+
let user = await req.server['userManager'].blockUserById(userId, reason)
|
|
313
|
+
user = await req.server['userManager'].resetExternalId(user.getId())
|
|
315
314
|
return { ok: !!user.getId() }
|
|
316
315
|
}
|
|
317
316
|
|
|
318
317
|
export async function unblock(req: FastifyRequest, reply: FastifyReply) {
|
|
319
318
|
if (!req.server['userManager'].isImplemented()) {
|
|
320
|
-
throw Error('Not implemented')
|
|
319
|
+
throw new Error('Not implemented')
|
|
321
320
|
}
|
|
322
321
|
|
|
323
322
|
if (!req.hasRole(roles.admin) && !req.hasRole(roles.backoffice)) {
|
|
@@ -20,7 +20,7 @@ export async function create(req: FastifyRequest, reply: FastifyReply) {
|
|
|
20
20
|
const data = req.data()
|
|
21
21
|
|
|
22
22
|
if (!data.name) {
|
|
23
|
-
return reply.status(404).send(Error('Token name not valid'))
|
|
23
|
+
return reply.status(404).send(new Error('Token name not valid'))
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// public is the default
|
|
@@ -32,7 +32,7 @@ export async function create(req: FastifyRequest, reply: FastifyReply) {
|
|
|
32
32
|
|
|
33
33
|
let token = await req.server['tokenManager'].createToken(data)
|
|
34
34
|
if (!token || !token.getId() || !token.externalId) {
|
|
35
|
-
return reply.status(400).send(Error('Token not registered'))
|
|
35
|
+
return reply.status(400).send(new Error('Token not registered'))
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
const bearerToken = await reply.jwtSign(
|
|
@@ -42,7 +42,7 @@ export async function create(req: FastifyRequest, reply: FastifyReply) {
|
|
|
42
42
|
}
|
|
43
43
|
)
|
|
44
44
|
if (!bearerToken) {
|
|
45
|
-
return reply.status(400).send(Error('Token not signed'))
|
|
45
|
+
return reply.status(400).send(new Error('Token not signed'))
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
token = await req.server['tokenManager'].updateTokenById(token.getId(), { token: bearerToken })
|
|
@@ -57,7 +57,7 @@ export async function remove(req: FastifyRequest, reply: FastifyReply) {
|
|
|
57
57
|
|
|
58
58
|
let token = await req.server['tokenManager'].retrieveTokenById(id)
|
|
59
59
|
if (!token) {
|
|
60
|
-
return reply.status(403).send(Error('Token not found'))
|
|
60
|
+
return reply.status(403).send(new Error('Token not found'))
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
token = await req.server['tokenManager'].removeTokenById(id)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FastifyReply, FastifyRequest } from 'fastify'
|
|
2
|
+
|
|
3
|
+
export async function synchronizeSchemas(req: FastifyRequest, reply: FastifyReply) {
|
|
4
|
+
if (!req.server['dataBaseManager'].isImplemented()) {
|
|
5
|
+
throw new Error('Not implemented')
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
reply.send({ ok: true })
|
|
9
|
+
|
|
10
|
+
log.warn('Database schema synchronization started')
|
|
11
|
+
await req.server['dataBaseManager'].synchronizeSchemas()
|
|
12
|
+
log.warn('Database schema synchronization finished')
|
|
13
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
config: {
|
|
3
|
+
title: 'Tool functions',
|
|
4
|
+
description: 'Tool functions',
|
|
5
|
+
controller: 'controller',
|
|
6
|
+
tags: ['tool']
|
|
7
|
+
},
|
|
8
|
+
routes: [
|
|
9
|
+
{
|
|
10
|
+
method: 'POST',
|
|
11
|
+
path: '/synchronize-schemas',
|
|
12
|
+
roles: [roles.admin],
|
|
13
|
+
handler: 'tool.synchronizeSchemas',
|
|
14
|
+
middlewares: ['global.isAuthenticated'],
|
|
15
|
+
config: {
|
|
16
|
+
title: 'Synchronize database schema',
|
|
17
|
+
description: 'Synchronize database schema',
|
|
18
|
+
response: {
|
|
19
|
+
200: { $ref: 'defaultResponse#' }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const glob = require('glob')
|
|
3
|
+
const { I18n } = require('i18n')
|
|
4
|
+
|
|
5
|
+
// import { normalizePatterns } from '../util/path'
|
|
6
|
+
|
|
7
|
+
export function load(): any {
|
|
8
|
+
const i18n = new I18n({
|
|
9
|
+
locales: ['en', 'it'],
|
|
10
|
+
defaultLocale: 'en',
|
|
11
|
+
// header: 'accept-language',
|
|
12
|
+
autoReload: false,
|
|
13
|
+
updateFiles: false,
|
|
14
|
+
syncFiles: false,
|
|
15
|
+
extension: '.json',
|
|
16
|
+
prefix: '',
|
|
17
|
+
objectNotation: true,
|
|
18
|
+
// mustacheConfig: { disable: false },
|
|
19
|
+
// directory: './src/locales',
|
|
20
|
+
|
|
21
|
+
logDebugFn: (msg) => log.debug(msg),
|
|
22
|
+
logWarnFn: (msg) => log.warn(msg),
|
|
23
|
+
logErrorFn: (msg) => log.error(msg)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const basePath = path.join(__dirname, '..', 'locales', '*.json').replaceAll('\\', '/')
|
|
27
|
+
|
|
28
|
+
glob.sync(basePath).forEach((f: string) => {
|
|
29
|
+
log.info('* Loading base dictionary %s', path.parse(f).base)
|
|
30
|
+
try {
|
|
31
|
+
const content = require(f)
|
|
32
|
+
addLocaleFile(i18n, path.parse(f).name, content)
|
|
33
|
+
} catch (err) {
|
|
34
|
+
log.error(err)
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const addPath = path.join(process.cwd(), 'src', 'locales', '*.json').replaceAll('\\', '/')
|
|
39
|
+
|
|
40
|
+
glob.sync(addPath).forEach((f: string) => {
|
|
41
|
+
log.info('* Loading additional dictionary %s', path.parse(f).base)
|
|
42
|
+
try {
|
|
43
|
+
const content = require(f)
|
|
44
|
+
addLocaleFile(i18n, path.parse(f).name, content)
|
|
45
|
+
} catch (err) {
|
|
46
|
+
log.error(err)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
i18n.setLocale(i18n.defaultLocale || 'en')
|
|
51
|
+
// let test1 = i18n.__('greeting.formal')
|
|
52
|
+
// let test2 = i18n.__('hello')
|
|
53
|
+
// let test3 = i18n.__('greeting.placeholder.formal', 'UGO')
|
|
54
|
+
// let test4 = i18n.__('greeting.placeholder.formalize:Buondì %s', 'UGO')
|
|
55
|
+
// let test5 = i18n.__('greeting.formal')
|
|
56
|
+
// let test6 = i18n.__({ phrase: 'greeting.formal', locale: 'it' })
|
|
57
|
+
// let test7 = i18n.__({ phrase: 'greeting.placeholder.formal', locale: 'it' }, 'UGO')
|
|
58
|
+
// i18n.setLocale(i18n.defaultLocale || 'en')
|
|
59
|
+
return i18n
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function addLocaleFile(i18n, locale, content) {
|
|
63
|
+
let catalog = i18n && i18n.getCatalog()
|
|
64
|
+
if (catalog && locale && content) {
|
|
65
|
+
catalog[locale] = catalog[locale] ? { ...catalog[locale], ...content } : content
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export class TranslatedError extends Error {
|
|
2
|
+
locale: string
|
|
3
|
+
status: number
|
|
4
|
+
translationCode: string
|
|
5
|
+
translatedMessage: string
|
|
6
|
+
data: any
|
|
7
|
+
|
|
8
|
+
constructor({ translationCode, data = {}, locale = 'en', defaultMessage = null, status = 400 }) {
|
|
9
|
+
super()
|
|
10
|
+
this.name = this.constructor?.name || 'TranslatedError'
|
|
11
|
+
this.locale = locale
|
|
12
|
+
this.status = status
|
|
13
|
+
this.translationCode = translationCode
|
|
14
|
+
this.translatedMessage = global.t.__({ phrase: translationCode || defaultMessage, locale: locale || 'en' }, data)
|
|
15
|
+
this.message = this.translatedMessage || defaultMessage || 'generic error'
|
|
16
|
+
this.data = data
|
|
17
|
+
|
|
18
|
+
Error.captureStackTrace(this, this.constructor || TranslatedError)
|
|
19
|
+
}
|
|
20
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volcanicminds/backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"codename": "turin",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "The volcanic (minds) backend",
|
|
@@ -44,12 +44,13 @@
|
|
|
44
44
|
"server": "cd dist && node server.js",
|
|
45
45
|
"start": "ts-node server.ts",
|
|
46
46
|
"dev": "nodemon --exec \"ts-node\" server.ts",
|
|
47
|
-
"test": "
|
|
47
|
+
"test": "yarn test:full",
|
|
48
|
+
"test:full": "cross-env PORT=2231 NODE_ENV=memory BROWSER=false mocha ./test/index.spec.ts -t 100000",
|
|
48
49
|
"reset": "yarn && yarn upgrade && yarn compile",
|
|
49
50
|
"upgrade-deps": "yarn upgrade-interactive"
|
|
50
51
|
},
|
|
51
52
|
"dependencies": {
|
|
52
|
-
"@apollo/server": "^4.
|
|
53
|
+
"@apollo/server": "^4.3.3",
|
|
53
54
|
"@as-integrations/fastify": "^1.2.0",
|
|
54
55
|
"@fastify/compress": "^6.2.0",
|
|
55
56
|
"@fastify/cors": "^8.2.0",
|
|
@@ -58,19 +59,24 @@
|
|
|
58
59
|
"@fastify/rate-limit": "^7.6.0",
|
|
59
60
|
"@fastify/swagger": "^8.2.0",
|
|
60
61
|
"@fastify/swagger-ui": "^1.3.0",
|
|
61
|
-
"@types/node": "^
|
|
62
|
+
"@types/node": "^20.3.1",
|
|
62
63
|
"dotenv": "^16.0.3",
|
|
63
|
-
"fastify": "^4.
|
|
64
|
+
"fastify": "^4.13.0",
|
|
64
65
|
"glob": "^8.0.3",
|
|
65
66
|
"graphql": "^16.6.0",
|
|
66
|
-
"
|
|
67
|
+
"i18n": "^0.15.1",
|
|
68
|
+
"nanoid": "^4.0.1",
|
|
67
69
|
"object-sizeof": "^1.6.3",
|
|
68
|
-
"pino": "^8.
|
|
70
|
+
"pino": "^8.10.0",
|
|
69
71
|
"pino-pretty": "^9.1.1",
|
|
70
72
|
"root-require": "^0.3.1",
|
|
71
73
|
"semver": "^7.3.8"
|
|
72
74
|
},
|
|
73
75
|
"devDependencies": {
|
|
76
|
+
"@types/mocha": "^10.0.1",
|
|
77
|
+
"cross-env": "^7.0.3",
|
|
78
|
+
"expect": "^29.5.0",
|
|
79
|
+
"mocha": "^10.2.0",
|
|
74
80
|
"nodemon": "^2.0.20",
|
|
75
81
|
"ts-node": "^10.9.1",
|
|
76
82
|
"typescript": "^4.9.3"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const axios = require('axios')
|
|
2
|
+
const http = axios.create({ baseURL: `http://0.0.0.0:${process.env.PORT?.replace(/\\n/gm, '\n') || 2231}/` })
|
|
3
|
+
|
|
4
|
+
import { DEFAULT_ADMIN_EMAIL, DEFAULT_ADMIN_PASSWORD } from '../common/bootstrap'
|
|
5
|
+
|
|
6
|
+
export async function login(email = DEFAULT_ADMIN_EMAIL, password = DEFAULT_ADMIN_PASSWORD) {
|
|
7
|
+
try {
|
|
8
|
+
delete http.defaults.headers.common.Authorization
|
|
9
|
+
const { data } = await http.post('/auth/login', {
|
|
10
|
+
email,
|
|
11
|
+
password
|
|
12
|
+
})
|
|
13
|
+
http.defaults.headers.common['Authorization'] = `Bearer ${data.token}`
|
|
14
|
+
return data
|
|
15
|
+
} catch (err) {
|
|
16
|
+
console.log(err)
|
|
17
|
+
throw err
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function logout() {
|
|
22
|
+
try {
|
|
23
|
+
delete http.defaults.headers.common.Authorization
|
|
24
|
+
} catch (err) {
|
|
25
|
+
throw err
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function get(...args) {
|
|
30
|
+
try {
|
|
31
|
+
const { data } = await http.get(...args)
|
|
32
|
+
return data
|
|
33
|
+
} catch (err) {
|
|
34
|
+
throw err
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function get_with_headers(...args) {
|
|
39
|
+
try {
|
|
40
|
+
const { data, headers } = await http.get(...args)
|
|
41
|
+
return { data, headers }
|
|
42
|
+
} catch (err) {
|
|
43
|
+
throw err
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function post(...args) {
|
|
48
|
+
try {
|
|
49
|
+
const { data } = await http.post(...args)
|
|
50
|
+
return data
|
|
51
|
+
} catch (err) {
|
|
52
|
+
throw err
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function put(...args) {
|
|
57
|
+
try {
|
|
58
|
+
const { data } = await http.put(...args)
|
|
59
|
+
return data
|
|
60
|
+
} catch (err) {
|
|
61
|
+
throw err
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function del(...args) {
|
|
66
|
+
try {
|
|
67
|
+
const { data } = await http.delete(...args)
|
|
68
|
+
return data
|
|
69
|
+
} catch (err) {
|
|
70
|
+
throw err
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function toQueryString(data) {
|
|
75
|
+
let qs = ''
|
|
76
|
+
Object.keys(data).map((k) => {
|
|
77
|
+
qs += `&${k}=${data[k]}`
|
|
78
|
+
})
|
|
79
|
+
return qs.slice(1).replace(' ', '%20')
|
|
80
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const { start: startServer } = require('../../index')
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_ADMIN_EMAIL = 'admin@user.com'
|
|
4
|
+
export const DEFAULT_ADMIN_PASSWORD = '71iD$k%3X#m4'
|
|
5
|
+
export const COMPANY2_SUPERUSER_EMAIL = 'user@volcanicminds.ai'
|
|
6
|
+
export const COMPANY2_SUPERUSER_PASSWORD = '44O$^yWqn@R4'
|
|
7
|
+
|
|
8
|
+
let server: any
|
|
9
|
+
|
|
10
|
+
export async function startUp() {
|
|
11
|
+
try {
|
|
12
|
+
global.log.level = 'warn'
|
|
13
|
+
server = await startServer()
|
|
14
|
+
} catch (err) {
|
|
15
|
+
console.log(err)
|
|
16
|
+
throw err
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function tearDown() {
|
|
21
|
+
await server.close()
|
|
22
|
+
process.exit(0)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function buildTasks() {
|
|
26
|
+
const taskToSkip = (process.env.MOCHA_SKIP_TASK || '').toLowerCase().split(',')
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
demo: !taskToSkip.includes('demo'),
|
|
30
|
+
unit: !taskToSkip.includes('unit'),
|
|
31
|
+
e2e: !taskToSkip.includes('e2e')
|
|
32
|
+
}
|
|
33
|
+
}
|