cache.ts 2.0 KB

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