@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.
Files changed (47) hide show
  1. package/.mocharc.json +3 -0
  2. package/README.md +1 -0
  3. package/TODO.md +0 -2
  4. package/dist/index.js +47 -36
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/api/auth/controller/auth.js +49 -50
  7. package/dist/lib/api/auth/controller/auth.js.map +1 -1
  8. package/dist/lib/api/token/controller/token.js +4 -4
  9. package/dist/lib/api/token/controller/token.js.map +1 -1
  10. package/dist/lib/api/tool/controller/tool.js +25 -0
  11. package/dist/lib/api/tool/controller/tool.js.map +1 -0
  12. package/dist/lib/api/tool/routes.js +26 -0
  13. package/dist/lib/api/tool/routes.js.map +1 -0
  14. package/dist/lib/hooks/onRequest.js +1 -7
  15. package/dist/lib/hooks/onRequest.js.map +1 -1
  16. package/dist/lib/loader/translation.js +53 -0
  17. package/dist/lib/loader/translation.js.map +1 -0
  18. package/dist/lib/locales/en.json +12 -0
  19. package/dist/lib/locales/it.json +12 -0
  20. package/dist/lib/util/errors.js +19 -0
  21. package/dist/lib/util/errors.js.map +1 -0
  22. package/dist/nodemon.json +7 -0
  23. package/dist/package.json +100 -0
  24. package/dist/tsconfig.json +32 -0
  25. package/index.ts +47 -38
  26. package/lib/api/auth/controller/auth.ts +52 -53
  27. package/lib/api/token/controller/token.ts +4 -4
  28. package/lib/api/tool/controller/tool.ts +13 -0
  29. package/lib/api/tool/routes.ts +24 -0
  30. package/lib/loader/translation.ts +67 -0
  31. package/lib/locales/en.json +12 -0
  32. package/lib/locales/it.json +12 -0
  33. package/lib/util/errors.ts +20 -0
  34. package/package.json +13 -7
  35. package/test/common/api.ts +80 -0
  36. package/test/common/bootstrap.ts +33 -0
  37. package/test/demo/demo.ts +9 -0
  38. package/test/demo/index.ts +14 -0
  39. package/test/e2e/index.ts +14 -0
  40. package/test/index.spec.ts +20 -0
  41. package/test/unit/index.ts +14 -0
  42. package/test/unit/semver.ts +24 -0
  43. package/test/unit/translation.ts +77 -0
  44. package/tsconfig.json +2 -1
  45. package/types/global.d.ts +5 -0
  46. package/test/example.test.js +0 -5
  47. 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 any
270
- const refreshTokenData = await reply.server.jwt['refreshToken'].verify(refreshToken)
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?.sub)
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
- const user = await req.server['userManager'].blockUserById(userId, reason)
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,12 @@
1
+ {
2
+ "hello": "Hello",
3
+ "greeting": {
4
+ "formal": "Hello",
5
+ "informal": "Hi",
6
+ "placeholder": {
7
+ "formal": "Hello %s",
8
+ "informal": "Hi %s"
9
+ }
10
+ },
11
+ "complex": "Hello {{user.firstname}} {{user.lastname}}"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "hello": "Ciao",
3
+ "greeting": {
4
+ "formal": "Ciao",
5
+ "informal": "Cià",
6
+ "placeholder": {
7
+ "formal": "Ciao %s",
8
+ "informal": "Cià %s"
9
+ }
10
+ },
11
+ "complex": "Ciao {{user.firstname}} {{user.lastname}}"
12
+ }
@@ -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.6",
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": "jest test --config jest.config.js",
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.2.2",
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": "^18.11.10",
62
+ "@types/node": "^20.3.1",
62
63
  "dotenv": "^16.0.3",
63
- "fastify": "^4.10.2",
64
+ "fastify": "^4.13.0",
64
65
  "glob": "^8.0.3",
65
66
  "graphql": "^16.6.0",
66
- "nanoid": "^4.0.0",
67
+ "i18n": "^0.15.1",
68
+ "nanoid": "^4.0.1",
67
69
  "object-sizeof": "^1.6.3",
68
- "pino": "^8.7.0",
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
+ }
@@ -0,0 +1,9 @@
1
+ import { expect } from 'expect'
2
+
3
+ module.exports = () => {
4
+ describe('A simple test', () => {
5
+ it('should log useless message', async () => {
6
+ expect(100).toBeGreaterThan(0)
7
+ })
8
+ })
9
+ }