-
-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathindex.js
104 lines (88 loc) · 2.85 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
'use strict'
/* eslint no-prototype-builtins: 0 */
const fp = require('fastify-plugin')
const { sync } = require('uid-safe')
const abstractCache = require('abstract-cache')
const defaultOptions = {
expiresIn: undefined,
serverExpiresIn: undefined,
privacy: undefined,
cache: undefined,
cacheSegment: 'fastify-caching'
}
function cachingExpires (date) {
if (!date) return this
this.header('Expires', (Date.prototype.isPrototypeOf(date)) ? date.toUTCString() : date)
return this
}
function etag (value, lifetime) {
this.header('ETag', value || sync(18))
this._etagLife = Number.isInteger(lifetime) ? lifetime : 3600000
return this
}
function cachingLoadByEtag (req, res, next) {
if (!req.headers['if-none-match']) return next()
const etag = req.headers['if-none-match']
this.cache.get({ id: etag, segment: this.cacheSegment }, (err, cached) => {
if (err) return next(err)
if (cached?.item) {
return res.status(304).send()
}
next()
})
}
function cachingStoreByEtag (_req, res, payload, next) {
const etag = res.getHeader('etag')
if (!etag || !res._etagLife) return next()
this.cache.set(
{ id: etag, segment: this.cacheSegment },
true,
res._etagLife,
(err) => next(err, payload)
)
}
function fastifyCaching (instance, options, next) {
let _options
if (Function.prototype.isPrototypeOf(options)) {
_options = Object.assign({}, defaultOptions)
} else {
_options = Object.assign({}, defaultOptions, options)
}
if (!_options.cache) _options.cache = abstractCache()
if (_options.privacy) {
// https://tools.ietf.org/html/rfc2616#section-14.9.4
let value = _options.privacy
if (_options.privacy.toLowerCase() !== 'no-cache' && _options.expiresIn) {
value = `${_options.privacy}, max-age=${_options.expiresIn}`
}
if (_options.privacy !== undefined && _options.privacy.toLowerCase() === 'public' && _options.serverExpiresIn) {
value += `, s-maxage=${_options.serverExpiresIn}`
}
instance.addHook('onRequest', function cachingSetCacheControlHeader (_req, res, next) {
if (!res.hasHeader('Cache-control')) {
res.header('Cache-control', value)
}
next()
})
}
instance.decorate('cache', _options.cache)
instance.decorate('cacheSegment', _options.cacheSegment)
instance.decorate('etagMaxLife', _options.etagMaxLife)
instance.decorateReply('etag', etag)
instance.decorateReply('expires', cachingExpires)
instance.addHook('onRequest', cachingLoadByEtag)
instance.addHook('onSend', cachingStoreByEtag)
instance[Symbol.for('fastify-caching.registered')] = true
next()
}
module.exports = fp(fastifyCaching, {
fastify: '5.x',
name: '@fastify/caching'
})
module.exports.default = fastifyCaching
module.exports.fastifyCaching = fastifyCaching
module.exports.privacy = {
NOCACHE: 'no-cache',
PUBLIC: 'public',
PRIVATE: 'private'
}