Created
January 29, 2020 09:16
-
-
Save aiportal/6b7149833d3676e1fc38ce2ad733f640 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import _ from 'lodash'; | |
import md5 from 'md5'; | |
import { NOT_MODIFIED } from 'http-status'; | |
import cacheManager from 'cache-manager'; | |
import mongoStore from 'cache-manager-mongodb'; | |
import redis from '../services/redis-service'; | |
import config from '../config'; | |
export default class EtagService { | |
/** | |
* update resource version | |
* @param name resource name | |
*/ | |
public static async update(name: string): Promise<void> { | |
const key = `resource:${name}`; | |
const etag = md5(`${name}-${Date.now()}-${Math.random().toString()}`); | |
await redis.setAsync(key, etag); | |
} | |
/** | |
* if fresh, request new data. | |
*/ | |
public static async fresh(ctx: any, name: string, ...relations: string[]): Promise<boolean> { | |
// check etag version | |
const values = await Promise.all( | |
[name, ...relations].map(async (x) => { | |
return await redis.getAsync(`resource:${x}`); | |
}), | |
); | |
const version = _.isEmpty(values) ? 'default' : md5(values.join(',')); | |
const etag = `${name}-${version}`; | |
ctx.response.set('Cache-Control', 'private'); | |
ctx.response.set('ETag', etag); | |
if (ctx.request.headers['if-none-match'] === etag) { | |
ctx.status = NOT_MODIFIED; | |
return false; | |
} | |
// if cached, return content from cache. | |
const cache = await EtagService.cacheGet(ctx.url, etag); | |
if (cache) { | |
ctx.body = cache; | |
return false; | |
} | |
return true; | |
} | |
/** | |
* set cache content by url hash. | |
* @param url | |
* @param value | |
*/ | |
public static async cacheSet(ctx: any): Promise<void> { | |
// store new cache | |
const version = ctx.response.get('ETag'); | |
if (version && ctx.body) { | |
const key = `${version}-${md5(ctx.url)}`; | |
const value = { | |
url: ctx.url, | |
data: ctx.body, | |
}; | |
await this.mongoCache.set(key, value); | |
} | |
// remove old cache | |
const oldVersion = ctx.request.headers['if-none-match']; | |
if (oldVersion) { | |
const oldKey = `${oldVersion}-${md5(ctx.url)}`; | |
await this.mongoCache.del(oldKey); | |
} | |
} | |
/** | |
* get cache content by url hash. | |
* @param url | |
*/ | |
private static async cacheGet(url: string, version: string): Promise<any> { | |
const key = `${version}-${md5(url)}`; | |
const value = await this.mongoCache.get(key); | |
return _.has(value, 'data') ? value.data : value; | |
} | |
private static _mongoCache: any; | |
private static get mongoCache() { | |
if (!this._mongoCache) { | |
const options = { | |
collection: 'etag', | |
compression: false, | |
poolSize: 5, | |
autoReconnect: true, | |
}; | |
this._mongoCache = cacheManager.caching({ | |
store: mongoStore, | |
uri: config.db.url, | |
options, | |
}); | |
} | |
return this._mongoCache; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment