@volcanicminds/backend 0.2.44 → 0.3.3

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 (40) hide show
  1. package/README.md +13 -1
  2. package/dist/index.js +25 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/api/auth/controller/auth.js +62 -15
  5. package/dist/lib/api/auth/controller/auth.js.map +1 -1
  6. package/dist/lib/api/auth/routes.js +16 -13
  7. package/dist/lib/api/auth/routes.js.map +1 -1
  8. package/dist/lib/config/plugins.js +35 -2
  9. package/dist/lib/config/plugins.js.map +1 -1
  10. package/dist/lib/loader/hooks.js +2 -1
  11. package/dist/lib/loader/hooks.js.map +1 -1
  12. package/dist/lib/loader/plugins.js +2 -1
  13. package/dist/lib/loader/plugins.js.map +1 -1
  14. package/dist/lib/loader/roles.js +2 -1
  15. package/dist/lib/loader/roles.js.map +1 -1
  16. package/dist/lib/loader/router.js +2 -1
  17. package/dist/lib/loader/router.js.map +1 -1
  18. package/dist/lib/loader/schemas.js +2 -1
  19. package/dist/lib/loader/schemas.js.map +1 -1
  20. package/dist/lib/schemas/auth.js +33 -1
  21. package/dist/lib/schemas/auth.js.map +1 -1
  22. package/dist/lib/util/path.js +12 -0
  23. package/dist/lib/util/path.js.map +1 -0
  24. package/index.ts +34 -13
  25. package/lib/api/auth/controller/auth.ts +76 -13
  26. package/lib/api/auth/routes.ts +16 -13
  27. package/lib/config/plugins.ts +35 -2
  28. package/lib/loader/hooks.ts +3 -1
  29. package/lib/loader/plugins.ts +2 -1
  30. package/lib/loader/roles.ts +2 -1
  31. package/lib/loader/router.ts +2 -1
  32. package/lib/loader/schemas.ts +3 -1
  33. package/lib/schemas/auth.ts +36 -0
  34. package/lib/util/path.ts +8 -0
  35. package/logo-dark.png +0 -0
  36. package/nodemon.json +7 -0
  37. package/package.json +3 -3
  38. package/types/global.d.ts +2 -0
  39. package/jest.config.js +0 -188
  40. package/nodemon.json +0 -15
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../../lib/loader/schemas.ts"],"names":[],"mappings":";;;AAAA,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AAE5B,SAAgB,KAAK,CAAC,MAAW;IAC/B,MAAM,QAAQ,GAAG,CAAC,GAAG,SAAS,uBAAuB,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAA;IAEhG,IAAI,WAAW,GAAG,CAAC,CAAA;IACnB,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,cAAc,GAAG,OAAO,CAAC,CAAA;QAC5C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAS,EAAE,EAAE;YACvC,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;YACvC,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;YAC9B,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;YAE5C,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;gBACvB,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;gBAChC,IAAI,MAAM,IAAI,IAAI,EAAE;oBAClB,IAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,GAAG,EAAE;wBACf,GAAG,CAAC,KAAK,CAAC,aAAa,MAAM,CAAC,GAAG,iBAAiB,cAAc,EAAE,CAAC,CAAA;wBACnE,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;wBACxB,WAAW,EAAE,CAAA;qBACd;yBAAM;wBACL,GAAG,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,GAAG,qBAAqB,cAAc,EAAE,CAAC,CAAA;qBACvE;iBACF;YACH,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,mBAAmB,WAAW,wBAAwB,CAAC,CAAA;AAC5E,CAAC;AA3BD,sBA2BC"}
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../../lib/loader/schemas.ts"],"names":[],"mappings":";;;AAAA,uCAAgD;AAEhD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AAE5B,SAAgB,KAAK,CAAC,MAAW;IAC/B,MAAM,QAAQ,GAAG,IAAA,wBAAiB,EAAC,CAAC,IAAI,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,CAAA;IAEnG,IAAI,WAAW,GAAG,CAAC,CAAA;IACnB,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,cAAc,GAAG,OAAO,CAAC,CAAA;QAC5C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAS,EAAE,EAAE;YACvC,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;YACvC,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;YAC9B,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;YAE5C,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;gBACvB,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;gBAChC,IAAI,MAAM,IAAI,IAAI,EAAE;oBAClB,IAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,GAAG,EAAE;wBACf,GAAG,CAAC,KAAK,CAAC,aAAa,MAAM,CAAC,GAAG,iBAAiB,cAAc,EAAE,CAAC,CAAA;wBACnE,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;wBACxB,WAAW,EAAE,CAAA;qBACd;yBAAM;wBACL,GAAG,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,GAAG,qBAAqB,cAAc,EAAE,CAAC,CAAA;qBACvE;iBACF;YACH,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,mBAAmB,WAAW,wBAAwB,CAAC,CAAA;AAC5E,CAAC;AA3BD,sBA2BC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resetPasswordBodySchema = exports.authChangePasswordBodySchema = exports.authRegisterResponseSchema = exports.authRegisterBodySchema = exports.authForgotPasswordBodySchema = exports.authLoginBodySchema = void 0;
3
+ exports.resetPasswordBodySchema = exports.authChangePasswordBodySchema = exports.authRegisterResponseSchema = exports.authRefreshTokenResponseSchema = exports.authRefreshTokenBodySchema = exports.authLoginResponseSchema = exports.authRegisterBodySchema = exports.authForgotPasswordBodySchema = exports.authLoginBodySchema = void 0;
4
4
  exports.authLoginBodySchema = {
5
5
  $id: 'authLoginBodySchema',
6
6
  type: 'object',
@@ -31,6 +31,38 @@ exports.authRegisterBodySchema = {
31
31
  requiredRoles: { type: 'array', items: { type: 'string' } }
32
32
  }
33
33
  };
