Better cache control when not using CDN

This commit is contained in:
Bobby Wibowo 2020-02-11 17:18:04 +07:00
parent c0fc463595
commit edce59243b
No known key found for this signature in database
GPG Key ID: 51C3A1E1E22D26CF
3 changed files with 66 additions and 48 deletions

View File

@ -101,7 +101,8 @@ module.exports = {
/*
Trust proxy.
Only enable if you are running this behind a proxy like Cloudflare, Incapsula, etc.
Enable this if you are using proxy such as Cloudflare or Incapsula,
and/or also when you are using reverse proxy such as nginx or Apache.
*/
trustProxy: true,
@ -141,13 +142,13 @@ module.exports = {
}
},
{
// 4 requests in 30 seconds
// 6 requests in 30 seconds
routes: [
'/api/album/zip'
],
config: {
windowMs: 30 * 1000,
max: 4
max: 6
}
},
{
@ -449,6 +450,8 @@ module.exports = {
ADVANCED: Use safe.fiery.me-exclusive cache control.
This will only work properly with certain settings in nginx reverse proxy and Cloudflare.
Do NOT enable unless you know what you are doing.
true: With CDN (Cloudflare)
2: When NOT using Cloudflare
*/
cacheControl: false,

View File

@ -352,14 +352,6 @@ self.get = async (req, res, next) => {
self.generateZip = async (req, res, next) => {
const versionString = parseInt(req.query.v)
const download = (filePath, fileName) => {
const headers = {}
if (config.cacheControl && versionString > 0) {
headers['Access-Control-Allow-Origin'] = '*'
headers['Cache-Control'] = 'public, max-age=2592000, must-revalidate, proxy-revalidate, immutable, stale-while-revalidate=86400, stale-if-error=604800'
}
return res.download(filePath, fileName, { headers })
}
const identifier = req.params.identifier
if (identifier === undefined)
@ -398,7 +390,7 @@ self.generateZip = async (req, res, next) => {
try {
const filePath = path.join(paths.zips, `${identifier}.zip`)
await paths.access(filePath)
return download(filePath, `${album.name}.zip`)
return res.download(filePath, `${album.name}.zip`)
} catch (error) {
// Re-throw error
if (error.code !== 'ENOENT')
@ -409,7 +401,7 @@ self.generateZip = async (req, res, next) => {
logger.log(`Waiting previous zip task for album: ${identifier}.`)
return self.zipEmitters.get(identifier).once('done', (filePath, fileName, json) => {
if (filePath && fileName)
download(filePath, fileName)
res.download(filePath, fileName)
else if (json)
res.json(json)
})
@ -481,7 +473,7 @@ self.generateZip = async (req, res, next) => {
const fileName = `${album.name}.zip`
self.zipEmitters.get(identifier).emit('done', filePath, fileName)
return download(filePath, fileName)
return res.download(filePath, fileName)
} catch (error) {
logger.error(error)
return res.status(500).json({ success: false, description: 'An unexpected error occurred. Try again?' })

View File

@ -50,52 +50,76 @@ if (Array.isArray(config.rateLimits) && config.rateLimits.length)
safe.use(bodyParser.urlencoded({ extended: true }))
safe.use(bodyParser.json())
let setHeaders
let cdnPages
let setHeaders = res => {
res.set('Access-Control-Allow-Origin', '*')
}
// Cache control (safe.fiery.me)
if (config.cacheControl) {
const cacheControls = {
// max-age: 30 days
default: 'public, max-age=2592000, must-revalidate, proxy-revalidate, immutable, stale-while-revalidate=86400, stale-if-error=604800',
// s-max-age: 30 days (only cache in proxy server)
// Obviously we have to purge proxy cache on every update
proxyOnly: 's-max-age=2592000, proxy-revalidate, stale-while-revalidate=86400, stale-if-error=604800',
// max-age: 6 months
static: 'public, max-age=15778800, must-revalidate, proxy-revalidate, immutable, stale-while-revalidate=86400, stale-if-error=604800',
// s-max-age: 6 months (only cache in CDN)
cdn: 's-max-age=15778800, proxy-revalidate, stale-while-revalidate=86400, stale-if-error=604800',
// validate cache's validity before using them (soft cache)
validate: 'no-cache',
// do not use cache at all
disable: 'no-store'
}
// By default soft cache everything
safe.use('/', (req, res, next) => {
res.set('Cache-Control', cacheControls.proxyOnly)
res.set('Cache-Control', cacheControls.validate)
next()
})
// Do NOT cache these dynamic routes
safe.use(['/a', '/api', '/nojs'], (req, res, next) => {
// If using CDN, cache public pages in CDN
if (config.cacheControl !== 2) {
cdnPages = config.pages.concat(['api/check'])
for (const page of cdnPages)
safe.use(`/${page === 'home' ? '' : page}`, (req, res, next) => {
res.set('Cache-Control', cacheControls.cdn)
next()
})
}
// If serving uploads with node
if (config.serveFilesWithNode)
safe.use('/', express.static(paths.uploads, {
setHeaders: res => {
res.set('Access-Control-Allow-Origin', '*')
// If using CDN, cache uploads in CDN as well
// Use with cloudflare.purgeCache enabled in config file
if (config.cacheControl !== 2)
res.set('Cache-Control', cacheControls.cdn)
}
}))
// Function for static assets.
// This requires the assets to use version in their query string,
// as they will be cached by clients for a very long time.
setHeaders = res => {
res.set('Access-Control-Allow-Origin', '*')
res.set('Cache-Control', cacheControls.static)
}
// Consider album ZIPs static as well, since they use version in their query string
safe.use(['/api/album/zip'], (req, res, next) => {
res.set('Access-Control-Allow-Origin', '*')
const versionString = parseInt(req.query.v)
if (versionString > 0)
res.set('Cache-Control', cacheControls.static)
else
res.set('Cache-Control', cacheControls.disable)
next()
})
// Cache these in proxy server though
safe.use(['/api/check'], (req, res, next) => {
res.set('Cache-Control', cacheControls.proxyOnly)
next()
})
// Cache album ZIPs
safe.use(['/api/album/zip'], (req, res, next) => {
setHeaders(res)
next()
})
// For static assets (and uploads if serving with node)
setHeaders = res => {
res.set('Access-Control-Allow-Origin', '*')
res.set('Cache-Control', cacheControls.default)
}
} else if (config.serveFilesWithNode) {
// If serving uploads with node
safe.use('/', express.static(paths.uploads))
}
if (config.serveFilesWithNode)
safe.use('/', express.static(paths.uploads, { setHeaders }))
// Static assets
safe.use('/', express.static(paths.public, { setHeaders }))
safe.use('/', express.static(paths.dist, { setHeaders }))
@ -142,13 +166,13 @@ safe.use('/api', api)
// Error pages
safe.use((req, res, next) => {
if (config.cacheControl) res.removeHeader('Cache-Control')
res.setHeader('Cache-Control', 'no-store')
res.status(404).sendFile(path.join(paths.errorRoot, config.errorPages[404]))
})
safe.use((error, req, res, next) => {
logger.error(error)
if (config.cacheControl) res.removeHeader('Cache-Control')
res.setHeader('Cache-Control', 'no-store')
res.status(500).sendFile(path.join(paths.errorRoot, config.errorPages[500]))
})
@ -196,12 +220,11 @@ safe.use('/api', api)
logger.log(`lolisafe started on port ${config.port}`)
// Cache control (safe.fiery.me)
// Also only if explicitly using Cloudflare
if (config.cacheControl)
// Purge Cloudflare cache
if (config.cacheControl === 1)
if (config.cloudflare.purgeCache) {
logger.log('Cache control enabled, purging Cloudflare\'s cache...')
const routes = config.pages.concat(['api/check'])
const results = await utils.purgeCloudflareCache(routes)
const results = await utils.purgeCloudflareCache(cdnPages)
let errored = false
let succeeded = 0
for (const result of results) {