.gitignore000066400000000000000000000003521520006216500130430ustar00rootroot00000000000000/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.npmignore000066400000000000000000000002151520006216500130500ustar00rootroot00000000000000/.idea /build /test /node_modules /*.iml /*.ipr /*.iws /.travis.yml /.scrutinizer.yml /Gruntfile.js /*.lock *.log /corifeus-boot.json /secure.travis.yml000066400000000000000000000014301520006216500131620ustar00rootroot00000000000000language: node_js node_js: - node 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.js000066400000000000000000000004471520006216500133550ustar00rootroot00000000000000const 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); } LICENSE000066400000000000000000000024401520006216500120600ustar00rootroot00000000000000 @license p3x-redis-ui-server v2019.2.8-2 🏍️ The p3x-redis-ui-server package motor that is connected to the p3x-redis-ui-material web user interface. https://pages.corifeus.com/redis-ui-server Copyright (c) 2019 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.md000066400000000000000000000073071520006216500123410ustar00rootroot00000000000000# 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://pages.corifeus.com/redis-ui [//]: #@corifeus-header [![Corifeus @ Facebook](https://img.shields.io/badge/Facebook-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [![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) [![Build Status](https://api.travis-ci.com/patrikx3/redis-ui-server.svg?branch=master)](https://travis-ci.com/patrikx3/redis-ui-server) [![Uptime Robot ratio (30 days)](https://img.shields.io/uptimerobot/ratio/m780749701-41bcade28c1ea8154eda7cca.svg)](https://uptimerobot.patrikx3.com/) --- # 🏍️ The p3x-redis-ui-server package motor that is connected to the p3x-redis-ui-material web user interface. v2019.2.10-1 🙏 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 mini 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. **Bugs are evident™ - MATRIX️** ### Node Version Requirement ``` >=10.13.0 ``` ### Built on Node ``` v11.9.0 ``` The ```async``` and ```await``` keywords are required. Install NodeJs: https://nodejs.org/en/download/package-manager/ # 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. # For development standalone 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 --- [**P3X-REDIS-UI-SERVER**](https://pages.corifeus.com/redis-ui-server) Build v2019.2.10-1 [![Like Corifeus @ Facebook](https://img.shields.io/badge/LIKE-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [![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) ## P3X Sponsors [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) [![NoSQLBooster](https://cdn.corifeus.com/assets/png/nosqlbooster-70x70.png)](https://www.nosqlbooster.com/) [The Smartest IDE for MongoDB](https://www.nosqlbooster.com) [//]: #@corifeus-footer:endartifacts/000077500000000000000000000000001520006216500130335ustar00rootroot00000000000000artifacts/boot/000077500000000000000000000000001520006216500137765ustar00rootroot00000000000000artifacts/boot/p3xrs.json000066400000000000000000000012731520006216500157530ustar00rootroot00000000000000{ "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" } }bin/000077500000000000000000000000001520006216500116235ustar00rootroot00000000000000bin/p3xrs.js000077500000000000000000000001031520006216500132350ustar00rootroot00000000000000#!/usr/bin/env node const boot = require('../src/lib/boot') boot() package.json000066400000000000000000000037511520006216500133470ustar00rootroot00000000000000{ "name": "p3x-redis-ui-server", "version": "2019.2.10-1", "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": "v11.9.0", "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": "^2019.2.8-1", "nodemon": "^1.18.10" }, "dependencies-saved": { "koa-body": "^4.0.4", "koa-router": "^7.4.0" }, "dependencies": { "chalk": "^2.4.2", "commander": "^2.19.0", "console-stamp": "^0.2.7", "corifeus-utils": "^2019.2.8-1", "ioredis": "^4.6.2", "koa": "^2.7.0", "koa-send": "^5.0.0", "koa-static": "^5.0.0", "lodash": "^4.17.11", "socket.io": "^2.2.0" }, "engines": { "node": ">=10.13.0" }, "homepage": "https://pages.corifeus.com/redis-ui-server" } redis-ui-server.iml000066400000000000000000000007441520006216500146100ustar00rootroot00000000000000 src/000077500000000000000000000000001520006216500116425ustar00rootroot00000000000000src/index.js000066400000000000000000000001251520006216500133050ustar00rootroot00000000000000module.exports = { lib: require('./lib'), services: require('./service'), } src/lib/000077500000000000000000000000001520006216500124105ustar00rootroot00000000000000src/lib/boot.js000066400000000000000000000011301520006216500137040ustar00rootroot00000000000000require('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 }) p3xrs.redisConnections = {} } module.exports = boot src/lib/cli.js000066400000000000000000000062411520006216500135200ustar00rootroot00000000000000const path = require('path') const fs = require('fs') const cli = () => { const pkg = require('../../package') if (!process.versions.hasOwnProperty('electron')) { 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') .parse(process.argv); if (!program.config) { program.config = path.resolve(path.dirname(require.main.filename) + path.sep + '..', `.${path.sep}p3xrs.json`) // program.outputHelp() // return false } const configPath = path.resolve(process.cwd(), program.config) //console.log(configPath) p3xrs.cfg = require(configPath).p3xrs if (program.readonlyConnections) { // console.warn(program.readonlyConnections) p3xrs.cfg.readonlyConnections = true //console.warn(p3xrs.cfg.readonlyConnections === true) } } else { p3xrs.cfg = { "http": { "port-info": "this is ommitted, it will be default 7843", "port": 7844 }, "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" } p3xrs.cfg.readonlyConnections = false } 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(); } p3xrs.cfg.connections['home'] = path.resolve(p3xrs.cfg.connections['home-dir'], '.p3xrs-conns.json') 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.js000066400000000000000000000017341520006216500155370ustar00rootroot00000000000000const chalk = require('chalk'); const consoleStamp = () => { // overriding the console should be after this!!! require('console-stamp')(console, { pattern: 'yyyy/mm/dd HH:MM:ss.l', datePrefix: '[P3XRS] ', dateSuffix: '', metadata: function () { return `[PID: ${(String(process.pid).padStart(6, 0))}]`; }, colors: { stamp: "yellow", label: function() { let color; switch(arguments[0]) { case '[ERROR]': color = chalk.bold.red break; case '[WARN]': color = chalk.bold.blue break; default: color = chalk.green; } return color(arguments[0]) }, metadata: chalk.black.bgGreenBright, }, }); } module.exports = consoleStamp src/lib/index.js000066400000000000000000000001721520006216500140550ustar00rootroot00000000000000module.exports = { boot: require('./boot'), cli: require('./cli'), consoleStamp: require('./console-stamp'), }src/service/000077500000000000000000000000001520006216500133025ustar00rootroot00000000000000src/service/index.js000066400000000000000000000000571520006216500147510ustar00rootroot00000000000000module.exports = { koa: require('./koa'), }src/service/koa/000077500000000000000000000000001520006216500140545ustar00rootroot00000000000000src/service/koa/index.js000066400000000000000000000062201520006216500155210ustar00rootroot00000000000000const 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); } } module.exports = koaServicesrc/service/socket.io/000077500000000000000000000000001520006216500152005ustar00rootroot00000000000000src/service/socket.io/index.js000066400000000000000000000006151520006216500166470ustar00rootroot00000000000000const 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); } } module.exports = socketIoService src/service/socket.io/request/000077500000000000000000000000001520006216500166705ustar00rootroot00000000000000src/service/socket.io/request/connection-connect.js000066400000000000000000000120121520006216500230100ustar00rootroot00000000000000const consolePrefix = 'socket.io connection-connect'; const Redis = require('ioredis') const sharedIoRedis = require('../shared') const generateConnectInfo = async (options) => { const { socket, redis } = options const results = await Promise.all([ redis.config('get', 'databases'), redis.command(), ]) const databases = results[0] const commands = results[1] await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, extend: { databases: parseInt(databases[1]), commands: commands } }) } module.exports = async(options) => { const { socket, payload } = options; const { connection, db } = payload try { if (socket.p3xrs.connectionId !== connection.id) { sharedIoRedis.disconnectRedis({ socket: socket, }) } if (!p3xrs.redisConnections.hasOwnProperty(connection.id)) { console.info(consolePrefix, 'creating new connection') 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 }) sharedIoRedis.sendStatus({ socket: socket, }) } else { const actualConnection = p3xrs.connections.list.find(con => options.payload.connection.id === con.id) const redisConfig = Object.assign({}, actualConnection); delete redisConfig.name delete redisConfig.id redisConfig.retryStrategy = () => { return false } if (db !== undefined) { redisConfig.db = db } let redis = new Redis(redisConfig) socket.p3xrs.connectionId = connection.id socket.p3xrs.ioredis = redis 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 }) } const disconnectedData = { connectionId: socket.p3xrs.connectionId, error: error, 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 sharedIoRedis.sendStatus({ socket: socket, }) } redis.on('error', redisErrorFun) 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 }) } catch(e) { socket.emit(options.responseEvent, { status: 'error', error: e, }) } finally { sharedIoRedis.sendStatus({ socket: socket, }) } }) } } catch (error) { console.error(error) socket.emit(options.responseEvent, { status: 'error', error: error }) } }src/service/socket.io/request/connection-delete.js000066400000000000000000000030171520006216500226260ustar00rootroot00000000000000const 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 (error) { console.log(error) socket.emit(options.responseEvent, { status: 'error', error: error }) } finally { if (!disableReadonlyConnections) { sharedIoRedis.sendConnections({ socket: socket, }) sharedIoRedis.triggerDisconnect({ connectionId: connectionSaveId, code: 'delete-connection', socket: socket, }) } } }src/service/socket.io/request/connection-disconnect.js000066400000000000000000000015621520006216500235200ustar00rootroot00000000000000const 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) { socket.emit(options.responseEvent, { status: 'error', error: error }) } finally { sharedIoRedis.sendStatus({ socket: socket, }) } }src/service/socket.io/request/connection-save.js000066400000000000000000000034521520006216500223250ustar00rootroot00000000000000const 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; } 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 }) } finally { if (!disableReadonlyConnections) { sharedIoRedis.sendConnections({ socket: socket, }) sharedIoRedis.triggerDisconnect({ connectionId: connectionSave.id, code: 'save-connection', socket: socket, }) } } }src/service/socket.io/request/console.js000066400000000000000000000030551520006216500206730ustar00rootroot00000000000000const consolePrefix = 'socket.io console call' module.exports = async(options) => { const { socket, payload } = options; const { command } = payload try { let redis = socket.p3xrs.ioredis const commands = command.trim().split(' ').filter(val => val.trim() !== '') let mainCommand = commands.shift() mainCommand = mainCommand.toLowerCase(); 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, }) } }src/service/socket.io/request/delete.js000066400000000000000000000011731520006216500204720ustar00rootroot00000000000000const consolePrefix = 'socket.io del key' const sharedIoRedis = require('../shared') module.exports = async(options) => { const { socket, payload } = options; try { let redis = socket.p3xrs.ioredis console.info(consolePrefix, payload.key) await redis.del(payload.key) await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, }) } catch(e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e, }) } }src/service/socket.io/request/expire.js000066400000000000000000000010461520006216500205230ustar00rootroot00000000000000const consolePrefix = 'socket.io expire' module.exports = async(options) => { const { socket, payload } = options; try { 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, }) } }src/service/socket.io/request/key-del-tree.js000066400000000000000000000020111520006216500215070ustar00rootroot00000000000000const consolePrefix = 'socket.io key del tree' const sharedIoRedis = require('../shared') module.exports = async(options) => { const { socket, payload } = options; try { let redis = socket.p3xrs.ioredis const deleteTree = `${payload.key}${payload.redisTreeDivider}*`; console.info(consolePrefix, deleteTree) const keys = await sharedIoRedis.getStreamKeys({ redis: redis, match: deleteTree }) const pipelineDeleteTree = redis.pipeline() for(let key of keys) { console.info(consolePrefix, 'delete key ', key) pipelineDeleteTree.del(key) } await pipelineDeleteTree.exec(); await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, }) } catch(e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e, }) } }src/service/socket.io/request/key-get.js000066400000000000000000000042561520006216500206020ustar00rootroot00000000000000const 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; } viewPipeline.ttl(key) viewPipeline.object('encoding', key) switch(type) { 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, }) } }src/service/socket.io/request/key-hash-delete-field.js000066400000000000000000000010441520006216500232570ustar00rootroot00000000000000const 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 { 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 }) } }src/service/socket.io/request/key-list-delete-index.js000066400000000000000000000013211520006216500233310ustar00rootroot00000000000000const 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 { 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 }) } }src/service/socket.io/request/key-new-or-set.js000066400000000000000000000052271520006216500220220ustar00rootroot00000000000000const consolePrefix = 'socket.io key new' const sharedIoRedis = require('../shared') module.exports = async(options) => { const {socket,payload } = options; const redis = socket.p3xrs.ioredis try { 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 '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; } await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, extend: { key: model.key } }) } catch(e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e }) } }src/service/socket.io/request/key-set-delete-member.js000066400000000000000000000010421520006216500233110ustar00rootroot00000000000000const 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 { 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 }) } }src/service/socket.io/request/key-set.js000066400000000000000000000010521520006216500206050ustar00rootroot00000000000000module.exports = async(options) => { const {socket,payload } = options; const redis = socket.p3xrs.ioredis try { 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 }) } }src/service/socket.io/request/key-zset-delete-member.js000066400000000000000000000010461520006216500235070ustar00rootroot00000000000000const consolePrefix = 'socket.io key zsit delete member' module.exports = async(options) => { const {socket, payload } = options; const redis = socket.p3xrs.ioredis try { 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 }) } }src/service/socket.io/request/persist.js000066400000000000000000000007071520006216500207230ustar00rootroot00000000000000const consolePrefix = 'socket.io persists' module.exports = async(options) => { const { socket, payload } = options; try { 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, }) } }src/service/socket.io/request/redis-test-connection.js000066400000000000000000000021751520006216500234530ustar00rootroot00000000000000const Redis = require('ioredis') module.exports = async(options) => { const { socket } = options; const 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; } } delete redisConfig.name delete redisConfig.id let redis = new Redis(redisConfig) redis.on('error', function(error) { console.error(error) socket.emit(options.responseEvent, { status: 'error', error: error }) redis.disconnect() }) redis.on('connect', async function() { try { await redis.call('client', 'list') socket.emit(options.responseEvent, { status: 'ok', }) } catch(error) { socket.emit(options.responseEvent, { status: 'error', error: error }) } finally { redis.disconnect() } }) }src/service/socket.io/request/refresh.js000066400000000000000000000011061520006216500206620ustar00rootroot00000000000000const 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 }) } }src/service/socket.io/request/rename.js000066400000000000000000000012211520006216500204710ustar00rootroot00000000000000const consolePrefix = 'socket.io rename key' const sharedIoRedis = require('../shared') module.exports = async(options) => { const { socket, payload } = options; try { 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, }) } catch(e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e, }) } }src/service/socket.io/request/save.js000066400000000000000000000006501520006216500201650ustar00rootroot00000000000000module.exports = async(options) => { const {socket } = options; const redis = socket.p3xrs.ioredis try { 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 }) } }src/service/socket.io/request/trigger-redis-disconnect.js000066400000000000000000000011271520006216500241250ustar00rootroot00000000000000const 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: error }) } }src/service/socket.io/shared.js000066400000000000000000000215301520006216500170050ustar00rootroot00000000000000const 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 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.ioredis = undefined } } const getStreamKeys = (options) => { const { redis } = options let { dbsize } = options return new Promise(async (resolve, reject) => { try { if (dbsize === undefined) { dbsize = await redis.dbsize() } let count = 100 if (dbsize > 110000) { count = 10000 } else if (dbsize > 11000) { count = 1000 } console.warn('socket.io getStreamKeys dbsize', dbsize , 'count', count) const stream = redis.scanStream({ match: options.match, count: count }); let keys = []; stream.on('data', (resultKeys) => { keys = keys.concat(resultKeys); // console.log('loading keys', keys.length) }); stream.on('end', async () => { try { resolve(keys); } catch (e) { console.error(e); reject(e) } }); } 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 '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') { continue } const lengthPipelineElement = lengthsPipeline.shift() obj.length = lengthPipelineElement[1] } return result; } const ensureReadonlyConnections = () => { if (p3xrs.cfg.readonlyConnections === true) { const errorCode = new Error('Connections add/save/delete are readonly only') errorCode.code = 'readonly-connections' 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, }), ]) const keys = results[1] let keysInfo = {} if (keys.length < 110000) { keysInfo = await getKeysInfo({ redis: redis, keys: keys, }) } // const keysInfo = [] return { info: results[0], keys: keys, keysInfo: keysInfo, dbsize: dbsize, } } const getFullInfoAndSendSocket = async (options) => { const { redis, socket, payload} = options 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, keys: result.keys, keysInfo: result.keysInfo, dbsize: result.dbsize, })) } 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 = getFullInfoAndSendSocketsrc/service/socket.io/socket.js000066400000000000000000000040751520006216500170340ustar00rootroot00000000000000const socketIoShared = require('./shared') 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, } console.info('socket.io connected %s', socket.id); 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}` require(`./request/${options.action}`)(options) }) socket.emit('configuration', { readonlyConnections: p3xrs.cfg.readonlyConnections === true }) socketIoShared.sendStatus({ socket: socket, }) socketIoShared.sendConnections({ socket: socket, }) }); }