UNPKG

@universis/user-storage

Version:

Universis Api Server Extension for managing user remote storage

187 lines (173 loc) 6.86 kB
import {AccessDeniedError} from '@themost/common'; import * as redis from 'redis'; import {UserStorageService, UserStorageAccessConfiguration} from './service'; import {promisify} from 'es6-promisify'; const REDIS_JSON_COMMANDS = ["json.del", "json.get", "json.mget", "json.set", "json.type", "json.numincrby", "json.nummultby", "json.strappend", "json.strlen", "json.arrappend", "json.arrindex", "json.arrinsert", "json.arrlen", "json.arrpop", "json.arrtrim", "json.objkeys", "json.objlen", "json.debug", "json.forget", "json.resp"]; function createJsonClient(options) { // add json commands REDIS_JSON_COMMANDS.forEach (command => { // noinspection JSUnresolvedFunction redis.addCommand(command); }); // and finally return client return redis.createClient(options); } class RedisUserStorageService extends UserStorageService { /** * Formats a path like key string to a redis command compatible key e.g. user1/application1/lastAction to .user1.application1.lastAction * @param key * @returns {string|*} */ static escapeKey(key) { let res = key.replace(/\//ig,'.'); if (/^\./.test(res)) { return res; } return '.' + res; } /** * @param {IApplication} app */ constructor(app) { super(app); } /** * @name RedisClient#json_set * @param {*} ...args */ /** * @name RedisClient#json_get * @param {*} ...args */ /** * * @param {DataContext} context * @param {string} key * @param {*} value * @param {number=} expiration */ async setItem(context, key, value, expiration) { // validate user const hasAccess = await context.getConfiguration().getStrategy(UserStorageAccessConfiguration).verify(context, `me/${key}`, 'write'); if (hasAccess == null) { throw new AccessDeniedError('Write access to the specified user storage key is denied'); } // create redis client const client = createJsonClient(this.options); const json_set = promisify( client.json_set).bind(client); // noinspection JSUnresolvedVariable const json_type = promisify( client.json_type).bind(client); const quit = promisify( client.quit).bind(client); // validate root object //split key e.g. .key1.key2.key3 to an array of [ '', 'key1', 'key2', 'key3' ] const keys = UserStorageService.escapeKey(key).split('.'); // enumerate existence of keys from [1] to [length - 1] in order to avoid redis errors for (let i = 0; i < keys.length-1; i++) { let key1 = keys.slice(0, i + 1).join('.'); // ensure root key if (i === 0) { key1 = '.'; } // validate key e.g. .key1 and .key1.key2 const value1 = await json_type(`${context.user.name}`, key1); // if key does not exist if (value1 == null) { // set value await json_set(`${context.user.name}`, key1, JSON.stringify({ })); } } if (typeof expiration === "number" && expiration > 0) { // call json_set() with expiration await json_set(`${context.user.name}`, UserStorageService.escapeKey(key), 'EX', Math.ceil(expiration), JSON.stringify(value)); // close client await quit(); // and return return value; } // call json_set() with expiration await json_set(`${context.user.name}`, UserStorageService.escapeKey(key),JSON.stringify(value)); // close client await quit(); // and return return value; } /** * @param {DataContext} context * @param {string} key */ async getItem(context, key) { // validate user const hasAccess = await context.getConfiguration().getStrategy(UserStorageAccessConfiguration).verify(context, `me/${key}`, 'read'); if (hasAccess == null) { throw new AccessDeniedError('Read access to the specified user storage key is denied'); } // create redis client const client = createJsonClient(this.options); // noinspection JSUnresolvedVariable const json_type = promisify( client.json_type).bind(client); const json_get = promisify( client.json_get).bind(client); // noinspection JSUnresolvedVariable const quit = promisify( client.quit).bind(client); // call json_get() const type = await json_type(`${context.user.name}`, UserStorageService.escapeKey(key)); if (type == null) { return null; } // get value const res = await json_get(`${context.user.name}`, 'NOESCAPE', UserStorageService.escapeKey(key)); // quit await quit(); // if value is not null if (res != null) { // parse string value and return return JSON.parse(res); } return res; } /** * @param {DataContext} context * @param {string} key * @returns Promise<*> */ async removeItem(context, key) { // validate user access const hasAccess = await context.getConfiguration().getStrategy(UserStorageAccessConfiguration).verify(context, `me/${key}`, 'write'); if (hasAccess == null) { throw new AccessDeniedError('Write access to the specified user storage key is denied'); } // create redis client const client = createJsonClient(this.options); // noinspection JSUnresolvedVariable const json_del = promisify( client.json_del).bind(client); const quit = promisify( client.quit).bind(client); // call json_del() const res = await json_del(`${context.user.name}`, UserStorageService.escapeKey(key)); // close client await quit(); // and return return (res === 1); } /** * @param {DataContext} context * @returns Promise<*> */ async clear(context) { // validate user access const hasAccess = context.getConfiguration().getStrategy(UserStorageAccessConfiguration).verify(context, `me`, 'write'); if (hasAccess == null) { throw new AccessDeniedError('Write access to the specified user storage key is denied'); } // create redis client const client = createJsonClient(this.options); const del = promisify( client.del).bind(client); const quit = promisify( client.quit).bind(client); // call json_del() const res = await del(`${context.user.name}`); await quit(); return (res === 1); } } module.exports.RedisUserStorageService = RedisUserStorageService;