@thzero/library_server_fastify 0.17.5 → 0.17.6
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 +17 -17
- package/boot/index.js +367 -367
- package/boot/plugins/admin/news.js +11 -11
- package/boot/plugins/admin/users.js +11 -11
- package/boot/plugins/api.js +16 -16
- package/boot/plugins/apiFront.js +21 -21
- package/boot/plugins/news.js +11 -11
- package/boot/plugins/users.js +11 -11
- package/boot/plugins/usersExtended.js +6 -6
- package/license.md +8 -8
- package/middleware/authentication.js +94 -94
- package/middleware/authorization.js +220 -220
- package/openSource.js +80 -80
- package/package.json +41 -41
- package/plugins/apiKey.js +48 -48
- package/plugins/auth.js +124 -124
- package/plugins/responseTime.js +111 -111
- package/plugins/settings.js +12 -12
- package/plugins/usageMetrics.js +24 -24
- package/routes/admin/index.js +140 -140
- package/routes/admin/news.js +22 -22
- package/routes/admin/users.js +26 -26
- package/routes/baseNews.js +45 -45
- package/routes/baseUsers.js +153 -153
- package/routes/home.js +28 -28
- package/routes/index.js +41 -41
- package/routes/news.js +6 -6
- package/routes/plans.js +39 -39
- package/routes/users.js +6 -6
- package/routes/utility.js +80 -80
- package/routes/version.js +39 -39
package/package.json
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@thzero/library_server_fastify",
|
|
3
|
-
"type": "module",
|
|
4
|
-
"version": "0.17.
|
|
5
|
-
"version_major": 0,
|
|
6
|
-
"version_minor": 17,
|
|
7
|
-
"version_patch":
|
|
8
|
-
"version_date": "03/18/2023",
|
|
9
|
-
"description": "An opinionated library of common functionality to bootstrap a Fastify based API application.",
|
|
10
|
-
"author": "thZero",
|
|
11
|
-
"license": "MIT",
|
|
12
|
-
"repository": {
|
|
13
|
-
"type": "git",
|
|
14
|
-
"url": "git+https://github.com/thzero/library_server_fastify.git"
|
|
15
|
-
},
|
|
16
|
-
"bugs": {
|
|
17
|
-
"url": "https://github.com/thzero/library_server_fastify/issues"
|
|
18
|
-
},
|
|
19
|
-
"homepage": "https://github.com/thzero/library_server_fastify#readme",
|
|
20
|
-
"engines": {
|
|
21
|
-
"node": ">=12.8.3"
|
|
22
|
-
},
|
|
23
|
-
"scripts": {
|
|
24
|
-
"cli-update": "library-cli --updateversion --pi",
|
|
25
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
26
|
-
},
|
|
27
|
-
"dependencies": {
|
|
28
|
-
"@fastify/auth": "^4.2.0",
|
|
29
|
-
"@fastify/compress": "^6.2.0",
|
|
30
|
-
"@fastify/cors": "^8.2.
|
|
31
|
-
"@fastify/helmet": "^10.1.0",
|
|
32
|
-
"@fastify/routes": "^5.1.0",
|
|
33
|
-
"@fastify/static": "^6.9.0",
|
|
34
|
-
"async-mutex": "^0.4.0",
|
|
35
|
-
"fastify": "^4.14.1"
|
|
36
|
-
},
|
|
37
|
-
"peerDependencies": {
|
|
38
|
-
"@thzero/library_common": "^0.17",
|
|
39
|
-
"@thzero/library_common_service": "^0.17",
|
|
40
|
-
"@thzero/library_server": "^0.17"
|
|
41
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@thzero/library_server_fastify",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.17.6",
|
|
5
|
+
"version_major": 0,
|
|
6
|
+
"version_minor": 17,
|
|
7
|
+
"version_patch": 6,
|
|
8
|
+
"version_date": "03/18/2023",
|
|
9
|
+
"description": "An opinionated library of common functionality to bootstrap a Fastify based API application.",
|
|
10
|
+
"author": "thZero",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/thzero/library_server_fastify.git"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/thzero/library_server_fastify/issues"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/thzero/library_server_fastify#readme",
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=12.8.3"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"cli-update": "library-cli --updateversion --pi",
|
|
25
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@fastify/auth": "^4.2.0",
|
|
29
|
+
"@fastify/compress": "^6.2.0",
|
|
30
|
+
"@fastify/cors": "^8.2.1",
|
|
31
|
+
"@fastify/helmet": "^10.1.0",
|
|
32
|
+
"@fastify/routes": "^5.1.0",
|
|
33
|
+
"@fastify/static": "^6.9.0",
|
|
34
|
+
"async-mutex": "^0.4.0",
|
|
35
|
+
"fastify": "^4.14.1"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@thzero/library_common": "^0.17",
|
|
39
|
+
"@thzero/library_common_service": "^0.17",
|
|
40
|
+
"@thzero/library_server": "^0.17"
|
|
41
|
+
}
|
|
42
42
|
}
|
package/plugins/apiKey.js
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
import fastifyPlugin from 'fastify-plugin';
|
|
2
|
-
|
|
3
|
-
import LibraryServerConstants from '@thzero/library_server/constants.js';
|
|
4
|
-
|
|
5
|
-
export default fastifyPlugin((instance, opts, done) => {
|
|
6
|
-
instance.addHook('onRequest', (request, reply, next) => {
|
|
7
|
-
if (request.originalUrl === '/favicon.ico') {
|
|
8
|
-
next();
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const key = request.headers[LibraryServerConstants.Headers.AuthKeys.API];
|
|
13
|
-
// opts.logger.debug('KoaBootMain', 'start', 'auth-api-token.key', key);
|
|
14
|
-
if (!String.isNullOrEmpty(key)) {
|
|
15
|
-
const auth = request.config.get('auth');
|
|
16
|
-
if (auth) {
|
|
17
|
-
const apiKey = auth.apiKey;
|
|
18
|
-
// this.loggerServiceI.debug('KoaBootMain', 'start', 'auth-api-token.apiKey', apiKey);
|
|
19
|
-
// this.loggerServiceI.debug('KoaBootMain', 'start', 'auth-api-token.key===apiKey', (key === apiKey));
|
|
20
|
-
if (key === apiKey) {
|
|
21
|
-
request.apiKey = key;
|
|
22
|
-
next();
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
(async () => {
|
|
29
|
-
const usageMetrics = {
|
|
30
|
-
url: request.routerPath,
|
|
31
|
-
correlationId: request.correlationId,
|
|
32
|
-
href: request.url,
|
|
33
|
-
headers: request.headers,
|
|
34
|
-
host: request.hostname,
|
|
35
|
-
hostname: request.hostname,
|
|
36
|
-
querystring: request.query,
|
|
37
|
-
token: request.token
|
|
38
|
-
};
|
|
39
|
-
await opts.usageMetrics.register(usageMetrics).catch((err) => {
|
|
40
|
-
opts.logger.error('FastifyBootMain', 'start', 'usageMetrics', err);
|
|
41
|
-
});
|
|
42
|
-
})();
|
|
43
|
-
|
|
44
|
-
console.log('Unauthorized... auth-api-token failure');
|
|
45
|
-
reply.status(401).send();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
done();
|
|
1
|
+
import fastifyPlugin from 'fastify-plugin';
|
|
2
|
+
|
|
3
|
+
import LibraryServerConstants from '@thzero/library_server/constants.js';
|
|
4
|
+
|
|
5
|
+
export default fastifyPlugin((instance, opts, done) => {
|
|
6
|
+
instance.addHook('onRequest', (request, reply, next) => {
|
|
7
|
+
if (request.originalUrl === '/favicon.ico') {
|
|
8
|
+
next();
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const key = request.headers[LibraryServerConstants.Headers.AuthKeys.API];
|
|
13
|
+
// opts.logger.debug('KoaBootMain', 'start', 'auth-api-token.key', key);
|
|
14
|
+
if (!String.isNullOrEmpty(key)) {
|
|
15
|
+
const auth = request.config.get('auth');
|
|
16
|
+
if (auth) {
|
|
17
|
+
const apiKey = auth.apiKey;
|
|
18
|
+
// this.loggerServiceI.debug('KoaBootMain', 'start', 'auth-api-token.apiKey', apiKey);
|
|
19
|
+
// this.loggerServiceI.debug('KoaBootMain', 'start', 'auth-api-token.key===apiKey', (key === apiKey));
|
|
20
|
+
if (key === apiKey) {
|
|
21
|
+
request.apiKey = key;
|
|
22
|
+
next();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
(async () => {
|
|
29
|
+
const usageMetrics = {
|
|
30
|
+
url: request.routerPath,
|
|
31
|
+
correlationId: request.correlationId,
|
|
32
|
+
href: request.url,
|
|
33
|
+
headers: request.headers,
|
|
34
|
+
host: request.hostname,
|
|
35
|
+
hostname: request.hostname,
|
|
36
|
+
querystring: request.query,
|
|
37
|
+
token: request.token
|
|
38
|
+
};
|
|
39
|
+
await opts.usageMetrics.register(usageMetrics).catch((err) => {
|
|
40
|
+
opts.logger.error('FastifyBootMain', 'start', 'usageMetrics', err);
|
|
41
|
+
});
|
|
42
|
+
})();
|
|
43
|
+
|
|
44
|
+
console.log('Unauthorized... auth-api-token failure');
|
|
45
|
+
reply.status(401).send();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
done();
|
|
49
49
|
});
|
package/plugins/auth.js
CHANGED
|
@@ -1,125 +1,125 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
// const fp = require('fastify-plugin')
|
|
4
|
-
import fp from 'fastify-plugin'
|
|
5
|
-
// const reusify = require('reusify')
|
|
6
|
-
import reusify from 'reusify'
|
|
7
|
-
|
|
8
|
-
function checkAuth (fastify, opts, next) {
|
|
9
|
-
fastify.decorate('auth', auth)
|
|
10
|
-
next()
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function auth (functions, opts) {
|
|
14
|
-
if (!Array.isArray(functions)) {
|
|
15
|
-
throw new Error('You must give an array of functions to the auth function')
|
|
16
|
-
}
|
|
17
|
-
if (!functions.length) {
|
|
18
|
-
throw new Error('Missing auth functions')
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const options = Object.assign({
|
|
22
|
-
relation: 'or',
|
|
23
|
-
run: null
|
|
24
|
-
}, opts)
|
|
25
|
-
|
|
26
|
-
if (options.relation !== 'or' && options.relation !== 'and') {
|
|
27
|
-
throw new Error('The value of options.relation should be one of [\'or\', \'and\']')
|
|
28
|
-
}
|
|
29
|
-
if (options.run && options.run !== 'all') {
|
|
30
|
-
throw new Error('The value of options.run must be \'all\'')
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/* eslint-disable-next-line no-var */
|
|
34
|
-
for (var i = 0; i < functions.length; i++) {
|
|
35
|
-
functions[i] = functions[i].bind(this)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const instance = reusify(Auth)
|
|
39
|
-
|
|
40
|
-
function _auth (request, reply, done) {
|
|
41
|
-
const obj = instance.get()
|
|
42
|
-
|
|
43
|
-
obj.request = request
|
|
44
|
-
obj.reply = reply
|
|
45
|
-
obj.done = done
|
|
46
|
-
obj.functions = this.functions
|
|
47
|
-
obj.options = this.options
|
|
48
|
-
obj.i = 0
|
|
49
|
-
obj.start = true
|
|
50
|
-
obj.firstResult = null
|
|
51
|
-
|
|
52
|
-
obj.nextAuth()
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return _auth.bind({ functions, options })
|
|
56
|
-
|
|
57
|
-
function Auth () {
|
|
58
|
-
this.next = null
|
|
59
|
-
this.i = 0
|
|
60
|
-
this.start = true
|
|
61
|
-
this.functions = []
|
|
62
|
-
this.options = {}
|
|
63
|
-
this.request = null
|
|
64
|
-
this.reply = null
|
|
65
|
-
this.done = null
|
|
66
|
-
this.firstResult = null
|
|
67
|
-
|
|
68
|
-
const that = this
|
|
69
|
-
|
|
70
|
-
this.nextAuth = function nextAuth (err) {
|
|
71
|
-
const func = that.functions[that.i++]
|
|
72
|
-
|
|
73
|
-
if (!func) {
|
|
74
|
-
that.completeAuth(err)
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const maybePromise = func(that.request, that.reply, that.onAuth, that.options)
|
|
79
|
-
|
|
80
|
-
if (maybePromise && typeof maybePromise.then === 'function') {
|
|
81
|
-
maybePromise.then(results => that.onAuth(null, results), that.onAuth)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
this.onAuth = function onAuth (err, results) {
|
|
86
|
-
if (that.options.relation === 'or') {
|
|
87
|
-
if (err) {
|
|
88
|
-
return that.nextAuth(err)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return that.completeAuth()
|
|
92
|
-
} else {
|
|
93
|
-
if (err) {
|
|
94
|
-
return that.completeAuth(err)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return that.nextAuth(err)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
this.completeAuth = function (err) {
|
|
102
|
-
if (that.start) {
|
|
103
|
-
that.start = false
|
|
104
|
-
that.firstResult = err
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (that.options.run === 'all' && that.i < that.functions.length) {
|
|
108
|
-
return that.nextAuth(err)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (that.firstResult && (!that.reply.raw.statusCode || that.reply.raw.statusCode < 400)) {
|
|
112
|
-
that.reply.code(401)
|
|
113
|
-
} else if (!that.firstResult && that.reply.raw.statusCode && that.reply.raw.statusCode >= 400) {
|
|
114
|
-
that.reply.code(200)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
that.done(that.firstResult)
|
|
118
|
-
instance.release(that)
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export default fp(checkAuth, {
|
|
124
|
-
fastify: '4.x'
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// const fp = require('fastify-plugin')
|
|
4
|
+
import fp from 'fastify-plugin'
|
|
5
|
+
// const reusify = require('reusify')
|
|
6
|
+
import reusify from 'reusify'
|
|
7
|
+
|
|
8
|
+
function checkAuth (fastify, opts, next) {
|
|
9
|
+
fastify.decorate('auth', auth)
|
|
10
|
+
next()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function auth (functions, opts) {
|
|
14
|
+
if (!Array.isArray(functions)) {
|
|
15
|
+
throw new Error('You must give an array of functions to the auth function')
|
|
16
|
+
}
|
|
17
|
+
if (!functions.length) {
|
|
18
|
+
throw new Error('Missing auth functions')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const options = Object.assign({
|
|
22
|
+
relation: 'or',
|
|
23
|
+
run: null
|
|
24
|
+
}, opts)
|
|
25
|
+
|
|
26
|
+
if (options.relation !== 'or' && options.relation !== 'and') {
|
|
27
|
+
throw new Error('The value of options.relation should be one of [\'or\', \'and\']')
|
|
28
|
+
}
|
|
29
|
+
if (options.run && options.run !== 'all') {
|
|
30
|
+
throw new Error('The value of options.run must be \'all\'')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* eslint-disable-next-line no-var */
|
|
34
|
+
for (var i = 0; i < functions.length; i++) {
|
|
35
|
+
functions[i] = functions[i].bind(this)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const instance = reusify(Auth)
|
|
39
|
+
|
|
40
|
+
function _auth (request, reply, done) {
|
|
41
|
+
const obj = instance.get()
|
|
42
|
+
|
|
43
|
+
obj.request = request
|
|
44
|
+
obj.reply = reply
|
|
45
|
+
obj.done = done
|
|
46
|
+
obj.functions = this.functions
|
|
47
|
+
obj.options = this.options
|
|
48
|
+
obj.i = 0
|
|
49
|
+
obj.start = true
|
|
50
|
+
obj.firstResult = null
|
|
51
|
+
|
|
52
|
+
obj.nextAuth()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return _auth.bind({ functions, options })
|
|
56
|
+
|
|
57
|
+
function Auth () {
|
|
58
|
+
this.next = null
|
|
59
|
+
this.i = 0
|
|
60
|
+
this.start = true
|
|
61
|
+
this.functions = []
|
|
62
|
+
this.options = {}
|
|
63
|
+
this.request = null
|
|
64
|
+
this.reply = null
|
|
65
|
+
this.done = null
|
|
66
|
+
this.firstResult = null
|
|
67
|
+
|
|
68
|
+
const that = this
|
|
69
|
+
|
|
70
|
+
this.nextAuth = function nextAuth (err) {
|
|
71
|
+
const func = that.functions[that.i++]
|
|
72
|
+
|
|
73
|
+
if (!func) {
|
|
74
|
+
that.completeAuth(err)
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const maybePromise = func(that.request, that.reply, that.onAuth, that.options)
|
|
79
|
+
|
|
80
|
+
if (maybePromise && typeof maybePromise.then === 'function') {
|
|
81
|
+
maybePromise.then(results => that.onAuth(null, results), that.onAuth)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
this.onAuth = function onAuth (err, results) {
|
|
86
|
+
if (that.options.relation === 'or') {
|
|
87
|
+
if (err) {
|
|
88
|
+
return that.nextAuth(err)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return that.completeAuth()
|
|
92
|
+
} else {
|
|
93
|
+
if (err) {
|
|
94
|
+
return that.completeAuth(err)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return that.nextAuth(err)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this.completeAuth = function (err) {
|
|
102
|
+
if (that.start) {
|
|
103
|
+
that.start = false
|
|
104
|
+
that.firstResult = err
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (that.options.run === 'all' && that.i < that.functions.length) {
|
|
108
|
+
return that.nextAuth(err)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (that.firstResult && (!that.reply.raw.statusCode || that.reply.raw.statusCode < 400)) {
|
|
112
|
+
that.reply.code(401)
|
|
113
|
+
} else if (!that.firstResult && that.reply.raw.statusCode && that.reply.raw.statusCode >= 400) {
|
|
114
|
+
that.reply.code(200)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
that.done(that.firstResult)
|
|
118
|
+
instance.release(that)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default fp(checkAuth, {
|
|
124
|
+
fastify: '4.x'
|
|
125
125
|
})
|
package/plugins/responseTime.js
CHANGED
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
// https://github.com/lolo32/fastify-response-time
|
|
2
|
-
import fastifyPlugin from 'fastify-plugin';
|
|
3
|
-
|
|
4
|
-
const symbolRequestTime = Symbol('RequestTimer');
|
|
5
|
-
const symbolServerTiming = Symbol('ServerTiming');
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
*
|
|
9
|
-
* @param {string} name
|
|
10
|
-
* @param {number|string} duration
|
|
11
|
-
* @param {string} description
|
|
12
|
-
* @return {string}
|
|
13
|
-
*/
|
|
14
|
-
const genTick = (name, duration, description) => {
|
|
15
|
-
let val = name;
|
|
16
|
-
// Parse duration. If could not be converted to float, does not add it
|
|
17
|
-
duration = parseFloat(duration);
|
|
18
|
-
if (!isNaN(duration)) {
|
|
19
|
-
val += `;dur=${duration}`;
|
|
20
|
-
}
|
|
21
|
-
// Parse the description. If empty, doest not add it. If string with space, double quote value
|
|
22
|
-
if ('string' === typeof description) {
|
|
23
|
-
val += `;desc=${description.includes(' ') ? `'${description}'` : description}`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return val;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Decorators
|
|
31
|
-
*
|
|
32
|
-
* @param {fastify} instance
|
|
33
|
-
* @param {function} instance.decorateReply
|
|
34
|
-
* @param {Object} opts
|
|
35
|
-
* @param {function} next
|
|
36
|
-
*/
|
|
37
|
-
export default fastifyPlugin((instance, opts, done) => {
|
|
38
|
-
// Check the options, and corrects with the default values if inadequate
|
|
39
|
-
if (isNaN(opts.digits) || 0 > opts.digits) {
|
|
40
|
-
opts.digits = 2;
|
|
41
|
-
}
|
|
42
|
-
opts.header = opts.header || 'X-Response-Time';
|
|
43
|
-
|
|
44
|
-
// Hook to be triggered on request (start time)
|
|
45
|
-
instance.addHook('onRequest', (request, reply, next) => {
|
|
46
|
-
// Store the start timer in nanoseconds resolution
|
|
47
|
-
// istanbul ignore next
|
|
48
|
-
if (request.raw && reply.raw) {
|
|
49
|
-
// support fastify >= v2
|
|
50
|
-
request.raw[symbolRequestTime] = process.hrtime();
|
|
51
|
-
reply.raw[symbolServerTiming] = {};
|
|
52
|
-
}
|
|
53
|
-
else if (request.req && reply.res) {
|
|
54
|
-
// support fastify >= v2
|
|
55
|
-
request.req[symbolRequestTime] = process.hrtime();
|
|
56
|
-
reply.res[symbolServerTiming] = {};
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
request[symbolRequestTime] = process.hrtime();
|
|
60
|
-
reply[symbolServerTiming] = {};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
next();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Hook to be triggered just before response to be send
|
|
67
|
-
instance.addHook('onSend', (request, reply, payload, next) => {
|
|
68
|
-
const headers = [];
|
|
69
|
-
|
|
70
|
-
// check if Server-Timing need to be added
|
|
71
|
-
const serverTiming = (reply.raw ? reply.raw[symbolServerTiming] : reply.res[symbolServerTiming]);
|
|
72
|
-
if (serverTiming) {
|
|
73
|
-
for (const name of Object.keys(serverTiming)) {
|
|
74
|
-
headers.push(serverTiming[name]);
|
|
75
|
-
}
|
|
76
|
-
if (headers.length) {
|
|
77
|
-
reply.header('Server-Timing', headers.join(','));
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Calculate the duration, in nanoseconds …
|
|
82
|
-
const hrDuration = (request.raw ? request.raw[symbolRequestTime] : request.req[symbolRequestTime]);
|
|
83
|
-
if (hrDuration) {
|
|
84
|
-
// … convert it to milliseconds …
|
|
85
|
-
const duration = (hrDuration[0] * 1e3 + hrDuration[1] / 1e6).toFixed(opts.digits);
|
|
86
|
-
// … add the header to the response
|
|
87
|
-
reply.header(opts.header, duration);
|
|
88
|
-
|
|
89
|
-
opts.logger.info2(`${request.method} ${request.url} - ${duration}`);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
next();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Can be used to add custom timing information
|
|
96
|
-
instance.decorateReply('setServerTiming', function (name, duration, description) {
|
|
97
|
-
// Reference to the res object storing values …
|
|
98
|
-
const serverTiming = this.res[symbolServerTiming];
|
|
99
|
-
// … return if value already exists (all subsequent occurrences MUST be ignored without signaling an error) …
|
|
100
|
-
if (serverTiming.hasOwnProperty(name)) {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
// … add the value the the list to send later
|
|
104
|
-
serverTiming[name] = genTick(name, duration, description);
|
|
105
|
-
// … return true, the value was added to the list
|
|
106
|
-
return true;
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
done();
|
|
110
|
-
});
|
|
111
|
-
// Not before 0.31 (onSend hook added to this version)
|
|
1
|
+
// https://github.com/lolo32/fastify-response-time
|
|
2
|
+
import fastifyPlugin from 'fastify-plugin';
|
|
3
|
+
|
|
4
|
+
const symbolRequestTime = Symbol('RequestTimer');
|
|
5
|
+
const symbolServerTiming = Symbol('ServerTiming');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param {string} name
|
|
10
|
+
* @param {number|string} duration
|
|
11
|
+
* @param {string} description
|
|
12
|
+
* @return {string}
|
|
13
|
+
*/
|
|
14
|
+
const genTick = (name, duration, description) => {
|
|
15
|
+
let val = name;
|
|
16
|
+
// Parse duration. If could not be converted to float, does not add it
|
|
17
|
+
duration = parseFloat(duration);
|
|
18
|
+
if (!isNaN(duration)) {
|
|
19
|
+
val += `;dur=${duration}`;
|
|
20
|
+
}
|
|
21
|
+
// Parse the description. If empty, doest not add it. If string with space, double quote value
|
|
22
|
+
if ('string' === typeof description) {
|
|
23
|
+
val += `;desc=${description.includes(' ') ? `'${description}'` : description}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return val;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Decorators
|
|
31
|
+
*
|
|
32
|
+
* @param {fastify} instance
|
|
33
|
+
* @param {function} instance.decorateReply
|
|
34
|
+
* @param {Object} opts
|
|
35
|
+
* @param {function} next
|
|
36
|
+
*/
|
|
37
|
+
export default fastifyPlugin((instance, opts, done) => {
|
|
38
|
+
// Check the options, and corrects with the default values if inadequate
|
|
39
|
+
if (isNaN(opts.digits) || 0 > opts.digits) {
|
|
40
|
+
opts.digits = 2;
|
|
41
|
+
}
|
|
42
|
+
opts.header = opts.header || 'X-Response-Time';
|
|
43
|
+
|
|
44
|
+
// Hook to be triggered on request (start time)
|
|
45
|
+
instance.addHook('onRequest', (request, reply, next) => {
|
|
46
|
+
// Store the start timer in nanoseconds resolution
|
|
47
|
+
// istanbul ignore next
|
|
48
|
+
if (request.raw && reply.raw) {
|
|
49
|
+
// support fastify >= v2
|
|
50
|
+
request.raw[symbolRequestTime] = process.hrtime();
|
|
51
|
+
reply.raw[symbolServerTiming] = {};
|
|
52
|
+
}
|
|
53
|
+
else if (request.req && reply.res) {
|
|
54
|
+
// support fastify >= v2
|
|
55
|
+
request.req[symbolRequestTime] = process.hrtime();
|
|
56
|
+
reply.res[symbolServerTiming] = {};
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
request[symbolRequestTime] = process.hrtime();
|
|
60
|
+
reply[symbolServerTiming] = {};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
next();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Hook to be triggered just before response to be send
|
|
67
|
+
instance.addHook('onSend', (request, reply, payload, next) => {
|
|
68
|
+
const headers = [];
|
|
69
|
+
|
|
70
|
+
// check if Server-Timing need to be added
|
|
71
|
+
const serverTiming = (reply.raw ? reply.raw[symbolServerTiming] : reply.res[symbolServerTiming]);
|
|
72
|
+
if (serverTiming) {
|
|
73
|
+
for (const name of Object.keys(serverTiming)) {
|
|
74
|
+
headers.push(serverTiming[name]);
|
|
75
|
+
}
|
|
76
|
+
if (headers.length) {
|
|
77
|
+
reply.header('Server-Timing', headers.join(','));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Calculate the duration, in nanoseconds …
|
|
82
|
+
const hrDuration = (request.raw ? request.raw[symbolRequestTime] : request.req[symbolRequestTime]);
|
|
83
|
+
if (hrDuration) {
|
|
84
|
+
// … convert it to milliseconds …
|
|
85
|
+
const duration = (hrDuration[0] * 1e3 + hrDuration[1] / 1e6).toFixed(opts.digits);
|
|
86
|
+
// … add the header to the response
|
|
87
|
+
reply.header(opts.header, duration);
|
|
88
|
+
|
|
89
|
+
opts.logger.info2(`${request.method} ${request.url} - ${duration}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
next();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Can be used to add custom timing information
|
|
96
|
+
instance.decorateReply('setServerTiming', function (name, duration, description) {
|
|
97
|
+
// Reference to the res object storing values …
|
|
98
|
+
const serverTiming = this.res[symbolServerTiming];
|
|
99
|
+
// … return if value already exists (all subsequent occurrences MUST be ignored without signaling an error) …
|
|
100
|
+
if (serverTiming.hasOwnProperty(name)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
// … add the value the the list to send later
|
|
104
|
+
serverTiming[name] = genTick(name, duration, description);
|
|
105
|
+
// … return true, the value was added to the list
|
|
106
|
+
return true;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
done();
|
|
110
|
+
});
|
|
111
|
+
// Not before 0.31 (onSend hook added to this version)
|
|
112
112
|
// }, '>= 0.31');
|