@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.
- package/README.md +13 -1
- package/dist/index.js +25 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/api/auth/controller/auth.js +62 -15
- package/dist/lib/api/auth/controller/auth.js.map +1 -1
- package/dist/lib/api/auth/routes.js +16 -13
- package/dist/lib/api/auth/routes.js.map +1 -1
- package/dist/lib/config/plugins.js +35 -2
- package/dist/lib/config/plugins.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/schemas/auth.js +33 -1
- package/dist/lib/schemas/auth.js.map +1 -1
- package/dist/lib/util/path.js +12 -0
- package/dist/lib/util/path.js.map +1 -0
- package/index.ts +34 -13
- package/lib/api/auth/controller/auth.ts +76 -13
- package/lib/api/auth/routes.ts +16 -13
- 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/schemas/auth.ts +36 -0
- package/lib/util/path.ts +8 -0
- package/logo-dark.png +0 -0
- package/nodemon.json +7 -0
- package/package.json +3 -3
- package/types/global.d.ts +2 -0
- package/jest.config.js +0 -188
- 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,
|
|
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"}
|
package/dist/lib/schemas/auth.js
CHANGED
|
@@ -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;
|
|
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 {
|
|
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(
|
|
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(
|
|
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
29
|
if ((data.requiredRoles || []).includes('admin')) {
|
|
26
30
|
existings = await req.server['userManager'].findQuery({ 'roles:in': 'admin' })
|
|
27
|
-
if (existings) {
|
|
28
|
-
return reply.status(
|
|
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(
|
|
218
|
+
return reply.status(400).send(Error('Email not valid'))
|
|
199
219
|
}
|
|
200
220
|
if (!password || !regExp.password.test(password)) {
|
|
201
|
-
return reply.status(
|
|
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 =
|
|
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
|
-
|
|
225
|
-
|
|
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)
|
package/lib/api/auth/routes.ts
CHANGED
|
@@ -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
|
},
|
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) => {
|
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/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.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
|