mirror of
https://github.com/DIYgod/RSSHub.git
synced 2025-12-11 15:47:48 +08:00
node_redis: Deprecated: The SETEX command contains a argument of type Promise. This is converted to "[object Promise]" by using .toString() now and will return an error from v.3.0 on. Please handle this in your code to make sure everything works as you intended it to.
160 lines
4.5 KiB
JavaScript
160 lines
4.5 KiB
JavaScript
// based on https://github.com/coderhaoxin/koa-redis-cache
|
|
|
|
const wrapper = require('co-redis');
|
|
const Redis = require('redis');
|
|
const common = require('./cache-common');
|
|
const md5 = require('../utils/md5');
|
|
|
|
module.exports = function(options = {}) {
|
|
let redisAvailable = false;
|
|
|
|
const {
|
|
prefix = 'koa-redis-cache:',
|
|
expire = 30 * 60, // 30 min
|
|
routes = ['(.*)'],
|
|
exclude = ['/'],
|
|
passParam = '',
|
|
maxLength = Infinity,
|
|
ignoreQuery = false,
|
|
onerror = function() {},
|
|
onconnect = function() {},
|
|
} = options;
|
|
|
|
const { host: redisHost = 'localhost', port: redisPort = 6379, url: redisUrl = `redis://${redisHost}:${redisPort}/`, options: redisOptions = {} } = options.redis || {};
|
|
|
|
/**
|
|
* redisClient
|
|
*/
|
|
if (!redisOptions.password) {
|
|
delete redisOptions.password;
|
|
}
|
|
const redisClient = wrapper(Redis.createClient(redisUrl, redisOptions));
|
|
redisClient.on('error', (error) => {
|
|
redisAvailable = false;
|
|
onerror(error);
|
|
});
|
|
redisClient.on('end', () => {
|
|
redisAvailable = false;
|
|
});
|
|
redisClient.on('connect', () => {
|
|
redisAvailable = true;
|
|
onconnect();
|
|
});
|
|
|
|
options.app.context.cache = {
|
|
get: async (key) => {
|
|
if (key) {
|
|
const value = await redisClient.get(key);
|
|
if (value) {
|
|
let ttl = await redisClient.ttl(key);
|
|
ttl = ttl > expire ? ttl : expire * 2;
|
|
await redisClient.setex(key, ttl, value);
|
|
}
|
|
return value;
|
|
}
|
|
},
|
|
set: async (key, value, maxAge) => {
|
|
if (!value || value === 'undefined') {
|
|
value = '';
|
|
}
|
|
if (typeof value === 'object') {
|
|
value = JSON.stringify(value);
|
|
}
|
|
if (key) {
|
|
await redisClient.setex(key, maxAge, value);
|
|
}
|
|
},
|
|
};
|
|
|
|
return async function cache(ctx, next) {
|
|
const { url, path } = ctx.request;
|
|
const resolvedPrefix = typeof prefix === 'function' ? prefix.call(ctx, ctx) : prefix;
|
|
const key = resolvedPrefix + md5(ignoreQuery ? path : url);
|
|
const tkey = key + ':type';
|
|
|
|
const validityCheck = common.validityCheck(routes, exclude, path);
|
|
const match = validityCheck.match;
|
|
let routeExpire = validityCheck.routeExpire;
|
|
|
|
if (!redisAvailable || !match || (passParam && ctx.request.query[passParam])) {
|
|
return await next();
|
|
}
|
|
|
|
let ok = false;
|
|
try {
|
|
ok = await getCache(ctx, key, tkey);
|
|
} catch (e) {
|
|
ok = false;
|
|
}
|
|
if (ok) {
|
|
return;
|
|
}
|
|
|
|
await next();
|
|
|
|
try {
|
|
const trueExpire = routeExpire || expire;
|
|
await setCache(ctx, key, tkey, trueExpire);
|
|
} catch (e) {} // eslint-disable-line no-empty
|
|
routeExpire = false;
|
|
};
|
|
|
|
/**
|
|
* getCache
|
|
*/
|
|
async function getCache(ctx, key, tkey) {
|
|
const value = await redisClient.get(key);
|
|
let type;
|
|
let ok = false;
|
|
|
|
if (value) {
|
|
ctx.response.status = 200;
|
|
type = (await redisClient.get(tkey)) || 'text/html';
|
|
// can happen if user specified return_buffers: true in redis options
|
|
if (Buffer.isBuffer(type)) {
|
|
type = type.toString();
|
|
}
|
|
ctx.response.set({
|
|
'X-Koa-Redis-Cache': 'true',
|
|
'Content-Type': type,
|
|
});
|
|
try {
|
|
ctx.state.data = JSON.parse(value);
|
|
} catch (e) {
|
|
ctx.state.data = {};
|
|
}
|
|
ok = true;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
/**
|
|
* setCache
|
|
*/
|
|
async function setCache(ctx, key, tkey, expire) {
|
|
ctx.state.data.lastBuildDate = new Date().toUTCString();
|
|
const body = JSON.stringify(ctx.state.data);
|
|
|
|
if (ctx.request.method !== 'GET' || !body) {
|
|
return;
|
|
}
|
|
if (Buffer.byteLength(body) > maxLength) {
|
|
return;
|
|
}
|
|
await redisClient.setex(key, expire, body);
|
|
|
|
await cacheType(ctx, tkey, expire);
|
|
}
|
|
|
|
/**
|
|
* cacheType
|
|
*/
|
|
async function cacheType(ctx, tkey, expire) {
|
|
const type = ctx.response.headers['content-type'];
|
|
if (type) {
|
|
await redisClient.setex(tkey, expire, type);
|
|
}
|
|
}
|
|
};
|