| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 | const CoreClass = require("../../core.js");const redis = require("redis");const config = require("config");const mongoose = require("mongoose");// Lightweight / convenience wrapper around redis module for our needsconst pubs = {},    subs = {};class CacheModule extends CoreClass {    constructor() {        super("cache");    }    initialize() {        return new Promise((resolve, reject) => {            this.schemas = {                session: require("./schemas/session"),                station: require("./schemas/station"),                playlist: require("./schemas/playlist"),                officialPlaylist: require("./schemas/officialPlaylist"),                song: require("./schemas/song"),                punishment: require("./schemas/punishment"),            };            this.url = config.get("redis").url;            this.password = config.get("redis").password;            this.log("INFO", "Connecting...");            this.client = redis.createClient({                url: this.url,                password: this.password,                retry_strategy: (options) => {                    if (this.getStatus() === "LOCKDOWN") return;                    if (this.getStatus() !== "RECONNECTING")                        this.setStatus("RECONNECTING");                    this.log("INFO", `Attempting to reconnect.`);                    if (options.attempt >= 10) {                        this.log("ERROR", `Stopped trying to reconnect.`);                        this.setStatus("FAILED");                        // this.failed = true;                        // this._lockdown();                        return undefined;                    }                    return 3000;                },            });            this.client.on("error", (err) => {                if (this.getStatus() === "INITIALIZING") reject(err);                if (this.getStatus() === "LOCKDOWN") return;                this.log("ERROR", `Error ${err.message}.`);            });            this.client.on("connect", () => {                this.log("INFO", "Connected succesfully.");                if (this.getStatus() === "INITIALIZING") resolve();                else if (                    this.getStatus() === "FAILED" ||                    this.getStatus() === "RECONNECTING"                )                    this.setStatus("READY");            });        });    }    /**     * Gracefully closes all the Redis client connections     */    QUIT(payload) {        return new Promise((resolve, reject) => {            if (this.client.connected) {                this.client.quit();                Object.keys(pubs).forEach((channel) => pubs[channel].quit());                Object.keys(subs).forEach((channel) =>                    subs[channel].client.quit()                );            }            resolve();        });    }    /**     * Sets a single value in a table     *     * @param {String} table - name of the table we want to set a key of (table === redis hash)     * @param {String} key -  name of the key to set     * @param {*} value - the value we want to set     * @param {Function} cb - gets called when the value has been set in Redis     * @param {Boolean} [stringifyJson=true] - stringify 'value' if it's an Object or Array     */    HSET(payload) {        //table, key, value, cb, stringifyJson = true        return new Promise((resolve, reject) => {            let key = payload.key;            let value = payload.value;            if (mongoose.Types.ObjectId.isValid(key)) key = key.toString();            // automatically stringify objects and arrays into JSON            if (["object", "array"].includes(typeof value))                value = JSON.stringify(value);            this.client.hset(payload.table, key, value, (err) => {                if (err) return reject(new Error(err));                else resolve(JSON.parse(value));            });        });    }    /**     * Gets a single value from a table     *     * @param {String} table - name of the table to get the value from (table === redis hash)     * @param {String} key - name of the key to fetch     * @param {Function} cb - gets called when the value is returned from Redis     * @param {Boolean} [parseJson=true] - attempt to parse returned data as JSON     */    HGET(payload) {        //table, key, cb, parseJson = true        return new Promise((resolve, reject) => {            // if (!key || !table)            // return typeof cb === "function" ? cb(null, null) : null;            let key = payload.key;            if (mongoose.Types.ObjectId.isValid(key)) key = key.toString();            this.client.hget(payload.table, key, (err, value) => {                if (err) return reject(new Error(err));                try {                    value = JSON.parse(value);                } catch (e) {}                resolve(value);            });        });    }    /**     * Deletes a single value from a table     *     * @param {String} table - name of the table to delete the value from (table === redis hash)     * @param {String} key - name of the key to delete     * @param {Function} cb - gets called when the value has been deleted from Redis or when it returned an error     */    HDEL(payload) {        //table, key, cb        return new Promise((resolve, reject) => {            // if (!payload.key || !table || typeof key !== "string")            // return cb(null, null);            let key = payload.key;            if (mongoose.Types.ObjectId.isValid(key)) key = key.toString();            this.client.hdel(payload.table, key, (err) => {                if (err) return reject(new Error(err));                else return resolve();            });        });    }    /**     * Returns all the keys for a table     *     * @param {String} table - name of the table to get the values from (table === redis hash)     * @param {Function} cb - gets called when the values are returned from Redis     * @param {Boolean} [parseJson=true] - attempts to parse all values as JSON by default     */    HGETALL(payload) {        //table, cb, parseJson = true        return new Promise((resolve, reject) => {            this.client.hgetall(payload.table, (err, obj) => {                if (err) return reject(new Error(err));                if (obj)                    Object.keys(obj).forEach((key) => {                        try {                            obj[key] = JSON.parse(obj[key]);                        } catch (e) {}                    });                else if (!obj) obj = [];                resolve(obj);            });        });    }    /**     * Publish a message to a channel, caches the redis client connection     *     * @param {String} channel - the name of the channel we want to publish a message to     * @param {*} value - the value we want to send     * @param {Boolean} [stringifyJson=true] - stringify 'value' if it's an Object or Array     */    PUB(payload) {        //channel, value, stringifyJson = true        return new Promise((resolve, reject) => {            /*if (pubs[channel] === undefined) {            pubs[channel] = redis.createClient({ url: this.url });            pubs[channel].on('error', (err) => console.error);            }*/            let value = payload.value;            if (["object", "array"].includes(typeof value))                value = JSON.stringify(value);            //pubs[channel].publish(channel, value);            this.client.publish(payload.channel, value, (err, res) => {                if (err) reject(err);                else resolve();            });        });    }    /**     * Subscribe to a channel, caches the redis client connection     *     * @param {String} channel - name of the channel to subscribe to     * @param {Function} cb - gets called when a message is received     * @param {Boolean} [parseJson=true] - parse the message as JSON     */    SUB(payload) {        //channel, cb, parseJson = true        return new Promise((resolve, reject) => {            if (subs[payload.channel] === undefined) {                subs[payload.channel] = {                    client: redis.createClient({                        url: this.url,                        password: this.password,                    }),                    cbs: [],                };                subs[payload.channel].client.on(                    "message",                    (channel, message) => {                        try {                            message = JSON.parse(message);                        } catch (e) {}                        subs[channel].cbs.forEach((cb) => cb(message));                    }                );                subs[payload.channel].client.subscribe(payload.channel);            }            subs[payload.channel].cbs.push(payload.cb);            resolve();        });    }    GET_SCHEMA(payload) {        return new Promise((resolve, reject) => {            resolve(this.schemas[payload.schemaName]);        });    }}module.exports = new CacheModule();
 |