34
+ exports.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
+ exports.authRefreshTokenBodySchema = {
50
+ $id: 'authRefreshTokenBodySchema',
51
+ type: 'object',
52
+ nullable: true,
53
+ properties: {
54
+ token: { type: 'string' },
55
+ refreshToken: { type: 'string' }
56
+ }
57
+ };
58
+ exports.authRefreshTokenResponseSchema = {
59
+ $id: 'authRefreshTokenResponseSchema',
60
+ type: 'object',
61
+ nullable: true,
62
+ properties: {
63
+ token: { type: 'string' }
64
+ }
65
+ };
34
66
  exports.authRegisterResponseSchema = {
35
67
  $id: 'authRegisterResponseSchema',
36
68
  type: 'object',
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../lib/schemas/auth.ts"],"names":[],"mappings":";;;AAAa,QAAA,mBAAmB,GAAG;IACjC,GAAG,EAAE,qBAAqB;IAC1B,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE;QACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACzB,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KAC7B;CACF,CAAA;AACY,QAAA,4BAA4B,GAAG;IAC1C,GAAG,EAAE,8BAA8B;IACnC,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE;QACV,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC5B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KAC1B;CACF,CAAA;AAEY,QAAA,sBAAsB,GAAG;IACpC,GAAG,EAAE,wBAAwB;IAC7B,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE;QACV,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC5B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACzB,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC7B,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC7B,aAAa,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;KAC5D;CACF,CAAA;AAEY,QAAA,0BAA0B,GAAG;IACxC,GAAG,EAAE,4BAA4B;IACjC,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE;QACV,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACtB,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACvB,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC9B,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC5B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACzB,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;QAC5B,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;KACpD;CACF,CAAA;AAEY,QAAA,4BAA4B,GAAG;IAC1C,GAAG,EAAE,8BAA8B;IACnC,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE;QACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACzB,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC/B,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAChC,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KACjC;CACF,CAAA;AAEY,QAAA,uBAAuB,GAAG;IACrC,GAAG,EAAE,yBAAyB;IAC9B,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACxB,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAChC,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KACjC;CACF,CAAA"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../lib/schemas/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizePatterns = void 0;
4
+ const path = require('path');
5
+ function normalizePatterns(path1, path2) {
6
+ return [
7
+ path.join(__dirname, ...path1).replaceAll('\\', '/'),
8
+ path.join(process.cwd(), ...path2).replaceAll('\\', '/')
9
+ ];
10
+ }
11
+ exports.normalizePatterns = normalizePatterns;
12
+ //# sourceMappingURL=path.js.map
@@ -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 { JWT_SECRET = '', JWT_EXPIRES_IN = '15d' } = process.env
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
 
@@ -160,6 +167,14 @@ const start = async (decorators) => {
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(email: string) {
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,28 +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(404).send(Error('Username not valid'))
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(404).send(Error('Email not valid'))
15
+ return reply.status(400).send(Error('Email not valid'))
12
16
  }
13
17
  if (!password || !regExp.password.test(password)) {
14
- return reply.status(404).send(Error('Password not valid'))
18
+ return reply.status(400).send(Error('Password not valid'))
15
19
  }
16
20
  if (!password2 || password2 !== password) {
17
- return reply.status(404).send(Error('Repeated password not match'))
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(404).send(Error('Email already registered'))
26
+ return reply.status(400).send(Error('Email already registered'))
23
27
  }
24
28
 
25
29
  if ((data.requiredRoles || []).includes('admin')) {
26
30
  existings = await req.server['userManager'].findQuery({ 'roles:in': 'admin' })
27
- if (existings) {
28
- 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'))
29
33
  }
30
34
  }
31
35
 
@@ -86,6 +90,10 @@ export async function validatePassword(req: FastifyRequest, reply: FastifyReply)
86
90
  export async function changePassword(req: FastifyRequest, reply: FastifyReply) {
87
91
  const { email, oldPassword, newPassword1, newPassword2 } = req.data()
88
92
 
93
+ if (!req.server['userManager'].isImplemented()) {
94
+ throw Error('Not implemented')
95
+ }
96
+
89
97
  if (!newPassword1 || !regExp.password.test(newPassword1)) {
90
98
  return reply.status(400).send(Error('New password is not valid'))
91
99
  }
@@ -113,6 +121,10 @@ export async function changePassword(req: FastifyRequest, reply: FastifyReply) {
113
121
  export async function forgotPassword(req: FastifyRequest, reply: FastifyReply) {
114
122
  const { username, email } = req.data()
115
123
 
124
+ if (!req.server['userManager'].isImplemented()) {
125
+ throw Error('Not implemented')
126
+ }
127
+
116
128
  if (!username && (!email || (email && !regExp.email.test(email)))) {
117
129
  return reply.status(400).send(Error('Missing a valid user identifier'))
118
130
  }
@@ -167,6 +179,10 @@ export async function confirmEmail(req: FastifyRequest, reply: FastifyReply) {
167
179
  export async function resetPassword(req: FastifyRequest, reply: FastifyReply) {
168
180
  const { code, newPassword1, newPassword2 } = req.data()
169
181
 
182
+ if (!req.server['userManager'].isImplemented()) {
183
+ throw Error('Not implemented')
184
+ }
185
+
170
186
  if (!newPassword1 || !regExp.password.test(newPassword1)) {
171
187
  return reply.status(400).send(Error('New password not valid'))
172
188
  }
@@ -194,15 +210,21 @@ export async function resetPassword(req: FastifyRequest, reply: FastifyReply) {
194
210
  export async function login(req: FastifyRequest, reply: FastifyReply) {
195
211
  const { email, password } = req.data()
196
212
 
213
+ if (!req.server['userManager'].isImplemented()) {
214
+ throw Error('Not implemented')
215
+ }
216
+
197
217
  if (!email || !regExp.email.test(email)) {
198
- return reply.status(404).send(Error('Email not valid'))
218
+ return reply.status(400).send(Error('Email not valid'))
199
219
  }
200
220
  if (!password || !regExp.password.test(password)) {
201
- return reply.status(404).send(Error('Password not valid'))
221
+ return reply.status(400).send(Error('Password not valid'))
202
222
  }
203
223
 
204
224
  const user = await req.server['userManager'].retrieveUserByPassword(email, password)
205
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
206
228
 
207
229
  if (!isValid) {
208
230
  return reply.status(403).send(Error('Wrong credentials'))
@@ -216,13 +238,46 @@ export async function login(req: FastifyRequest, reply: FastifyReply) {
216
238
  return reply.status(403).send(Error('User blocked'))
217
239
  }
218
240
 
219
- // log.trace('User: ' + JSON.stringify(user) + ' ' + roles)
220
241
  // https://www.iana.org/assignments/jwt/jwt.xhtml
221
- const token = user !== null ? await reply.jwtSign({ sub: user.externalId }) : null
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
+
222
247
  return {
223
248
  ...user,
224
- token: token || null,
225
- roles: (user.roles || [global.role?.public?.code || 'public']).map((r) => r?.code || r)
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
226
281
  }
227
282
  }
228
283
 
@@ -238,6 +293,10 @@ export async function invalidateTokens(req: FastifyRequest, reply: FastifyReply)
238
293
  }
239
294
 
240
295
  export async function block(req: FastifyRequest, reply: FastifyReply) {
296
+ if (!req.server['userManager'].isImplemented()) {
297
+ throw Error('Not implemented')
298
+ }
299
+
241
300
  if (!req.hasRole(roles.admin) && !req.hasRole(roles.backoffice)) {
242
301
  return reply.status(403).send({ statusCode: 403, code: 'ROLE_NOT_ALLOWED', message: 'Not allowed to block a user' })
243
302
  }
@@ -250,6 +309,10 @@ export async function block(req: FastifyRequest, reply: FastifyReply) {
250
309
  }
251
310
 
252
311
  export async function unblock(req: FastifyRequest, reply: FastifyReply) {
312
+ if (!req.server['userManager'].isImplemented()) {
313
+ throw Error('Not implemented')
314
+ }
315
+
253
316
  if (!req.hasRole(roles.admin) && !req.hasRole(roles.backoffice)) {
254
317
  return reply
255
318
  .status(403)
@@ -125,19 +125,22 @@ module.exports = {
125
125
  description: 'Basic login authentication',
126
126
  body: { $ref: 'authLoginBodySchema#' },
127
127
  response: {
128
- 200: {
129
- description: 'Default response',
130
- type: 'object',
131
- properties: {
132
- id: { type: 'string' },
133
- _id: { type: 'string' },
134
- externalId: { type: 'string' },
135
- username: { type: 'string' },
136
- email: { type: 'string' },
137
- roles: { type: 'array', items: { type: 'string' } },
138
- token: { type: 'string' }
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
  },
@@ -3,8 +3,41 @@
3
3
  module.exports = [
4
4
  {
5
5
  name: 'cors',
6
- enable: false,
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',
@@ -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 = [`${__dirname}/../hooks/*.{ts,js}`, `${process.cwd()}/src/hooks/*.{ts,js}`]
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) => {
@@ -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 = [`${__dirname}/../config/plugins.{ts,js}`, `${process.cwd()}/src/config/plugins.{ts,js}`]
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) => {
@@ -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 = [`${__dirname}/../config/roles.{ts,js}`, `${process.cwd()}/src/config/roles.{ts,js}`]
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) => {
@@ -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 = [`${__dirname}/../api/**/routes.{ts,js}`, `${process.cwd()}/src/api/**/routes.{ts,js}`]
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) => {
@@ -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 = [`${__dirname}/../schemas/*.{ts,js}`, `${process.cwd()}/src/schemas/*.{ts,js}`]
7
+ const patterns = normalizePatterns(['..', 'schemas', '*.{ts,js}'], ['src', 'schemas', '*.{ts,js}'])
6
8
 
7
9
  let schemaCount = 0
8
10
  patterns.forEach((pattern) => {
@@ -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',
@@ -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
@@ -0,0 +1,7 @@
1
+ {
2
+ "watch": ["lib"],
3
+ "verbose": true,
4
+ "ext": "js,ts,json,jsonc",
5
+ "ignore": [".git", "coverage", "dist", "lib/**/*.spec.ts", "node_modules"],
6
+ "exec": "ts-node ./lib/index.ts"
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volcanicminds/backend",
3
- "version": "0.2.44",
3
+ "version": "0.3.3",
4
4
  "codename": "turin",
5
5
  "license": "MIT",
6
6
  "description": "The volcanic (minds) backend",
@@ -46,7 +46,8 @@
46
46
  "dev": "nodemon --exec \"ts-node\" server.ts",
47
47
  "test": "jest test --config jest.config.js",
48
48
  "reset": "yarn && yarn upgrade && yarn compile",
49
- "upgrade-deps": "yarn upgrade-interactive"
49
+ "upgrade-deps": "yarn upgrade-interactive",
50
+ "publish": "yarn && yarn compile && npm publish --access public"
50
51
  },
51
52
  "dependencies": {
52
53
  "@apollo/server": "^4.2.2",
@@ -71,7 +72,6 @@
71
72
  "semver": "^7.3.8"
72
73
  },
73
74
  "devDependencies": {
74
- "jest": "^29.3.1",
75
75
  "nodemon": "^2.0.20",
76
76
  "ts-node": "^10.9.1",
77
77
  "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