@universis/janitor
Version:
Universis api plugin for handling user authorization and rate limiting
125 lines (115 loc) • 4.83 kB
JavaScript
import { TraceUtils } from '@themost/common';
import { RedisStore } from 'rate-limit-redis';
import { Redis } from 'ioredis';
import '@themost/promise-sequence';
let superLoadIncrementScript;
let superLoadGetScript;
function noLoadGetScript() {
//
}
function noLoadIncrementScript() {
//
}
if (RedisStore.prototype.loadIncrementScript.name === 'loadIncrementScript') {
// get super method for future use
superLoadIncrementScript = RedisStore.prototype.loadIncrementScript;
RedisStore.prototype.loadIncrementScript = noLoadIncrementScript;
}
if (RedisStore.prototype.loadGetScript.name === 'loadGetScript') {
// get super method
superLoadGetScript = RedisStore.prototype.loadGetScript;
RedisStore.prototype.loadGetScript = noLoadGetScript;
}
class RedisClientStore extends RedisStore {
/**
* @type {import('redis').RedisClientType}
*/
client;
/**
*
* @param {import('@themost/common').ApplicationService} service
* @param {{windowMs: number}} options
*/
constructor(service, options) {
super({
/**
* @param {...string} args
* @returns {Promise<*>}
*/
sendCommand: function () {
const args = Array.from(arguments);
const [command] = args.splice(0, 1);
const self = this;
if (command === 'SCRIPT') {
const connectOptions = service.getApplication().getConfiguration().getSourceAt('settings/redis/options') || {
host: '127.0.0.1',
port: 6379
};
const client = new Redis(connectOptions);
return client.call(command, args).catch((error) => {
if (error instanceof TypeError && error.message === 'Invalid argument type') {
TraceUtils.warn('RedisClientStore: Invalid argument type: ' + JSON.stringify(args));
}
return Promise.reject(error);
}).finally(() => {
if (client.isOpen) {
client.disconnect().catch((errDisconnect) => {
TraceUtils.error(errDisconnect);
});
}
});
}
if (self.client == null) {
const connectOptions = service.getApplication().getConfiguration().getSourceAt('settings/redis/options') || {
host: '127.0.0.1',
port: 6379
};
self.client = new Redis(connectOptions);
}
if (self.client.isOpen) {
return self.client.call(command, args).catch((error) => {
if (error instanceof TypeError && error.message === 'Invalid argument type') {
TraceUtils.warn('RedisClientStore: Invalid argument type: ' + JSON.stringify(args));
}
return Promise.reject(error);
});
}
// send load script commands once
return (() => {
if (self.incrementScriptSha == null) {
return this.postInit();
}
return Promise.resolve();
})().then(() => {
// send command
args[0] = self.incrementScriptSha;
return self.client.call(command, args).catch((error) => {
if (error instanceof TypeError && error.message === 'Invalid argument type') {
TraceUtils.warn('RedisClientStore: Invalid argument type: ' + JSON.stringify(args));
}
return Promise.reject(error);
});
});
}
});
this.init(options);
TraceUtils.debug('RedisClientStore: Starting up and loading increment and get scripts.');
void this.postInit().then(() => {
TraceUtils.debug('RedisClientStore: Successfully loaded increment and get scripts.');
}).catch((err) => {
TraceUtils.error('RedisClientStore: Failed to load increment and get scripts.');
TraceUtils.error(err);
});
}
async postInit() {
const [incrementScriptSha, getScriptSha] = await Promise.sequence([
() => superLoadIncrementScript.call(this),
() => superLoadGetScript.call(this)
]);
this.incrementScriptSha = incrementScriptSha;
this.getScriptSha = getScriptSha;
}
}
export {
RedisClientStore
}