Skip to content

Instantly share code, notes, and snippets.

@aiportal
Created January 29, 2020 09:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aiportal/6b7149833d3676e1fc38ce2ad733f640 to your computer and use it in GitHub Desktop.
Save aiportal/6b7149833d3676e1fc38ce2ad733f640 to your computer and use it in GitHub Desktop.
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