.editorconfig000066400000000000000000000006401520154223400135310ustar00rootroot00000000000000# EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # editorconfig.org root = true [*] # Change these settings to your own preference indent_style = space indent_size = 4 # We recommend you to keep these unchanged end_of_line = lf charset = utf-8 trim_trailing_whitespace = false insert_final_newline = true [*.md] trim_trailing_whitespace = false .github/000077500000000000000000000000001520154223400124145ustar00rootroot00000000000000.github/workflows/000077500000000000000000000000001520154223400144515ustar00rootroot00000000000000.github/workflows/build.yml000066400000000000000000000016761520154223400163050ustar00rootroot00000000000000# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions name: build on: schedule: - cron: '0 0 1 * *' push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: ['lts/*'] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} - run: npm i -g grunt-cli - run: npm install - run: grunt .gitignore000066400000000000000000000003521520154223400130440ustar00rootroot00000000000000/build /node_modules /*.log /*.iws .idea/workspace.xml .idea/tasks.xml .idea/profiles_settings.xml .idea/inspectionProfiles/Project_Default.xml .idea/inspectionProfiles/profiles_settings.xml node_modules/.yarn-integrity /p3xrs.json.ncurc.json000066400000000000000000000000521520154223400131340ustar00rootroot00000000000000{ "reject": [ "chalk" ] } .npmignore000066400000000000000000000002271520154223400130540ustar00rootroot00000000000000/.idea /build /test /node_modules /*.iml /*.ipr /*.iws /.travis.yml /.scrutinizer.yml /Gruntfile.js /*.lock *.log /corifeus-boot.json /secure /.github .travis.yml000066400000000000000000000014571520154223400131740ustar00rootroot00000000000000language: node_js cache: npm: false node_js: - lts/* before_script: - npm install -g grunt-cli npm env: global: secure: r4bMeHdS9vdDWuL3ax+va5YoaZKmn5MxsXNxMgnT7lrk4AFkb2wr+17+4zDalQFyt7zyqM0HJzGXRYGTiG4kow6Jk2P55wv3LeiNJxpTBOGYfKTldjLZKIxxaqJLYJ40JhlfuA+TaTj0JWVsohz5CQJxO1O7Z+/xq/0ZwHVIhDNwW5XdFDdCdBZRQMV7XKSynGAB7L0pdhASSdl4laQTxjL/6A3B0Sr9rwO5Nd8YbzZpUPQ5opTLXfln2JSMTiAEtxgLS2ARu5Aa+9qfIA5VTDXzcyYAE+uErjIXeUfNRbTCJuizNQQZOoeMR36tam0cOu+B0qt0FWqCECOlmN12tL0TpY54vqsJfS4AIUT4dm2LpjPKCKZn4jIZxVJe3uxTRKZrWiDSaKUmnuoRIoTCPh4JMYc7t6pxP/4kKcAEpw4OxYS0eERG4RXKSJx9I2szqvJ+9nS/eAO3AC/5KWn+o+x1ebnjNgmGk/7BQbgdEOm1wsUazpEAg/nkL4b5EW6IEzUJm8V31NO1OxoAs+U0NLgOu3OKys0KxjwpzTFtQ0bvQoNcy715Y6JfLwZQFOTOUM6OjMXhAE4AF6mt/SYHE7iTY1I+TZxt71RLN5+t0DQSyNHQ/VStx1nTg9HOjs35ZxxJYG2y5fbsBBmeV8wGY1iX9GAsTO2oGgfAzUIuMQs= Gruntfile.js000066400000000000000000000004471520154223400133560ustar00rootroot00000000000000const utils = require('corifeus-utils'); module.exports = (grunt) => { const _ = require('lodash'); const builder = require(`corifeus-builder`); const loader = new builder.loader(grunt); loader.js({ }); grunt.registerTask('default', builder.config.task.build.js); } LICENSE000066400000000000000000000024311520154223400120610ustar00rootroot00000000000000 @license p3x-redis-ui-server v2022.4.134 🏍️ The p3x-redis-ui-server package motor that is connected to the p3x-redis-ui-material web user interface https://corifeus.com/redis-ui-server Copyright (c) 2022 Patrik Laszlo / P3X / Corifeus and contributors. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. README.md000066400000000000000000000113151520154223400123340ustar00rootroot00000000000000# This is a development package For the full-blown package, please follow: https://github.com/patrikx3/redis-ui https://www.npmjs.com/package/p3x-redis-ui https://corifeus.com/redis-ui [//]: #@corifeus-header [![Donate for Corifeus / P3X](https://img.shields.io/badge/Donate-Corifeus-003087.svg)](https://paypal.me/patrikx3) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) [![Corifeus @ Facebook](https://img.shields.io/badge/Facebook-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [![Build Status](https://github.com/patrikx3/redis-ui-server/workflows/build/badge.svg)](https://github.com/patrikx3/redis-ui-server/actions?query=workflow%3Abuild) [![Uptime Robot ratio (30 days)](https://img.shields.io/uptimerobot/ratio/m780749701-41bcade28c1ea8154eda7cca.svg)](https://stats.uptimerobot.com/9ggnzcWrw) --- # 🏍️ The p3x-redis-ui-server package motor that is connected to the p3x-redis-ui-material web user interface v2022.10.105 **Bugs are evident™ - MATRIX️** ### NodeJS LTS is supported ### Built on NodeJs version ```txt v18.12.1 ``` # Description [//]: #@corifeus-header:end This is part of a composable `p3x-redis-ui` package. This is the server based on Socket.IO (no rest at all). The server will be using the `p3x-redis-ui-material` web client package based on built with Webpack, Socket.IO and AngularJs Material. This package is named as `p3x-redis-ui-server`. ## Configuration For now, there are 2 configuration files: ```bash p3xrs --config ./p3xrs.json ``` The 2nd configuration is the list of the connections if found in `p3xrs.json` it either in the config: ```text p3xrs.json/p3xrs.connections['home-dir'] = undefined|home|absolute|relative ``` The best is to keep it undefined and it will be in your home dir, but you can choose any place as well. You may also set connections file name which overrides default .p3xrs-conns.json ```text p3xrs --connections-file-name .p3xrs-conns.json ``` ### Verbose CLI help ```text patrikx3@workstation:~/Projects/patrikx3/redis-ui-workspace/redis-ui-server$ p3xrs.js --help Usage: p3xrs [options] Options: -V, --version output the version number -c, --config [config] Set the p3xr.json p3x-redis-ui-server configuration, see more help in https://github.com/patrikx3/redis-ui-server -r, --readonly-connections Set the connections to be readonly, no adding, saving or delete a connection -n, --connections-file-name [filename] Set the connections file name, overrides default .p3xrs-conns.json -h, --help output usage information ``` # For development standalone For file names do not use camelCase, but use kebab-case. Folder should be named as kebab-case as well. As you can see, all code filenames are using it like that, please do not change that. Please apply the `.editorconfig` settings in your IDE. Copy from `./artifacts/boot/p3xrs.json` to the root folder (`./p3xrs.json`). ```bash npm install npm run dev ``` It uses `nodemon` and when any file is changed, it will re-load it. The server app is available @ http://localhost:7843 [//]: #@corifeus-footer --- 🙏 This is an open-source project. Star this repository, if you like it, or even donate to maintain the servers and the development. Thank you so much! Possible, this server, rarely, is down, please, hang on for 15-30 minutes and the server will be back up. All my domains ([patrikx3.com](https://patrikx3.com) and [corifeus.com](https://corifeus.com)) could have minor errors, since I am developing in my free time. However, it is usually stable. **Note about versioning:** Versions are cut in Major.Minor.Patch schema. Major is always the current year. Minor is either 4 (January - June) or 10 (July - December). Patch is incremental by every build. If there is a breaking change, it should be noted in the readme. --- [**P3X-REDIS-UI-SERVER**](https://corifeus.com/redis-ui-server) Build v2022.10.105 [![Donate for Corifeus / P3X](https://img.shields.io/badge/Donate-Corifeus-003087.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) [![Like Corifeus @ Facebook](https://img.shields.io/badge/LIKE-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) ## P3X Sponsor [IntelliJ - The most intelligent Java IDE](https://www.jetbrains.com/?from=patrikx3) [![JetBrains](https://cdn.corifeus.com/assets/svg/jetbrains-logo.svg)](https://www.jetbrains.com/?from=patrikx3) [//]: #@corifeus-footer:end artifacts/000077500000000000000000000000001520154223400130345ustar00rootroot00000000000000artifacts/boot/000077500000000000000000000000001520154223400137775ustar00rootroot00000000000000artifacts/boot/p3xrs.json000066400000000000000000000015511520154223400157530ustar00rootroot00000000000000{ "p3xrs": { "http": { "port-info": "this is ommitted, it will be default 7843", "port": 7843 }, "connections": { "home-dir-info": "if the dir config is empty or home, the connections are saved in the home folder, otherwise it will resolve the directory set as it is, either relative ./ or absolute starting with /. NodeJs will resolve this directory in p3xrs.connections.dir", "home-dir": "home" }, "static-info": "This is the best configuration, if it starts with ~, then it is in resolve the path in the node_modules, otherwise it resolves to the current process current working directory.", "static-disabled": "~p3x-redis-ui-material/dist", "treeDividers": [ ":", "/", "|", "-", "@" ] } } artifacts/cluster.md000066400000000000000000000035321520154223400150420ustar00rootroot00000000000000[//]: #@corifeus-header # 🏍️ The p3x-redis-ui-server package motor that is connected to the p3x-redis-ui-material web user interface [//]: #@corifeus-header:end # Run a Docker Cluster ```bash docker run -e "IP=0.0.0.0" -p 7000:7000 -p 7001:7001 -p 7002:7002 -p 7003:7003 -p 7004:7004 -p 7005:7005 grokzen/redis-cluster:latest ``` [//]: #@corifeus-footer --- 🙏 This is an open-source project. Star this repository, if you like it, or even donate to maintain the servers and the development. Thank you so much! Possible, this server, rarely, is down, please, hang on for 15-30 minutes and the server will be back up. All my domains ([patrikx3.com](https://patrikx3.com) and [corifeus.com](https://corifeus.com)) could have minor errors, since I am developing in my free time. However, it is usually stable. **Note about versioning:** Versions are cut in Major.Minor.Patch schema. Major is always the current year. Minor is either 4 (January - June) or 10 (July - December). Patch is incremental by every build. If there is a breaking change, it should be noted in the readme. --- [**P3X-REDIS-UI-SERVER**](https://corifeus.com/redis-ui-server) Build v2022.10.105 [![Donate for Corifeus / P3X](https://img.shields.io/badge/Donate-Corifeus-003087.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) [![Like Corifeus @ Facebook](https://img.shields.io/badge/LIKE-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) ## P3X Sponsor [IntelliJ - The most intelligent Java IDE](https://www.jetbrains.com/?from=patrikx3) [![JetBrains](https://cdn.corifeus.com/assets/svg/jetbrains-logo.svg)](https://www.jetbrains.com/?from=patrikx3) [//]: #@corifeus-footer:end bin/000077500000000000000000000000001520154223400116245ustar00rootroot00000000000000bin/p3xrs.js000077500000000000000000000001031520154223400132360ustar00rootroot00000000000000#!/usr/bin/env node const boot = require('../src/lib/boot') boot() package.json000066400000000000000000000036771520154223400133570ustar00rootroot00000000000000{ "name": "p3x-redis-ui-server", "version": "2022.10.105", "description": "🏍️ The p3x-redis-ui-server package motor that is connected to the p3x-redis-ui-material web user interface", "corifeus": { "icon": "fas fa-flag-checkered", "code": "Reverse", "opencollective": false, "build": true, "nodejs": "v18.12.1", "reponame": "redis-ui-server", "publish": true, "prefix": "p3x-", "type": "p3x" }, "main": "src/indexjs", "bin": { "p3xrs": "./bin/p3xrs.js" }, "scripts": { "test": "grunt", "start": "node ./bin/p3xrs --config ./p3xrs.json", "dev": "nodemon --watch src --watch package.json --watch bin ./bin/p3xrs", "dev-readonly-connections": "nodemon --watch src --watch package.json --watch bin ./bin/p3xrs --readonly-connections" }, "watch": { "run": "src/**/*.js" }, "repository": { "type": "git", "url": "https://github.com/patrikx3/redis-ui-server.git" }, "keywords": [ "redis", "ui", "gui", "web", "electron", "desktop", "server", "angularjs", "javascript", "material", "dark", "light" ], "author": "Patrik Laszlo ", "license": "MIT", "devDependencies": { "corifeus-builder": "^2022.10.102", "nodemon": "^2.0.20" }, "dependencies-saved": { "koa-body": "^4.0.4", "koa-router": "^7.4.0" }, "dependencies": { "chalk": "^4.1.2", "commander": "^9.4.1", "corifeus-utils": "^2022.4.130", "ioredis": "^5.2.4", "koa": "^2.14.1", "koa-send": "^5.0.1", "koa-static": "^5.0.0", "lodash": "^4.17.21", "socket.io": "^4.5.4" }, "engines": { "node": ">=12.13.0" }, "homepage": "https://corifeus.com/redis-ui-server" }redis-ui-server.iml000066400000000000000000000011551520154223400146060ustar00rootroot00000000000000 scripts/000077500000000000000000000000001520154223400125435ustar00rootroot00000000000000scripts/redis-bulk.lua000066400000000000000000000003541520154223400153110ustar00rootroot00000000000000-- -- Created by IntelliJ IDEA. -- User: patrikx3 -- Date: 7/9/20 -- Time: 4:59 PM -- To change this template use File | Settings | File Templates. -- for i = 1, 100000, 1 do redis.call("SET", "bulk-key-"..i, i) end return "Ok!" scripts/redis-cluster.sh000077500000000000000000000002311520154223400156630ustar00rootroot00000000000000#!/usr/bin/env bash docker run -e "IP=0.0.0.0" -p 7000:7000 -p 7001:7001 -p 7002:7002 -p 7003:7003 -p 7004:7004 -p 7005:7005 grokzen/redis-cluster:latestsrc/000077500000000000000000000000001520154223400116435ustar00rootroot00000000000000src/index.js000066400000000000000000000001251520154223400133060ustar00rootroot00000000000000module.exports = { lib: require('./lib'), services: require('./service'), } src/lib/000077500000000000000000000000001520154223400124115ustar00rootroot00000000000000src/lib/boot.js000066400000000000000000000020531520154223400137120ustar00rootroot00000000000000require('corifeus-utils'); const boot = async () => { global.p3xrs = {} p3xrs.cfg = undefined const cli = require('./cli'); if (!cli()) { return; } const consoleStamp = require('./console-stamp') consoleStamp() const koaService = require('../service/koa') p3xrs.koa = new koaService() await p3xrs.koa.boot() const socketIoService = require('../service/socket.io') p3xrs.socketIo = new socketIoService(); await p3xrs.socketIo.boot({ koaService: p3xrs.koa }) const checkLicense = require('./check-license') checkLicense({ socket: p3xrs.socketIo.socketio, payload: { license: p3xrs.connections.license } }) setInterval(() => { checkLicense({ socket: p3xrs.socketIo.socketio, payload: { license: p3xrs.connections.license } }) }, 1000 * 60 /* 1 minute */ * 60) p3xrs.redisConnections = {} p3xrs.redisConnectionsSubscriber = {} } module.exports = boot src/lib/check-license.js000066400000000000000000000043171520154223400154510ustar00rootroot00000000000000const utils = require('corifeus-utils') module.exports = async (options) => { const { socket } = options; p3xrs.cfg.donated = true socket.emit(options.responseEvent || 'info-interval', { donated: true, info: 'ok', status: 'ok', }) /* console.log(new Date().toLocaleString(), 'check license') let license = options.payload.license || ''; let donated = false try { let serverError = false let disableDonated = false if (typeof license === 'string' && license.trim().length === 0) { disableDonated = true } if (disableDonated === false && (typeof license !== 'string' || license.length !== 128)) { throw new Error('invalid_license') } if (!disableDonated) { const response = await utils.http.request({ url: `https://server.patrikx3.com/api/patrikx3/redis-ui/status/${license}` // url: `https://server.patrikx3.com/api/patrikx3/test/521` }) if (response.statusCode !== 200) { license = '' serverError = true } else if (response.body.isValid === false) { license = '' } else { donated = true } } if (typeof license === 'string' && license.length === 128) { console.log(new Date().toLocaleString(), 'valid license') } else { console.log(new Date().toLocaleString(), 'in-valid license') } const fs = require('fs') p3xrs.connections.license = license fs.writeFileSync(p3xrs.cfg.connections.home, JSON.stringify(p3xrs.connections, null, 4)) p3xrs.cfg.donated = donated socket.emit(options.responseEvent || 'info-interval', { donated: donated, info: disableDonated ? 'cleared_license' : (serverError ? 'server_error': 'ok'), status: 'ok', }) } catch (e) { p3xrs.cfg.donated = false console.error(e) socket.emit(options.responseEvent || 'info-interval', { donated: false, status: 'error', error: e.message }) } */ } src/lib/cli.js000066400000000000000000000106751520154223400135270ustar00rootroot00000000000000const path = require('path') const fs = require('fs') const cli = () => { const pkg = require('../../package') if (!process.versions.hasOwnProperty('electron') && !process.env.hasOwnProperty('P3XRS_DOCKER_HOME')) { const program = require('commander') program .version(pkg.version) .option('-c, --config [config]', 'Set the p3xr.json p3x-redis-ui-server configuration, see more help in https://github.com/patrikx3/redis-ui-server') .option('-r, --readonly-connections', 'Set the connections to be readonly, no adding, saving or delete a connection') .option('-n, --connections-file-name [filename]', 'Set the connections file name, overrides default .p3xrs-conns.json') .parse(process.argv); const programOptions = program.opts(); if (!programOptions.config) { programOptions.config = path.resolve(path.dirname(require.main.filename) + path.sep + '..', `.${path.sep}p3xrs.json`) // program.outputHelp() // return false } const configPath = path.resolve(process.cwd(), programOptions.config) //console.log(configPath) p3xrs.cfg = require(configPath).p3xrs if (programOptions.readonlyConnections) { // console.warn(programOptions.readonlyConnections) p3xrs.cfg.readonlyConnections = true //console.warn(p3xrs.cfg.readonlyConnections === true) } if (typeof programOptions.connectionsFileName !== 'undefined' && programOptions.connectionsFileName) { // console.warn(programOptions.connectionsFileName) p3xrs.cfg.connectionsFileName = programOptions.connectionsFileName //console.warn(p3xrs.cfg.readonlyConnections === true) } } else { p3xrs.cfg = { "http": { "port-info": "this is ommitted, it will be default 7843", "port": process.env.hasOwnProperty('P3XRS_DOCKER_HOME') ? 7843 : global.p3xrsElectronPort }, "connections": { "home-dir-info": "if the dir config is empty or home, the connections are saved in the home folder, otherwise it will resolve the directory set as it is, either relative ./ or absolute starting with /. NodeJs will resolve this directory in p3xrs.connections.dir", "home-dir": "home" }, "static-info": "This is the best configuration, if it starts with ~, then it is in resolve the path in the node_modules, otherwise it resolves to the current process current working directory.", "static": "~p3x-redis-ui-material/dist", "treeDividers": [ ":", "/", "|", "-", "@" ] } p3xrs.cfg.readonlyConnections = false } if (p3xrs.cfg.connectionsFileName === undefined) { p3xrs.cfg.connectionsFileName = '.p3xrs-conns.json' } if (!p3xrs.cfg.hasOwnProperty('static')) { } if (!p3xrs.cfg.hasOwnProperty('connections')) { p3xrs.cfg.connections = {} } if (!p3xrs.cfg.connections.hasOwnProperty('home-dir')) { p3xrs.cfg.connections = 'home' } if (p3xrs.cfg.connections['home-dir'] === 'home') { p3xrs.cfg.connections['home-dir'] = require('os').homedir(); } if (process.env.hasOwnProperty('P3XRS_DOCKER_HOME')) { p3xrs.cfg.connections['home-dir'] = process.env.P3XRS_DOCKER_HOME } if (process.env.hasOwnProperty('P3XRS_PORT')) { p3xrs.cfg.http.port = process.env.P3XRS_PORT } p3xrs.cfg.connections['home'] = path.resolve(p3xrs.cfg.connections['home-dir'], p3xrs.cfg.connectionsFileName) console.info('using home config is', p3xrs.cfg.connections['home']) if (!fs.existsSync(p3xrs.cfg.connections.home)) { fs.writeFileSync(p3xrs.cfg.connections.home, JSON.stringify({ update: new Date(), list: [], }, null, 4)) } p3xrs.connections = require(p3xrs.cfg.connections.home) //console.log(p3xrs.cfg.connections.home, p3xrs.connections) //console.log(p3xrs.connections) /* p3xrs.redis = {} let keyStreamPaging = 10000 Object.defineProperty(p3xrs.redis, 'key-stream-paging', { get: () => { return keyStreamPaging }, set: (value) => { keyStreamPaging = value } }) */ return true; } module.exports = cli; src/lib/console-stamp.js000066400000000000000000000025721520154223400155410ustar00rootroot00000000000000const chalk = require('chalk'); const consoleStamp = () => { // overriding the console should be after this!!! const methods = ['log', 'info', 'warn', 'error', 'debug'] const originalMethods = {} for(let method of methods) { originalMethods[method] = console[method] console[method] = function() { if (arguments[0]) { let label switch(method) { case 'error': label = chalk`{bold.red ${method.toUpperCase()}}`; break; case 'warn': label = chalk`{bold.blue ${method.toUpperCase()}}`; break; default: label = chalk`{green ${method.toUpperCase()}}`; } let data = '' //chalk`${moment().format(`YYYY/MM/DD HH:mm:ss.SSS`)} ` data += chalk`{black.grey [P3XRS]}` + ` [PID: ${(String(process.pid).padStart(6, 0))}] ` //arguments[0] = data + arguments[0] const mainArguments = Array.prototype.slice.call(arguments); mainArguments.unshift(data); originalMethods[method].apply(null, mainArguments) } else { originalMethods[method].apply(null, arguments) } } } } module.exports = consoleStamp src/lib/index.js000066400000000000000000000001721520154223400140560ustar00rootroot00000000000000module.exports = { boot: require('./boot'), cli: require('./cli'), consoleStamp: require('./console-stamp'), }src/lib/ioredis-cluster/000077500000000000000000000000001520154223400155265ustar00rootroot00000000000000src/lib/ioredis-cluster/cluster.js000066400000000000000000000124641520154223400175540ustar00rootroot00000000000000const Redis = require('ioredis') const {EventEmitter} = require('events') const redisInfo = require('./redis-info') const setDefaultOptionsFromServer = require('./set-default-options-from-server') module.exports = class Cluster extends Redis.Cluster { constructor(server, options = {}) { server = Array.isArray(server) ? server : [server] options = setDefaultOptionsFromServer(options, server) super(server, options) } /* async infoObject(...args) { const [key] = args if (key === 'keyspace') { return { databases: [await this._getClusterInfoKeyspace()] } } const info = await this.info(...args) const infoObject = redisInfo.parse(info) if (!key) { infoObject.databases[0] = await this._getClusterInfoKeyspace() } return infoObject } */ async _getClusterInfoKeyspace(info) { const keyspaceList = await Promise.all(this.nodes('master').map(node => { return node.info('keyspace') })) let keys = 0 let expires = 0 let avg_ttl = 0 for (const nodeKeyspace of keyspaceList) { if (!nodeKeyspace) { continue } const parsed = redisInfo.parse(nodeKeyspace) const db0 = parsed.databases[0] if (!db0) { continue } const { keys: nodeKeys = 0, expires: nodeExpires = 0, avg_ttl: nodeAvgTtl = 0, } = db0 keys += nodeKeys expires += nodeExpires avg_ttl += nodeAvgTtl } avg_ttl = avg_ttl ? Math.round(avg_ttl / expires) : 0 const clusterKeyspace = { keys, expires, avg_ttl, } // console.log({clusterKeyspace}) return clusterKeyspace } async originalDbsize(...args) { return super.dbsize(...args) } async dbsize() { const nodeCounts = await Promise.all(this.nodes('master').map(node => { return node.dbsize() })) const count = nodeCounts.reduce((tt, c) => tt + c, 0) return count } originalRename(...args) { return super.rename(...args) } async rename(key, keyNew, callback) { let res = null let err = null try { let [value, ttl] = await Promise.all([ this.dumpBuffer(key), this.ttl(key), ]) ttl = parseInt(ttl) if (ttl < 0) { ttl = 0 } await this.del(keyNew) await this.restore(keyNew, ttl, value) await this.del(key) res = 'OK' } catch (e) { err = e } if (typeof callback === 'function') { callback(err, res) } else if (err) { throw err } return res } originalPipeline(...args) { return super.pipeline(...args) } pipeline(...pipelineArgs) { const calls = [] async function exec(calls) { const results = [] for (let c of calls) { const result = await c() results.push(result) } // console.log({results}) return results } const proxy = new Proxy(calls, { get: (calls, method) => { return (...callArgs) => { switch (method) { case 'exec': return exec(calls) break } const callback = async () => { let err = null let result = null try { result = await this[method](...callArgs) } catch (e) { err = e } return [err, result] } calls.push(callback) return proxy } } }) return proxy } scanStream(...args) { const stream = new EventEmitter() this._streamNodes({ stream, method: 'scanStream', params: args, }) return stream } async _streamNodes(options = {}) { let { nodes = this.nodes('master'), stream = new EventEmitter(), method, params = [], } = options try { const promises = [] for (let node of nodes) { promises.push( new Promise((resolve, reject) => { const nodeStream = node[method](...params) nodeStream.on('data', (resultKeys) => { // console.log({resultKeys}) stream.emit('data', resultKeys) }) nodeStream.on('end', () => { resolve() }) }) ) } await Promise.all(promises) } finally { stream.emit('end') } } } src/lib/ioredis-cluster/create-with-cluster-auto-detect.js000066400000000000000000000013101520154223400241660ustar00rootroot00000000000000const Redis = require('ioredis') const isClusterEnabled = require('./is-cluster-enabled') const getClusterNodes = require('./get-cluster-nodes') const Cluster = require('./cluster') const setDefaultOptionsFromServer = require('./set-default-options-from-server') module.exports = async function createWithClusterAutoDetect(server, options = {}) { let isCluster if (Array.isArray(server)) { isCluster = true } else { isCluster = await isClusterEnabled(server, true) } if (!isCluster) { return new Redis(server) } // server = await getClusterNodes(server) options = setDefaultOptionsFromServer(options, server) return new Cluster(server, options) } src/lib/ioredis-cluster/get-cluster-nodes.js000066400000000000000000000036271520154223400214400ustar00rootroot00000000000000const Redis = require('ioredis') module.exports = async function getClusterNodes(servers, options = {}) { if (!Array.isArray(servers)) { servers = [servers] } const errors = [] let nodes for (const server of servers) { try { const redis = new Redis({...server, retryStrategy: () => false}) const rawNodes = await new Promise((resolve, reject) => { redis.sendCommand( new Redis.Command( 'CLUSTER', ['NODES'], 'utf-8', function (err, value) { if (err) reject(err) else resolve(value.toString()) } ) ) }) const lines = rawNodes.trim().split("\n") nodes = lines.reduce((arr, line) => { if (!line) { return arr } const row = line.split(' ') const [ node_id, server, flags, ] = row const [target, slots] = server.split('@') const [host, port] = target.split(':') const node = { host, port, } arr.push(node) return arr }, []) return nodes } catch (error) { console.error(error) errors.push(error) } finally { redis.disconnect() } if (nodes) { break } } if (nodes) { return nodes } const errorsMsg = errors.map(e => e.toString()).join(', ') throw new Error('getClusterNodes errors: ' + errorsMsg) } src/lib/ioredis-cluster/get-info.js000066400000000000000000000004541520154223400175770ustar00rootroot00000000000000const Redis = require('ioredis') const redisInfo = require('./redis-info') module.exports = async function getInfo(server, options = {}) { const redis = new Redis(server) const rawInfo = await redis.info() redis.disconnect() const info = redisInfo.parse(rawInfo) return info } src/lib/ioredis-cluster/index.js000066400000000000000000000000701520154223400171700ustar00rootroot00000000000000const Redis = require('./redis') module.exports = Redis src/lib/ioredis-cluster/is-cluster-enabled.js000066400000000000000000000003351520154223400215470ustar00rootroot00000000000000const getInfo = require('./get-info') module.exports = async function isClusterEnabled(server, cache = false) { const {cluster_enabled} = await getInfo(server, {cache}) return Boolean(parseInt(cluster_enabled)) } src/lib/ioredis-cluster/redis-info.js000066400000000000000000000056171520154223400201340ustar00rootroot00000000000000/* from npm's redis-info, adding missing avg_ttl */ const { fromPairs, find, has, } = require('lodash') module.exports = { parse: function (info) { return parseFields(splitStr(info)) } } function startWith(pattern) { return function (value) { return value.indexOf(pattern) === 0 } } function split(s) { return function (v) { return v.split(s) } } function orEmptyStr(v) { return v || '' } function takeN(func, n) { return function (v) { return func(v[n]) } } function takeFirst(func) { return takeN(func, 0) } /** * Split the info string by \n and : * @param {String} str the returned redis info * @return {Array} Array of [key, value] */ function splitStr(str) { return str.split('\n') .filter(function (line) { return line.length > 0 && line.indexOf('#') !== 0 }) .map(function (line) { return line.trim().split(':') }) } function parseDatabases(info) { return info .filter(takeFirst(startWith('db'))) .map(function _parseDatabaseInfo(args) { var dbName = args[0] var value = args[1] var values = orEmptyStr(value).split(',') // console.log({values}) function extract(param) { return parseInt(orEmptyStr(find(values, startWith(param))).split('=')[1] || 0, 10) } return { index: parseInt(dbName.substr(2), 10), keys: extract('keys'), expires: extract('expires'), avg_ttl: extract('avg_ttl'), } }) .reduce(function (m, v) { m[v.index] = { keys: v.keys, expires: v.expires, avg_ttl: v.avg_ttl, } return m }, {}) } function parseCommands(info) { return fromPairs(info.filter(function (a) { return orEmptyStr(a[0]).indexOf('cmdstat_') === 0 }) .map(function _parseCommands(args) { var v = args[0] var a = args[1] var val = fromPairs(orEmptyStr(a).split(',').map(split('='))) if (has(val, 'calls')) { val.calls = parseInt(val.calls, 10) } if (has(val, 'usec')) { val.usec = parseInt(val.usec, 10) } if (has(val, 'usec_per_call')) { val.usec_per_call = parseFloat(val.usec_per_call, 10) } return [orEmptyStr(v).split('_')[1], val] })) } function parseFields(info) { var fields = info.reduce(function (m, v) { if (!v[0].trim() || v[0].indexOf('db') === 0 || v[0].indexOf('cmdstat_') === 0) { return m } m[v[0]] = v[1] return m }, { databases: parseDatabases(info), commands: parseCommands(info) }) return fields } src/lib/ioredis-cluster/redis.js000066400000000000000000000017371520154223400172020ustar00rootroot00000000000000const IORedis = require('ioredis') const redisInfo = require('./redis-info') const Cluster = require('./cluster') const createWithClusterAutoDetect = require('./create-with-cluster-auto-detect') const getInfo = require('./get-info') const getClusterNodes = require('./get-cluster-nodes') const isClusterEnabled = require('./is-cluster-enabled') class Redis extends IORedis { constructor(server, {autoDetectCluster, ...options} = {}) { if (autoDetectCluster && !Array.isArray(server)) { return createWithClusterAutoDetect(server, options) } if (Array.isArray(server)) { return new Cluster(server, options) } super(server) } /* async infoObject(...args) { const info = await this.info(...args) return redisInfo.parse(info) } */ } Redis.Cluster = Cluster Redis.isClusterEnabled = isClusterEnabled Redis.getClusterNodes = getClusterNodes Redis.getInfo = getInfo module.exports = Redis src/lib/ioredis-cluster/set-default-options-from-server.js000066400000000000000000000023711520154223400242420ustar00rootroot00000000000000function getDefaultOptionsFromServer(server) { const server1 = Array.isArray(server) ? server[0] : server if (typeof server1 === 'object' && server1 !== null) { return server1 } } module.exports = function(options, server) { const serverOptions = getDefaultOptionsFromServer(server) let {redisOptions} = options if (redisOptions === undefined) { redisOptions = {} options.redisOptions = redisOptions } if (redisOptions.password === undefined) { redisOptions.password = serverOptions.password } if (serverOptions.tlsWithoutCert) { serverOptions.tls = { } } else if (typeof serverOptions.tlsCa === 'string' && serverOptions.tlsCa.trim() !== '') { redisOptions.tls = { cert: serverOptions.tlsCrt, key: serverOptions.tlsKey, ca: serverOptions.tlsCa, } } if ((typeof serverOptions.tlsCa === 'string' && serverOptions.tlsCa.trim() !== '') || serverOptions.tlsWithoutCert) { redisOptions.tls.rejectUnauthorized = serverOptions.tlsRejectUnauthorized redisOptions.tls.rejectUnauthorized = serverOptions.tlsRejectUnauthorized === undefined ? false : serverOptions.tlsRejectUnauthorized } return options } src/service/000077500000000000000000000000001520154223400133035ustar00rootroot00000000000000src/service/index.js000066400000000000000000000000571520154223400147520ustar00rootroot00000000000000module.exports = { koa: require('./koa'), }src/service/koa/000077500000000000000000000000001520154223400140555ustar00rootroot00000000000000src/service/koa/index.js000066400000000000000000000062341520154223400155270ustar00rootroot00000000000000const Koa = require('koa'); //const Router = require('koa-router') const fs = require('fs') //const koaBody = require('koa-body') const path = require('path') const koaService = function () { const self = this; self.boot = async () => { const app = new Koa(); this.app = app; // const router = new Router(); // this.router = router; // app.use(koaBody()); const resolvePath = (inputPath) => { let resolvedPath if (inputPath.startsWith('~')) { const inputPathFromNodeModules = inputPath.substring(1) resolvedPath = path.resolve(path.dirname(require.main.filename) + path.sep + '..', `node_modules${path.sep}${inputPathFromNodeModules}`) } else { resolvedPath = path.resolve(process.cwd(), inputPath) } return resolvedPath } let hasStatic = false let staticPath if (typeof p3xrs.cfg.static === 'string') { hasStatic = true staticPath = resolvePath(p3xrs.cfg.static) const serve = require('koa-static'); app.use(serve(staticPath)); } app.on('error', err => { console.error('koa server error', err) }); /* app.context.p3x = { status: { 404: () => { const error = new Error('not-found'); error.status = 404; throw error; } } } */ /* app.use(async (ctx) => { ctx.body = { status: 'operational' }; }); */ if (hasStatic) { const send = require('koa-send') app.use(async (ctx) => { await send(ctx, 'index.html', {root: staticPath}); }); } else { app.use(async (ctx) => { ctx.response.body = { status: 'operational' } }); } // app.use(router.routes()) // app.use(router.allowedMethods()); /* const keyFilename = resolvePath(p3xrs.cfg.https2.key) const certFilename = resolvePath(p3xrs.cfg.https2.cert) const certs = [ // key fs.readFileSync(keyFilename), // cert fs.readFileSync(certFilename), ] const options = { key: certs[0].toString(), cert: certs[1].toString(), }; */ //console.warn('keyFilename', keyFilename, options.key) //console.warn('certFilename', certFilename, options.cert) //const spdy = require('spdy'); //const server = spdy.createServer(options, app.callback()) // not working with websocket-s native node http2 //const http2 = require('http2'); //const server = http2.createSecureServer(options, app.callback()); const http = require('http') const server = http.createServer(app.callback()) this.server = server; server.listen(p3xrs.cfg.http.port || 7843, '0.0.0.0'); } } module.exports = koaService src/service/socket.io/000077500000000000000000000000001520154223400152015ustar00rootroot00000000000000src/service/socket.io/index.js000066400000000000000000000006551520154223400166540ustar00rootroot00000000000000const socketIo = require('socket.io') const socketIoService = function () { const self = this; self.boot = async (options) => { const {koaService} = options const socketio = require('socket.io')(koaService.server, { secure: true, path: '/socket.io', }); require('./socket')(socketio); this.socketio = socketio } } module.exports = socketIoService src/service/socket.io/request/000077500000000000000000000000001520154223400166715ustar00rootroot00000000000000src/service/socket.io/request/connection-connect.js000066400000000000000000000611251520154223400230220ustar00rootroot00000000000000const donationWareFeatureError = new Error('donation-ware-feature') donationWareFeatureError.code = 'donation-ware-feature' const consolePrefix = 'socket.io connection-connect'; const Redis = require('../../../lib/ioredis-cluster') const sharedIoRedis = require('../shared') const staticCommands = [["xclaim",-6,["write","random","fast"],1,1,1,["@write","@stream","@fast"]],["xgroup",-2,["write","denyoom"],2,2,1,["@write","@stream","@slow"]],["lpos",-3,["readonly"],1,1,1,["@read","@list","@slow"]],["config",-2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"]],["zincrby",4,["write","denyoom","fast"],1,1,1,["@write","@sortedset","@fast"]],["hvals",2,["readonly","sort_for_script"],1,1,1,["@read","@hash","@slow"]],["lset",4,["write","denyoom"],1,1,1,["@write","@list","@slow"]],["info",-1,["random","loading","stale"],0,0,0,["@slow","@dangerous"]],["xrange",-4,["readonly"],1,1,1,["@read","@stream","@slow"]],["brpoplpush",4,["write","denyoom","noscript"],1,2,1,["@write","@list","@slow","@blocking"]],["type",2,["readonly","fast"],1,1,1,["@keyspace","@read","@fast"]],["sismember",3,["readonly","fast"],1,1,1,["@read","@set","@fast"]],["pfadd",-2,["write","denyoom","fast"],1,1,1,["@write","@hyperloglog","@fast"]],["set",-3,["write","denyoom"],1,1,1,["@write","@string","@slow"]],["georadiusbymember",-5,["write","denyoom","movablekeys"],1,1,1,["@write","@geo","@slow"]],["zpopmax",-2,["write","fast"],1,1,1,["@write","@sortedset","@fast"]],["bzpopmin",-3,["write","noscript","fast"],1,-2,1,["@write","@sortedset","@fast","@blocking"]],["pttl",2,["readonly","random","fast"],1,1,1,["@keyspace","@read","@fast"]],["script",-2,["noscript"],0,0,0,["@slow","@scripting"]],["scard",2,["readonly","fast"],1,1,1,["@read","@set","@fast"]],["expireat",3,["write","fast"],1,1,1,["@keyspace","@write","@fast"]],["save",1,["admin","noscript"],0,0,0,["@admin","@slow","@dangerous"]],["georadius",-6,["write","denyoom","movablekeys"],1,1,1,["@write","@geo","@slow"]],["object",-2,["readonly","random"],2,2,1,["@keyspace","@read","@slow"]],["lrange",4,["readonly"],1,1,1,["@read","@list","@slow"]],["monitor",1,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"]],["hsetnx",4,["write","denyoom","fast"],1,1,1,["@write","@hash","@fast"]],["sunion",-2,["readonly","sort_for_script"],1,-1,1,["@read","@set","@slow"]],["hexists",3,["readonly","fast"],1,1,1,["@read","@hash","@fast"]],["decrby",3,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"]],["pexpire",3,["write","fast"],1,1,1,["@keyspace","@write","@fast"]],["geohash",-2,["readonly"],1,1,1,["@read","@geo","@slow"]],["sdiffstore",-3,["write","denyoom"],1,-1,1,["@write","@set","@slow"]],["setnx",3,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"]],["restore",-4,["write","denyoom"],1,1,1,["@keyspace","@write","@slow","@dangerous"]],["xreadgroup",-7,["write","movablekeys"],0,0,0,["@write","@stream","@slow","@blocking"]],["llen",2,["readonly","fast"],1,1,1,["@read","@list","@fast"]],["brpop",-3,["write","noscript"],1,-2,1,["@write","@list","@slow","@blocking"]],["sinter",-2,["readonly","sort_for_script"],1,-1,1,["@read","@set","@slow"]],["append",3,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"]],["rpoplpush",3,["write","denyoom"],1,2,1,["@write","@list","@slow"]],["rpushx",-3,["write","denyoom","fast"],1,1,1,["@write","@list","@fast"]],["hkeys",2,["readonly","sort_for_script"],1,1,1,["@read","@hash","@slow"]],["rpush",-3,["write","denyoom","fast"],1,1,1,["@write","@list","@fast"]],["ttl",2,["readonly","random","fast"],1,1,1,["@keyspace","@read","@fast"]],["scan",-2,["readonly","random"],0,0,0,["@keyspace","@read","@slow"]],["unsubscribe",-1,["pubsub","noscript","loading","stale"],0,0,0,["@pubsub","@slow"]],["lpop",2,["write","fast"],1,1,1,["@write","@list","@fast"]],["setbit",4,["write","denyoom"],1,1,1,["@write","@bitmap","@slow"]],["ping",-1,["stale","fast"],0,0,0,["@fast","@connection"]],["hmset",-4,["write","denyoom","fast"],1,1,1,["@write","@hash","@fast"]],["wait",3,["noscript"],0,0,0,["@keyspace","@slow"]],["hget",3,["readonly","fast"],1,1,1,["@read","@hash","@fast"]],["strlen",2,["readonly","fast"],1,1,1,["@read","@string","@fast"]],["bitcount",-2,["readonly"],1,1,1,["@read","@bitmap","@slow"]],["substr",4,["readonly"],1,1,1,["@read","@string","@slow"]],["auth",-2,["noscript","loading","stale","skip_monitor","skip_slowlog","fast","no_auth"],0,0,0,["@fast","@connection"]],["zinterstore",-4,["write","denyoom","movablekeys"],1,1,1,["@write","@sortedset","@slow"]],["zrank",3,["readonly","fast"],1,1,1,["@read","@sortedset","@fast"]],["geodist",-4,["readonly"],1,1,1,["@read","@geo","@slow"]],["publish",3,["pubsub","loading","stale","fast"],0,0,0,["@pubsub","@fast"]],["xtrim",-2,["write","random"],1,1,1,["@write","@stream","@slow"]],["psync",3,["admin","noscript"],0,0,0,["@admin","@slow","@dangerous"]],["hincrby",4,["write","denyoom","fast"],1,1,1,["@write","@hash","@fast"]],["hstrlen",3,["readonly","fast"],1,1,1,["@read","@hash","@fast"]],["psubscribe",-2,["pubsub","noscript","loading","stale"],0,0,0,["@pubsub","@slow"]],["linsert",5,["write","denyoom"],1,1,1,["@write","@list","@slow"]],["bitpos",-3,["readonly"],1,1,1,["@read","@bitmap","@slow"]],["replicaof",3,["admin","noscript","stale"],0,0,0,["@admin","@slow","@dangerous"]],["del",-2,["write"],1,-1,1,["@keyspace","@write","@slow"]],["hgetall",2,["readonly","random"],1,1,1,["@read","@hash","@slow"]],["echo",2,["readonly","fast"],0,0,0,["@read","@fast","@connection"]],["unwatch",1,["noscript","loading","stale","fast"],0,0,0,["@fast","@transaction"]],["dump",2,["readonly","random"],1,1,1,["@keyspace","@read","@slow"]],["watch",-2,["noscript","loading","stale","fast"],1,-1,1,["@fast","@transaction"]],["zrevrange",-4,["readonly"],1,1,1,["@read","@sortedset","@slow"]],["srandmember",-2,["readonly","random"],1,1,1,["@read","@set","@slow"]],["zrangebylex",-4,["readonly"],1,1,1,["@read","@sortedset","@slow"]],["getrange",4,["readonly"],1,1,1,["@read","@string","@slow"]],["keys",2,["readonly","sort_for_script"],0,0,0,["@keyspace","@read","@slow","@dangerous"]],["sinterstore",-3,["write","denyoom"],1,-1,1,["@write","@set","@slow"]],["sadd",-3,["write","denyoom","fast"],1,1,1,["@write","@set","@fast"]],["zrem",-3,["write","fast"],1,1,1,["@write","@sortedset","@fast"]],["setrange",4,["write","denyoom"],1,1,1,["@write","@string","@slow"]],["flushdb",-1,["write"],0,0,0,["@keyspace","@write","@slow","@dangerous"]],["renamenx",3,["write","fast"],1,2,1,["@keyspace","@write","@fast"]],["getbit",3,["readonly","fast"],1,1,1,["@read","@bitmap","@fast"]],["zremrangebylex",4,["write"],1,1,1,["@write","@sortedset","@slow"]],["pfmerge",-2,["write","denyoom"],1,-1,1,["@write","@hyperloglog","@slow"]],["sunionstore",-3,["write","denyoom"],1,-1,1,["@write","@set","@slow"]],["xsetid",3,["write","denyoom","fast"],1,1,1,["@write","@stream","@fast"]],["hincrbyfloat",4,["write","denyoom","fast"],1,1,1,["@write","@hash","@fast"]],["exists",-2,["readonly","fast"],1,-1,1,["@keyspace","@read","@fast"]],["pfcount",-2,["readonly"],1,-1,1,["@read","@hyperloglog","@slow"]],["time",1,["readonly","random","loading","stale","fast"],0,0,0,["@read","@fast"]],["rename",3,["write"],1,2,1,["@keyspace","@write","@slow"]],["sort",-2,["write","denyoom","movablekeys"],1,1,1,["@write","@set","@sortedset","@list","@slow","@dangerous"]],["pexpireat",3,["write","fast"],1,1,1,["@keyspace","@write","@fast"]],["mset",-3,["write","denyoom"],1,-1,2,["@write","@string","@slow"]],["rpop",2,["write","fast"],1,1,1,["@write","@list","@fast"]],["evalsha",-3,["noscript","movablekeys"],0,0,0,["@slow","@scripting"]],["subscribe",-2,["pubsub","noscript","loading","stale"],0,0,0,["@pubsub","@slow"]],["bitfield_ro",-2,["readonly","fast"],1,1,1,["@read","@bitmap","@fast"]],["zscore",3,["readonly","fast"],1,1,1,["@read","@sortedset","@fast"]],["multi",1,["noscript","loading","stale","fast"],0,0,0,["@fast","@transaction"]],["smembers",2,["readonly","sort_for_script"],1,1,1,["@read","@set","@slow"]],["hdel",-3,["write","fast"],1,1,1,["@write","@hash","@fast"]],["psetex",4,["write","denyoom"],1,1,1,["@write","@string","@slow"]],["expire",3,["write","fast"],1,1,1,["@keyspace","@write","@fast"]],["georadius_ro",-6,["readonly"],1,1,1,["@read","@geo","@slow"]],["client",-2,["admin","noscript","random","loading","stale"],0,0,0,["@admin","@slow","@dangerous","@connection"]],["mget",-2,["readonly","fast"],1,-1,1,["@read","@string","@fast"]],["xrevrange",-4,["readonly"],1,1,1,["@read","@stream","@slow"]],["get",2,["readonly","fast"],1,1,1,["@read","@string","@fast"]],["spop",-2,["write","random","fast"],1,1,1,["@write","@set","@fast"]],["latency",-2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"]],["restore-asking",-4,["write","denyoom","asking"],1,1,1,["@keyspace","@write","@slow","@dangerous"]],["bitop",-4,["write","denyoom"],2,-1,1,["@write","@bitmap","@slow"]],["hello",-2,["noscript","loading","stale","skip_monitor","skip_slowlog","fast","no_auth"],0,0,0,["@fast","@connection"]],["decr",2,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"]],["zcount",4,["readonly","fast"],1,1,1,["@read","@sortedset","@fast"]],["pfdebug",-3,["write","admin"],0,0,0,["@write","@admin","@slow","@dangerous"]],["incrby",3,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"]],["unlink",-2,["write","fast"],1,-1,1,["@keyspace","@write","@fast"]],["xdel",-3,["write","fast"],1,1,1,["@write","@stream","@fast"]],["zrevrangebyscore",-4,["readonly"],1,1,1,["@read","@sortedset","@slow"]],["swapdb",3,["write","fast"],0,0,0,["@keyspace","@write","@fast","@dangerous"]],["cluster",-2,["admin","random","stale"],0,0,0,["@admin","@slow","@dangerous"]],["srem",-3,["write","fast"],1,1,1,["@write","@set","@fast"]],["xpending",-3,["readonly","random"],1,1,1,["@read","@stream","@slow"]],["host:",-1,["readonly","loading","stale"],0,0,0,["@read","@slow"]],["eval",-3,["noscript","movablekeys"],0,0,0,["@slow","@scripting"]],["touch",-2,["readonly","fast"],1,-1,1,["@keyspace","@read","@fast"]],["flushall",-1,["write"],0,0,0,["@keyspace","@write","@slow","@dangerous"]],["acl",-2,["admin","noscript","loading","stale","skip_slowlog"],0,0,0,["@admin","@slow","@dangerous"]],["geoadd",-5,["write","denyoom"],1,1,1,["@write","@geo","@slow"]],["post",-1,["readonly","loading","stale"],0,0,0,["@read","@slow"]],["ltrim",4,["write"],1,1,1,["@write","@list","@slow"]],["lindex",3,["readonly"],1,1,1,["@read","@list","@slow"]],["zremrangebyrank",4,["write"],1,1,1,["@write","@sortedset","@slow"]],["migrate",-6,["write","random","movablekeys"],0,0,0,["@keyspace","@write","@slow","@dangerous"]],["hscan",-3,["readonly","random"],1,1,1,["@read","@hash","@slow"]],["lpush",-3,["write","denyoom","fast"],1,1,1,["@write","@list","@fast"]],["slaveof",3,["admin","noscript","stale"],0,0,0,["@admin","@slow","@dangerous"]],["pubsub",-2,["pubsub","random","loading","stale"],0,0,0,["@pubsub","@slow"]],["zremrangebyscore",4,["write"],1,1,1,["@write","@sortedset","@slow"]],["incrbyfloat",3,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"]],["persist",2,["write","fast"],1,1,1,["@keyspace","@write","@fast"]],["blpop",-3,["write","noscript"],1,-2,1,["@write","@list","@slow","@blocking"]],["zcard",2,["readonly","fast"],1,1,1,["@read","@sortedset","@fast"]],["randomkey",1,["readonly","random"],0,0,0,["@keyspace","@read","@slow"]],["slowlog",-2,["admin","random","loading","stale"],0,0,0,["@admin","@slow","@dangerous"]],["replconf",-1,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"]],["hmget",-3,["readonly","fast"],1,1,1,["@read","@hash","@fast"]],["xlen",2,["readonly","fast"],1,1,1,["@read","@stream","@fast"]],["sync",1,["admin","noscript"],0,0,0,["@admin","@slow","@dangerous"]],["georadiusbymember_ro",-5,["readonly"],1,1,1,["@read","@geo","@slow"]],["xack",-4,["write","random","fast"],1,1,1,["@write","@stream","@fast"]],["xread",-4,["readonly","movablekeys"],0,0,0,["@read","@stream","@slow","@blocking"]],["geopos",-2,["readonly"],1,1,1,["@read","@geo","@slow"]],["bitfield",-2,["write","denyoom"],1,1,1,["@write","@bitmap","@slow"]],["readwrite",1,["fast"],0,0,0,["@keyspace","@fast"]],["debug",-2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"]],["lastsave",1,["readonly","random","loading","stale","fast"],0,0,0,["@read","@admin","@fast","@dangerous"]],["shutdown",-1,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"]],["zrevrank",3,["readonly","fast"],1,1,1,["@read","@sortedset","@fast"]],["move",3,["write","fast"],1,1,1,["@keyspace","@write","@fast"]],["setex",4,["write","denyoom"],1,1,1,["@write","@string","@slow"]],["hlen",2,["readonly","fast"],1,1,1,["@read","@hash","@fast"]],["readonly",1,["fast"],0,0,0,["@keyspace","@fast"]],["role",1,["readonly","noscript","loading","stale","fast"],0,0,0,["@read","@fast","@dangerous"]],["module",-2,["admin","noscript"],0,0,0,["@admin","@slow","@dangerous"]],["discard",1,["noscript","loading","stale","fast"],0,0,0,["@fast","@transaction"]],["pfselftest",1,["admin"],0,0,0,["@hyperloglog","@admin","@slow","@dangerous"]],["asking",1,["fast"],0,0,0,["@keyspace","@fast"]],["smove",4,["write","fast"],1,2,1,["@write","@set","@fast"]],["xadd",-5,["write","denyoom","random","fast"],1,1,1,["@write","@stream","@fast"]],["bgsave",-1,["admin","noscript"],0,0,0,["@admin","@slow","@dangerous"]],["xinfo",-2,["readonly","random"],2,2,1,["@read","@stream","@slow"]],["zpopmin",-2,["write","fast"],1,1,1,["@write","@sortedset","@fast"]],["bgrewriteaof",1,["admin","noscript"],0,0,0,["@admin","@slow","@dangerous"]],["zunionstore",-4,["write","denyoom","movablekeys"],1,1,1,["@write","@sortedset","@slow"]],["lpushx",-3,["write","denyoom","fast"],1,1,1,["@write","@list","@fast"]],["command",-1,["random","loading","stale"],0,0,0,["@slow","@connection"]],["zrange",-4,["readonly"],1,1,1,["@read","@sortedset","@slow"]],["memory",-2,["readonly","random","movablekeys"],0,0,0,["@read","@slow"]],["lolwut",-1,["readonly","fast"],0,0,0,["@read","@fast"]],["lrem",4,["write"],1,1,1,["@write","@list","@slow"]],["hset",-4,["write","denyoom","fast"],1,1,1,["@write","@hash","@fast"]],["punsubscribe",-1,["pubsub","noscript","loading","stale"],0,0,0,["@pubsub","@slow"]],["stralgo",-2,["readonly","movablekeys"],0,0,0,["@read","@string","@slow"]],["bzpopmax",-3,["write","noscript","fast"],1,-2,1,["@write","@sortedset","@fast","@blocking"]],["getset",3,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"]],["sscan",-3,["readonly","random"],1,1,1,["@read","@set","@slow"]],["zadd",-4,["write","denyoom","fast"],1,1,1,["@write","@sortedset","@fast"]],["zscan",-3,["readonly","random"],1,1,1,["@read","@sortedset","@slow"]],["select",2,["loading","stale","fast"],0,0,0,["@keyspace","@fast"]],["sdiff",-2,["readonly","sort_for_script"],1,-1,1,["@read","@set","@slow"]],["zrangebyscore",-4,["readonly"],1,1,1,["@read","@sortedset","@slow"]],["incr",2,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"]],["zlexcount",4,["readonly","fast"],1,1,1,["@read","@sortedset","@fast"]],["msetnx",-3,["write","denyoom"],1,-1,2,["@write","@string","@slow"]],["exec",1,["noscript","loading","stale","skip_monitor","skip_slowlog"],0,0,0,["@slow","@transaction"]],["zrevrangebylex",-4,["readonly"],1,1,1,["@read","@sortedset","@slow"]],["dbsize",1,["readonly","fast"],0,0,0,["@keyspace","@read","@fast"]]] const generateConnectInfo = async (options) => { const {socket, redis, payload} = options const { db} = payload // console.warn('generateConnectInfo', options.payload) let databases //let results let commands = staticCommands const probeDatabaseCount = async() => { let tryUntilSelectDatabaseIsNotOk = true let currentDb = 0 let totalDb = 0 let maxDb = 512 while(tryUntilSelectDatabaseIsNotOk) { try { currentDb++ await redis.call('select', currentDb) //console.info('found correct database index', currentDb) if (currentDb > maxDb) { console.warn(`limiting to max ${maxDb} database index, as it could crash with a big db index number`) tryUntilSelectDatabaseIsNotOk = false } } catch(e) { console.warn('found wrong current db index', currentDb) tryUntilSelectDatabaseIsNotOk = false } } totalDb = currentDb if (db <= totalDb) { await redis.call('select', db) } console.log('calculated max databases index', totalDb) return totalDb } if (options.payload.connection.cluster === true) { databases = 1 //commands = await redis.command() } else { try { databases = (await redis.config('get', 'databases'))[1] console.info(options.payload.connection.name, 'instance successfully works the database listing') } catch(e) { console.warn(options.payload.connection.name, 'instance get databases listing is disabled', e) databases = await probeDatabaseCount() } } console.info(options.payload.connection.name, 'databases got', databases) try { //commands = await redis.call('command2') commands = await redis.command() console.info(options.payload.connection.name, 'instance command listing is available') } catch(e) { console.warn(options,payload.connection.name, 'instance command listing is not available, not all redis instances are not available command listing', e) } //console.log(JSON.stringify(commands)) //socket.p3xrs.commands = commands.map(e => e[0].toLowerCase()) //console.log('payload', payload) await sharedIoRedis.getFullInfoAndSendSocket({ setDb: true, redis: redis, responseEvent: options.responseEvent, socket: socket, extend: { databases: databases, commands: commands }, payload: payload, }) } module.exports = async (options) => { const {socket, payload} = options; const {connection, db} = payload try { if (!p3xrs.cfg.donated) { if (payload.connection.cluster === true) { throw donationWareFeatureError } } if (socket.p3xrs.connectionId !== connection.id) { sharedIoRedis.disconnectRedis({ socket: socket, }) } if (!p3xrs.redisConnections.hasOwnProperty(connection.id)) { p3xrs.redisConnections[connection.id] = { connection: connection, clients: [] } } if (!p3xrs.redisConnections[connection.id].clients.includes(socket.id)) { console.info(consolePrefix, 'added new socket.id', socket.id, 'to', connection.id, 'name with', connection.name) p3xrs.redisConnections[connection.id].clients.push(socket.id) } if (socket.p3xrs.ioredis !== undefined) { console.info(consolePrefix, 'redis was already connected') socket.p3xrs.connectionId = connection.id await generateConnectInfo({ redis: socket.p3xrs.ioredis, socket: socket, responseEvent: options.responseEvent, payload: payload }) sharedIoRedis.sendStatus({ socket: socket, }) } else { const actualConnection = p3xrs.connections.list.find(con => options.payload.connection.id === con.id) if (actualConnection === undefined) { throw new Error('auto-connection-failed') } let redisConfig = Object.assign({}, actualConnection); delete redisConfig.name delete redisConfig.id redisConfig.retryStrategy = () => { return false } /* redisConfig.showFriendlyErrorStack = true if (db !== undefined) { redisConfig.db = db } */ if (redisConfig.cluster === true) { redisConfig = [redisConfig].concat(actualConnection.nodes) } if (redisConfig.tlsWithoutCert) { redisConfig.tls = { } } else if (typeof redisConfig.tlsCa === 'string' && redisConfig.tlsCa.trim() !== '') { redisConfig.tls = { //rejectUnauthorized: false, cert: redisConfig.tlsCrt, key: redisConfig.tlsKey, ca: redisConfig.tlsCa, } } if ((typeof redisConfig.tlsCa === 'string' && redisConfig.tlsCa.trim() !== '') || redisConfig.tlsWithoutCert) { redisConfig.tls.rejectUnauthorized = redisConfig.tlsRejectUnauthorized === undefined ? false : redisConfig.tlsRejectUnauthorized } let redis = new Redis(redisConfig) let redisSubscriber = new Redis(redisConfig) // let redis = await new Redis(redisConfig, {autoDetectCluster: true}) // let redisSubscriber = await new Redis(redisConfig, {autoDetectCluster: true}) socket.p3xrs.connectionId = connection.id socket.p3xrs.readonly = actualConnection.readonly === true socket.p3xrs.ioredis = redis socket.p3xrs.ioredisSubscriber = redisSubscriber let didConnected = false const redisErrorFun = async function (error) { const consolePrefix = 'socket.io connection-connect redis error fun' console.warn(consolePrefix, connection.id, connection.name, 'error') console.error(error) console.warn(consolePrefix, 'didConnected', didConnected) if (!didConnected) { socket.emit(options.responseEvent, { status: 'error', error: error.message }) } const disconnectedData = { connectionId: socket.p3xrs.connectionId, error: error.message, status: 'error', } console.warn(consolePrefix, 'disconnectedData', disconnectedData) socket.p3xrs.io.emit('redis-disconnected', disconnectedData) try { await sharedIoRedis.disconnectRedis({ socket: socket, }) } catch (e) { console.warn(consolePrefix, 'disconnectRedis') console.error(e) } delete p3xrs.redisConnections[socket.connectionId] socket.p3xrs.connectionId = undefined socket.p3xrs.ioredis = undefined socket.p3xrs.ioredisSubscriber = undefined sharedIoRedis.sendStatus({ socket: socket, }) } redis.on('error', redisErrorFun) redisSubscriber.on('error', redisErrorFun) //console.warn('create psubscribe', actualConnection.id) redisSubscriber.psubscribe('*', function (error, count) { if (error) { console.error(error) } }) //console.warn('create pmessage', actualConnection.id) redisSubscriber.on('pmessage', function (channel, pattern, message) { //console.log(`receive pmessage channel: ${channel} - pattern: ${pattern}, message: ${message}`); //console.log('list clients', actualConnection.id, JSON.stringify(p3xrs.redisConnections[actualConnection.id].clients, null, 4)) socket.emit('pubsub-message', { channel: pattern, message: message, }) }); redis.on('connect', async function () { try { console.info(consolePrefix, options.payload.connection.id, options.payload.connection.name, 'connected') didConnected = true await generateConnectInfo({ redis: redis, socket: socket, responseEvent: options.responseEvent, payload: options.payload, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } finally { sharedIoRedis.sendStatus({ socket: socket, }) } }) } } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/connection-delete.js000066400000000000000000000030161520154223400226260ustar00rootroot00000000000000const sharedIoRedis = require('../shared') module.exports = async (options) => { const {socket} = options; const connectionSaveId = options.payload.id; let connectionIndexExisting; let disableReadonlyConnections = true try { sharedIoRedis.ensureReadonlyConnections() disableReadonlyConnections = false for (let connectionIndex in p3xrs.connections.list) { const connection = p3xrs.connections.list[connectionIndex] if (connection.id === connectionSaveId) { connectionIndexExisting = connectionIndex break; } } if (connectionIndexExisting !== undefined) { p3xrs.connections.list.splice(connectionIndexExisting, 1) p3xrs.connections.update = new Date() const fs = require('fs') fs.writeFileSync(p3xrs.cfg.connections.home, JSON.stringify(p3xrs.connections, null, 4)) } socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } finally { if (!disableReadonlyConnections) { sharedIoRedis.sendConnections({ socket: socket, }) sharedIoRedis.triggerDisconnect({ connectionId: connectionSaveId, code: 'delete-connection', socket: socket, }) } } } src/service/socket.io/request/connection-disconnect.js000066400000000000000000000016201520154223400235140ustar00rootroot00000000000000const sharedIoRedis = require('../shared') const consolePrefix = 'socket.io connection disconnect' module.exports = async (options) => { const {socket, payload} = options; const {connectionId} = payload; console.warn(consolePrefix, 'connectionId', connectionId, 'socket.p3xrs.connectionId', socket.p3xrs.connectionId) try { if (socket.p3xrs.connectionId === connectionId) { console.warn(consolePrefix, 'will disconnect from redis') sharedIoRedis.disconnectRedis({ socket: socket, }) } socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } finally { sharedIoRedis.sendStatus({ socket: socket, }) } } src/service/socket.io/request/connection-save.js000066400000000000000000000056721520154223400223340ustar00rootroot00000000000000const sharedIoRedis = require('../shared') module.exports = async (options) => { const {socket} = options; const connectionSave = options.payload.model; let disableReadonlyConnections = true try { sharedIoRedis.ensureReadonlyConnections() disableReadonlyConnections = false let connectionIndexExisting; for (let connectionIndex in p3xrs.connections.list) { const connection = p3xrs.connections.list[connectionIndex] if (connection.id === connectionSave.id) { connectionIndexExisting = connectionIndex break; } } p3xrs.connections.update = new Date() if (connectionIndexExisting !== undefined) { if (p3xrs.connections.list[connectionIndexExisting].id === connectionSave.password) { connectionSave.password = p3xrs.connections.list[connectionIndexExisting].password; } if (p3xrs.connections.list[connectionIndexExisting].id === connectionSave.tlsCrt) { connectionSave.tlsCrt = p3xrs.connections.list[connectionIndexExisting].tlsCrt; } if (p3xrs.connections.list[connectionIndexExisting].id === connectionSave.tlsKey) { connectionSave.tlsKey = p3xrs.connections.list[connectionIndexExisting].tlsKey; } if (p3xrs.connections.list[connectionIndexExisting].id === connectionSave.tlsCa) { connectionSave.tlsCa = p3xrs.connections.list[connectionIndexExisting].tlsCa; } //TODO fix secured nodes password if (Array.isArray(connectionSave.nodes)) { for (let node of connectionSave.nodes) { const findNode = p3xrs.connections.list[connectionIndexExisting].nodes.find((findNode) => { return findNode.id === node.id && node.password === findNode.id }) if (findNode !== undefined) { node.password = findNode.password } } } p3xrs.connections.list[connectionIndexExisting] = connectionSave } else { p3xrs.connections.list.push(connectionSave) } const fs = require('fs') fs.writeFileSync(p3xrs.cfg.connections.home, JSON.stringify(p3xrs.connections, null, 4)) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } finally { if (!disableReadonlyConnections) { sharedIoRedis.sendConnections({ socket: socket, }) sharedIoRedis.triggerDisconnect({ connectionId: connectionSave.id, code: 'save-connection', socket: socket, }) } } } src/service/socket.io/request/console.js000066400000000000000000000041111520154223400206660ustar00rootroot00000000000000const sharedIoRedis = require('../shared') const parser = sharedIoRedis.argumentParser const disabledCommands = ['monitor'] const consolePrefix = 'socket.io console call' module.exports = async (options) => { const {socket, payload} = options; const {command} = payload try { let redis = socket.p3xrs.ioredis const commands = parser( command); let mainCommand = commands.shift() mainCommand = mainCommand.toLowerCase(); if (disabledCommands.includes(mainCommand)) { throw new Error('invalid_console_command') } if (mainCommand !== 'select') { sharedIoRedis.ensureReadonlyConnection({ socket }) } console.info(consolePrefix, mainCommand, commands) /* if (!socket.p3xrs.commands.includes(mainCommand)) { throw new Error(`ERR Unknown command '${mainCommand}'.`) } */ let result = await redis.call(mainCommand, commands) const defaultEmit = {} let generatedCommand = mainCommand if (commands.length > 0) { generatedCommand += ' ' + commands.join(' ') } switch (mainCommand) { case 'select': defaultEmit.database = parseInt(commands[0]) break; } /* switch (generatedCommand) { case 'client list': //result = result.split(' ') break; } */ //console.warn(consolePrefix, typeof result, result) /* try { const clone = JSON.parse(JSON.stringify(result)) console.warn(consolePrefix, typeof clone, clone) } catch(e) { console.warn(e) } */ socket.emit(options.responseEvent, Object.assign(defaultEmit, { status: 'ok', result: result, generatedCommand: generatedCommand, })) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/delete.js000066400000000000000000000015071520154223400204740ustar00rootroot00000000000000const consolePrefix = 'socket.io del key' const sharedIoRedis = require('../shared') module.exports = async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) let redis = socket.p3xrs.ioredis console.info(consolePrefix, payload.key) await redis.del(payload.key) socket.emit(options.responseEvent, { status: 'ok', }) /* await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, payload: payload, }) */ } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/expire.js000066400000000000000000000012171520154223400205240ustar00rootroot00000000000000const sharedIoRedis = require('../shared') const consolePrefix = 'socket.io expire' module.exports = async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) let redis = socket.p3xrs.ioredis console.info(consolePrefix, payload.key, payload.ttl) await redis.expire(payload.key, parseInt(payload.ttl)) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/key-del-tree.js000066400000000000000000000023771520154223400215270ustar00rootroot00000000000000const consolePrefix = 'socket.io key del tree' const sharedIoRedis = require('../shared') module.exports = async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) let redis = socket.p3xrs.ioredis const deleteTree = `${payload.key}${payload.redisTreeDivider}*`; console.info(consolePrefix, deleteTree) const keys = await sharedIoRedis.getStreamKeys({ redis: redis, match: deleteTree, maxKeys: payload.maxKeys, }) const pipelineDeleteTree = redis.pipeline() for (let key of keys) { console.info(consolePrefix, 'delete key ', key) pipelineDeleteTree.del(key) } await pipelineDeleteTree.exec(); socket.emit(options.responseEvent, { status: 'ok', }) /* await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, payload: payload, }) */ } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/key-get.js000066400000000000000000000045721520154223400206040ustar00rootroot00000000000000const consolePrefix = 'socket.io key get full' module.exports = async (options) => { const {socket, payload} = options; try { let redis = socket.p3xrs.ioredis const key = payload.key; //const type = payload.type; const type = await redis.type(key) //console.info(consolePrefix, payload, type, key) const viewPipeline = redis.pipeline() switch (type) { case 'string': viewPipeline.get(key) break; case 'list': viewPipeline.lrange(key, 0, -1) break; case 'hash': viewPipeline.hgetall(key) break; case 'set': viewPipeline.smembers(key) break; case 'zset': viewPipeline.zrange(key, 0, -1, 'WITHSCORES') break; case 'stream': viewPipeline.xrange(key, '-', '+') break; } viewPipeline.ttl(key) viewPipeline.object('encoding', key) switch (type) { case 'stream': viewPipeline.xlen(key) break; case 'hash': viewPipeline.hlen(key) break; case 'list': viewPipeline.llen(key) break; case 'set': viewPipeline.scard(key) break; case 'zset': viewPipeline.zcard(key) break; } const viewPipelineResult = await viewPipeline.exec() // console.log(viewPipelineResult) const value = viewPipelineResult[0][1] const ttl = viewPipelineResult[1][1] const encoding = viewPipelineResult[2][1] let length if (type !== 'string') { length = viewPipelineResult[3][1] } const socketResult = { length: length, key: key, status: 'ok', type: type, value: value, ttl: ttl, encoding: encoding, }; // console.warn('socketResult', socketResult) socket.emit(options.responseEvent, socketResult) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/key-hash-delete-field.js000066400000000000000000000012231520154223400232570ustar00rootroot00000000000000const sharedIoRedis = require('../shared') const consolePrefix = 'socket.io key hash delete key' const utils = require('corifeus-utils') module.exports = async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const {hashKey, key} = payload; await redis.hdel(key, hashKey) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/key-list-delete-index.js000066400000000000000000000015001520154223400233310ustar00rootroot00000000000000const sharedIoRedis = require('../shared') const consolePrefix = 'socket.io key list delete index' const utils = require('corifeus-utils') module.exports = async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const {index, key} = payload; const uniqueValue = utils.random.complexUuid() console.log(consolePrefix, key, index, uniqueValue) await redis.lset(key, index, uniqueValue) await redis.lrem(key, 1, uniqueValue) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/key-new-or-set.js000066400000000000000000000062251520154223400220220ustar00rootroot00000000000000const sharedIoRedis = require('../shared') const consolePrefix = 'socket.io key new' module.exports = async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const {model} = payload; model.score = model.score === null ? undefined : model.score model.index = model.index === null ? undefined : model.index model.hashKey = model.hashKey === null ? undefined : model.hashKey //console.warn(consolePrefix, payload) switch (model.type) { case 'stream': const arguments = [ model.key, model.streamTimestamp, ].concat(sharedIoRedis.argumentParser(model.value)) await redis.xadd(...arguments) break; case 'string': await redis.set(model.key, model.value) break; case 'list': if (model.index === undefined) { await redis.rpush(model.key, model.value) } else { if (model.index === -1) { await redis.lpush(model.key, model.value) } else { const size = await redis.llen(model.key); if (model.index > -1 && model.index < size) { await redis.lset(model.key, model.index, model.value) } else { const listOutOBoundsError = new Error('list-out-of-bounds') listOutOBoundsError.code = 'list-out-of-bounds' throw listOutOBoundsError } } } break; case 'hash': if (payload.hasOwnProperty('originalHashKey')) { await redis.hdel(model.key, payload.originalHashKey) } await redis.hset(model.key, model.hashKey, model.value) break; case 'set': if (payload.hasOwnProperty('originalValue')) { await redis.srem(model.key, payload.originalValue) } await redis.sadd(model.key, model.value) break; case 'zset': if (payload.hasOwnProperty('originalValue')) { await redis.zrem(model.key, payload.originalValue) } await redis.zadd(model.key, model.score, model.value) break; } socket.emit(options.responseEvent, { status: 'ok', key: model.key, }) /* await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, extend: { key: model.key }, payload: payload, }) */ } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/key-set-delete-member.js000066400000000000000000000012211520154223400233110ustar00rootroot00000000000000const sharedIoRedis = require('../shared') const consolePrefix = 'socket.io key list delete index' const utils = require('corifeus-utils') module.exports = async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const {key, value} = payload; await redis.srem(key, value) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/key-set.js000066400000000000000000000012351520154223400206110ustar00rootroot00000000000000const sharedIoRedis = require('../shared') module.exports = async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const ttl = await redis.ttl(payload.key) await redis.set(payload.key, payload.value) if (ttl !== -1) { await redis.expire(payload.key, ttl) } socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/key-stream-delete-timestamp.js000066400000000000000000000012521520154223400245510ustar00rootroot00000000000000const sharedIoRedis = require('../shared') const consolePrefix = 'socket.io stream delete timestamp id' const utils = require('corifeus-utils') module.exports = async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const {key, streamTimestamp} = payload; await redis.xdel(key, streamTimestamp) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/key-zset-delete-member.js000066400000000000000000000012251520154223400235070ustar00rootroot00000000000000const sharedIoRedis = require('../shared') const consolePrefix = 'socket.io key zsit delete member' module.exports = async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const {key, value} = payload; console.log(consolePrefix, payload) await redis.zrem(key, value) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/persist.js000066400000000000000000000010701520154223400207160ustar00rootroot00000000000000const sharedIoRedis = require('../shared') const consolePrefix = 'socket.io persists' module.exports = async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) let redis = socket.p3xrs.ioredis await redis.persist(payload.key) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/redis-test-connection.js000066400000000000000000000060731520154223400234550ustar00rootroot00000000000000const Redis = require('../../../lib/ioredis-cluster') module.exports = async (options) => { const {socket} = options; try { let redisConfig = options.payload.model; const actualConnection = p3xrs.connections.list.find(con => redisConfig.id === con.id) if (actualConnection !== undefined) { if (redisConfig.password === actualConnection.id) { redisConfig.password = actualConnection.password; } if (redisConfig.tlsCrt === actualConnection.id) { redisConfig.tlsCrt = actualConnection.tlsCrt; } if (redisConfig.tlsKey === actualConnection.id) { redisConfig.tlsKey = actualConnection.tlsKey; } if (redisConfig.tlsCa === actualConnection.id) { redisConfig.tlsCa = actualConnection.tlsCa; } } //TODO fix secured nodes password delete redisConfig.name delete redisConfig.id if (redisConfig.cluster === true) { redisConfig.nodes = redisConfig.nodes.map((node) => { if (node.password === node.id) { const foundNode = actualConnection.nodes.find((findNode) => findNode.id === node.password) node.password = foundNode.password } return node }) redisConfig = [redisConfig].concat(redisConfig.nodes) } if (redisConfig.tlsWithoutCert) { redisConfig.tls = { } } else if (typeof redisConfig.tlsCa === 'string' && redisConfig.tlsCa.trim() !== '') { redisConfig.tls = { //rejectUnauthorized: false, cert: redisConfig.tlsCrt, key: redisConfig.tlsKey, ca: redisConfig.tlsCa, } } if ((typeof redisConfig.tlsCa === 'string' && redisConfig.tlsCa.trim() !== '') || redisConfig.tlsWithoutCert) { redisConfig.tls.rejectUnauthorized = redisConfig.tlsRejectUnauthorized === undefined ? false : redisConfig.tlsRejectUnauthorized } let redis = new Redis(redisConfig) redis.on('error', function (error) { console.error(error) socket.emit(options.responseEvent, { status: 'error', error: error.message }) redis.disconnect() }) redis.on('connect', async function () { try { //await redis.call('client', 'list') socket.emit(options.responseEvent, { status: 'ok', }) } catch (error) { console.error(error) socket.emit(options.responseEvent, { status: 'error', error: error.message }) } finally { redis.disconnect() } }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/refresh.js000066400000000000000000000011201520154223400206570ustar00rootroot00000000000000const sharedIoRedis = require('../shared') //const consolePrefix = 'socket.io refresh redis' module.exports = async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, payload: payload, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/rename.js000066400000000000000000000015371520154223400205040ustar00rootroot00000000000000const consolePrefix = 'socket.io rename key' const sharedIoRedis = require('../shared') module.exports = async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) let redis = socket.p3xrs.ioredis console.info(consolePrefix, payload.key) await redis.rename(payload.key, payload.keyNew) /* await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, payload: payload, }) */ socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/save.js000066400000000000000000000010321520154223400201610ustar00rootroot00000000000000const sharedIoRedis = require('../shared') module.exports = async (options) => { const {socket} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) await redis.save() socket.emit(options.responseEvent, { status: 'ok', info: await redis.info(), }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/set-language.js000066400000000000000000000007571520154223400216140ustar00rootroot00000000000000module.exports = async (options) => { const { socket, payload } = options; try { if (global.p3xre) { global.p3xre.setLanguage({ key: payload.key }) } socket.emit(options.responseEvent, { status: 'ok', key: payload.key, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/set-license.js000066400000000000000000000002761520154223400214470ustar00rootroot00000000000000const utils = require('corifeus-utils') const checkLicense = require('../../../lib/check-license') module.exports = async (options) => { options.save = true checkLicense(options) } src/service/socket.io/request/trigger-redis-disconnect.js000066400000000000000000000011351520154223400241250ustar00rootroot00000000000000const sharedIoRedis = require('../shared') const consolePrefix = 'socket.io trigger redis disconnect' module.exports = async (options) => { const {socket} = options; try { console.warn(consolePrefix, 'socket.p3xrs.connectionId', socket.p3xrs.connectionId) sharedIoRedis.disconnectRedis({ socket: socket, }) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/shared.js000066400000000000000000000314121520154223400170060ustar00rootroot00000000000000const triggerDisconnect = (options) => { const {connectionId, code, socket} = options if (p3xrs.redisConnections.hasOwnProperty(connectionId)) { delete p3xrs.redisConnections[connectionId] socket.p3xrs.io.emit('redis-disconnected', { connectionId: connectionId, status: 'code', code: code }) sendStatus({ socket: socket }) } } const sendStatus = (options) => { const {socket} = options const redisConnections = {} Object.keys(p3xrs.redisConnections).forEach((redisConnectionKey) => { redisConnections[redisConnectionKey] = {} Object.keys(p3xrs.redisConnections[redisConnectionKey]).forEach(redisConnectionKey2 => { redisConnections[redisConnectionKey][redisConnectionKey2] = p3xrs.redisConnections[redisConnectionKey][redisConnectionKey2] }) }) socket.p3xrs.io.emit('redis-status', { redisConnections: redisConnections, }) } const consolePrefixDisconnectRedis = 'socket.io shared disconnect redis' const disconnectRedis = (options) => { const {socket} = options //console.warn(consolePrefixDisconnectRedis, `${socket.p3xrs.connectionId} !== ${connection.id}`) if (p3xrs.redisConnections.hasOwnProperty(socket.p3xrs.connectionId)) { console.warn(consolePrefixDisconnectRedis, `includes ${p3xrs.redisConnections[socket.p3xrs.connectionId].clients.includes(socket.id)} length === 1 ${p3xrs.redisConnections[socket.p3xrs.connectionId].clients.length}`) if (p3xrs.redisConnections[socket.p3xrs.connectionId].clients.includes(socket.id) && p3xrs.redisConnections[socket.p3xrs.connectionId].clients.length === 1) { //console.warn(consolePrefixDisconnectRedis, p3xrs.redisConnections[socket.p3xrs.connectionId]) //p3xrs.redisConnections[socket.p3xrs.connectionId].ioredis.disconnect() delete p3xrs.redisConnections[socket.p3xrs.connectionId] } else { let connectionIndexExisting = p3xrs.redisConnections[socket.p3xrs.connectionId].clients.indexOf(socket.id); console.warn(consolePrefixDisconnectRedis, socket.p3xrs.connectionId, p3xrs.redisConnections[socket.p3xrs.connectionId].clients, socket.id, connectionIndexExisting) if (connectionIndexExisting > -1) { p3xrs.redisConnections[socket.p3xrs.connectionId].clients.splice(connectionIndexExisting, 1) } } } if (p3xrs.redisConnections.hasOwnProperty(socket.p3xrs.connectionId) && p3xrs.redisConnections[socket.p3xrs.connectionId].hasOwnProperty('clients') && p3xrs.redisConnections[socket.p3xrs.connectionId].clients.length === 0) { delete p3xrs.redisConnections[socket.p3xrs.connectionId] } module.exports.disconnectRedisIo(options) socket.p3xrs.connectionId = undefined } const cloneDeep = require('lodash/cloneDeep') const sendConnections = (options) => { const {socket} = options const connections = cloneDeep(p3xrs.connections); let connectionsList = connections.list.map(connection => { delete connection.password delete connection.tlsCrt delete connection.tlsKey delete connection.tlsCa //TODO fix secured nodes password if (Array.isArray(connection.nodes)) { connection.nodes = connection.nodes.map(node => { delete node.password return node }) } return connection }) connections.list = connectionsList socket.p3xrs.io.emit('connections', { status: 'ok', connections: connections }) } const disconnectRedisIo = (options) => { const {socket} = options console.warn('shared disconnectRedisIo', 'try') if (socket.p3xrs.ioredis !== undefined) { console.warn('shared disconnectRedisIo', 'executed') socket.p3xrs.ioredis.disconnect() socket.p3xrs.ioredisSubscriber.disconnect() socket.p3xrs.ioredis = undefined socket.p3xrs.ioredisSubscriber = undefined } } const getStreamKeys = (options) => { const {redis, } = options let {dbsize, maxKeys} = options return new Promise(async (resolve, reject) => { try { /* if (dbsize === undefined) { dbsize = await redis.dbsize() } */ if (isNaN(maxKeys) || maxKeys < 5 || maxKeys > 100000) { maxKeys = 10000 } //console.warn('check if received max keys', maxKeys, typeof maxKeys, !isNaN(maxKeys), maxKeys < 5, maxKeys > 100000) /* let count = 100 if (dbsize > 110000) { count = 10000 } else if (dbsize > 11000) { count = 1000 } */ let count = Math.round(maxKeys / 10) if (count < 5) { count = 5 } //console.warn('socket.io getStreamKeys dbsize', dbsize, 'count', count, 'maxKeys', maxKeys) const stream = redis.scanStream({ match: options.match, count: count }); let keys = []; let ended = false stream.on('data', (resultKeys) => { /* keys = keys.concat(resultKeys); if (maxKeys && keys.length >= maxKeys && !ended) { ended = true console.warn('reached max key count', maxKeys, 'found', keys.length, 'keys our of unknown total') //stream.pause() //stream.destroy() stream.emit('end') } */ if (maxKeys && keys.length < maxKeys) { keys = keys.concat(resultKeys); if (keys.length >= maxKeys) { ended = true resolve(keys) //stream.emit('end') } } else if (!ended) { ended = true resolve(keys) } }); stream.on('end', () => { if (ended) { return } resolve(keys); }); /* stream.on('error', (error) => { console.error('getStreamKeys stream', error) reject(error) }) */ } catch (e) { reject(e) } }) } /* const getStreamTypedKeys = (options) => { const { redis, key, match } = options let { scan } = options if (scan === undefined) { scan = 'scanStream' } return new Promise((resolve, reject) => { let stream; if (scan === 'scanStream') { stream = redis[scan]({ match: match }); } else { stream = redis[scan](key, { match: match }); } let keys = []; stream.on('data', (resultKeys) => { keys = keys.concat(resultKeys); }); stream.on('end', async () => { try { resolve(keys); } catch (e) { console.error(e); reject(e) } }); }) } */ const getKeysInfo = async (options) => { const {redis, keys} = options; const keyTypePipeline = redis.pipeline() // const promises = []; for (let key of keys) { keyTypePipeline.type(key) // promises.push(redis.type(key)) } // const keysType = await Promise.all(promises); const keysType = await keyTypePipeline.exec(); const result = {} const complexLengthPipeline = redis.pipeline() for (let keysIndex in keys) { const keyType = keysType[keysIndex] const key = keys[keysIndex] const obj = { type: keyType[1] } switch (obj.type) { case 'stream': complexLengthPipeline.xlen(key) break; case 'hash': complexLengthPipeline.hlen(key) break; case 'list': complexLengthPipeline.llen(key) break; case 'set': complexLengthPipeline.scard(key) break; case 'zset': complexLengthPipeline.zcard(key) break; } result[key] = obj } const lengthsPipeline = await complexLengthPipeline.exec() for (let keysIndex in keys) { const key = keys[keysIndex] const obj = result[key] if (obj.type === 'string' || obj.type === 'none') { continue } const lengthPipelineElement = lengthsPipeline.shift() if (lengthPipelineElement === undefined) { continue } obj.length = lengthPipelineElement[1] } return result; } const ensureReadonlyConnections = () => { if (p3xrs.cfg.readonlyConnections === true) { const errorCode = new Error('readonly-connection-mode') throw errorCode; } } const ensureReadonlyConnection = ({ socket }) => { if (socket.p3xrs.readonly === true) { const errorCode = new Error('readonly-connection-mode') throw errorCode; } } const getFullInfo = async (options) => { const {redis} = options; let {payload} = options if (payload === undefined) { payload = {} } const dbsize = await redis.dbsize() const results = await Promise.all([ redis.info(), getStreamKeys({ dbsize: dbsize, redis: redis, match: payload.match, maxKeys: payload.maxKeys, }), redis.pubsub('channels', '*'), // redis.infoObject(), ]) const keys = results[1] let keysInfo = {} if (keys.length < 110000) { keysInfo = await getKeysInfo({ redis: redis, keys: keys, }) } // const keysInfo = [] const result = { info: results[0], // infoObject: results[3], keys: keys, keysInfo: keysInfo, dbsize: dbsize, channels: results[2] } //console.log('get full info', result) return result } const getFullInfoAndSendSocket = async (options) => { const {redis, socket, payload, setDb} = options if (setDb === true) { try { await redis.call('select', payload.db || 0) } catch(e) { console.warn(e) } } const result = await getFullInfo({ redis: redis, payload: payload, }) let {extend} = options if (extend === undefined) { extend = {} } socket.emit(options.responseEvent, Object.assign(extend, { status: 'ok', info: result.info, // infoObject: result.infoObject, keys: result.keys, keysInfo: result.keysInfo, dbsize: result.dbsize, })) } const argumentParser = (input, sep, keepQuotes) => { const separator = sep || /\s/g; let singleQuoteOpen = false; let doubleQuoteOpen = false; let tokenBuffer = []; const ret = []; console.log('argumentParser input', input) const arr = input.split(''); for (let i = 0; i < arr.length; ++i) { let element = arr[i]; let matches = element.match(separator); if (element === "'" && !doubleQuoteOpen) { if (keepQuotes === true) { tokenBuffer.push(element); } singleQuoteOpen = !singleQuoteOpen; continue; } else if (element === '"' && !singleQuoteOpen) { if (keepQuotes === true) { tokenBuffer.push(element); } doubleQuoteOpen = !doubleQuoteOpen; continue; } if (!singleQuoteOpen && !doubleQuoteOpen && matches) { if (tokenBuffer.length > 0) { ret.push(tokenBuffer.join('')); tokenBuffer = []; } else if (!!sep) { ret.push(element); } } else { tokenBuffer.push(element); } } if (tokenBuffer.length > 0) { ret.push(tokenBuffer.join('')); } else if (!!sep) { ret.push(''); } return ret; } module.exports.argumentParser = argumentParser module.exports.ensureReadonlyConnections = ensureReadonlyConnections module.exports.triggerDisconnect = triggerDisconnect module.exports.getStreamKeys = getStreamKeys module.exports.disconnectRedisIo = disconnectRedisIo module.exports.sendConnections = sendConnections module.exports.sendStatus = sendStatus module.exports.disconnectRedis = disconnectRedis module.exports.getKeysInfo = getKeysInfo module.exports.getFullInfo = getFullInfo module.exports.getFullInfoAndSendSocket = getFullInfoAndSendSocket module.exports.ensureReadonlyConnection = ensureReadonlyConnection src/service/socket.io/socket.js000066400000000000000000000065221520154223400170340ustar00rootroot00000000000000const socketIoShared = require('./shared') const originalPkg = require('../../../package.json') let pkg = originalPkg try { pkg = require('../../../../../package.json') if (pkg.name !== 'p3x-redis-ui') { console.warn('cannot find p3x-redis-ui version, but it is not required, found', pkg.name) pkg = originalPkg } } catch(e) { console.warn('cannot find p3x-redis-ui version, but it is not required', e) } module.exports = (io) => { io.on('connect', function (socket) { //const token = socket.handshake.query.token; socket.p3xrs = { address: socket.handshake.headers.origin, connectedAt: new Date(), connectionId: undefined, io: io, ioredis: undefined, ioredisSubscriber: undefined, readonly: undefined, // commands: undefined, } console.info(`socket.io connected ${socket.id}`); socket.emit('info-interval', { status: 'ok', donated: p3xrs.cfg.donated, }) socket.on('disconnect', function () { console.warn('socket.p3xrs.connectionId', socket.p3xrs.connectionId) if (socket.p3xrs.connectionId !== undefined) { const connectionId = socket.p3xrs.connectionId; if (p3xrs.redisConnections.hasOwnProperty(connectionId)) { const redisConnectionIndex = p3xrs.redisConnections[connectionId].clients.indexOf(socket.id); if (redisConnectionIndex !== -1) { p3xrs.redisConnections[connectionId].clients.splice(redisConnectionIndex, 1); } if (p3xrs.redisConnections[connectionId].clients.length === 0) { delete p3xrs.redisConnections[connectionId] } socketIoShared.disconnectRedisIo({ socket: socket, }) } } // Call on disconnect. console.info('socket.io disconnected %s', socket.id); socketIoShared.sendStatus({ socket: socket, }) }); socket.on('p3xr-request', (options) => { options.socket = socket; options.responseEvent = `p3xr-response-${options.requestId}` if (options && options.action && typeof options.action === 'string' && !options.action.includes('\\') && !options.action.includes('/')) { require(`./request/${options.action}`)(options) } else { console.warn('trying bad action socket.on p3xr-request with options', options) } }) let dividers = [ ":", "/", "|", "-", "@" ] if (p3xrs.cfg.hasOwnProperty('treeDividers') && Array.isArray(p3xrs.cfg.treeDividers)) { dividers = p3xrs.cfg.treeDividers } socket.emit('configuration', { readonlyConnections: p3xrs.cfg.readonlyConnections === true, snapshot: pkg.name !== 'p3x-redis-ui', treeDividers: dividers, version: pkg.version }) socketIoShared.sendStatus({ socket: socket, }) socketIoShared.sendConnections({ socket: socket, }) }); }