cache.ts 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. import * as express from 'express'
  2. import * as AsyncLock from 'async-lock'
  3. import { parseDurationToMs } from '../helpers/core-utils'
  4. import { Redis } from '../lib/redis'
  5. import { logger } from '../helpers/logger'
  6. const lock = new AsyncLock({ timeout: 5000 })
  7. function cacheRoute (lifetimeArg: string | number) {
  8. const lifetime = parseDurationToMs(lifetimeArg)
  9. return async function (req: express.Request, res: express.Response, next: express.NextFunction) {
  10. const redisKey = Redis.Instance.generateCachedRouteKey(req)
  11. try {
  12. await lock.acquire(redisKey, async (done) => {
  13. const cached = await Redis.Instance.getCachedRoute(req)
  14. // Not cached
  15. if (!cached) {
  16. logger.debug('No cached results for route %s.', req.originalUrl)
  17. const sendSave = res.send.bind(res)
  18. const redirectSave = res.redirect.bind(res)
  19. res.send = (body) => {
  20. if (res.statusCode >= 200 && res.statusCode < 400) {
  21. const contentType = res.get('content-type')
  22. Redis.Instance.setCachedRoute(req, body, lifetime, contentType, res.statusCode)
  23. .then(() => done())
  24. .catch(err => {
  25. logger.error('Cannot cache route.', { err })
  26. return done(err)
  27. })
  28. } else {
  29. done()
  30. }
  31. return sendSave(body)
  32. }
  33. res.redirect = url => {
  34. done()
  35. return redirectSave(url)
  36. }
  37. return next()
  38. }
  39. if (cached.contentType) res.set('content-type', cached.contentType)
  40. if (cached.statusCode) {
  41. const statusCode = parseInt(cached.statusCode, 10)
  42. if (!isNaN(statusCode)) res.status(statusCode)
  43. }
  44. logger.debug('Use cached result for %s.', req.originalUrl)
  45. res.send(cached.body).end()
  46. return done()
  47. })
  48. } catch (err) {
  49. logger.error('Cannot serve cached route.', { err })
  50. return next()
  51. }
  52. }
  53. }
  54. // ---------------------------------------------------------------------------
  55. export {
  56. cacheRoute
  57. }