.gitignore000066400000000000000000000003521520170007300130410ustar00rootroot00000000000000/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.npmignore000066400000000000000000000002151520170007300130460ustar00rootroot00000000000000/.idea /build /test /node_modules /*.iml /*.ipr /*.iws /.travis.yml /.scrutinizer.yml /Gruntfile.js /*.lock *.log /corifeus-boot.json /secure.travis.yml000066400000000000000000000014301520170007300131600ustar00rootroot00000000000000language: node_js node_js: - node before_script: - npm install -g grunt-cli npm env: global: secure: ndPY/yENX+jrLScJBSsu+GV93TZcnmFyPirG19UBvu2ZdUyh+jUnDfvsqtz9VCoYgArDoXaE2cn+O7ymlhzo3FaiTn4uhZFwWkNDvB/o4yVUY8JfqUJiKM1Ed/1FCwsKNQ5ClH9EyTKlsecu68f3Tl36C+YRI2n0ar2ILD9Fz4SzcFUS1sxAa1zAk20P5JKeEL/sEzHHYT/ex2p/TEltN/6SaV4ITV6z94FH5dxIuwgz7WQeteNx3dPG6H/b+mkpE6Lyzgb5fRhYSdoSiynWK6/Z4gswftXLG12YIue8NVR8Fa2RMAESA2Nzeu0SDfZl/jRJqw0DWKlNq+8Wzme81TPJw0S8aQDYVNWJAnbIXI2qGibXidy7+WHhM8AhoK/e+1VAKuD61H2W75YERGkktiN9oldcoGiyO/ODkxQ6gGZ6URsxC57SbtQdsTDWCAOGjkZOrcXE4qT30ItrfkVif0BpHtHhgCd8iAsnVDKcuTzj995gI/gNDitAKJjnMgIVlS3nLTYBATy7xJh90BMfDBG0MDvnonA7Co5Qrz04MotgQZ5abXckaNPkBAe9WTFeQ51pHcWSoWcuK/InTPIZCB6Hb6i/q5y8SOlGzTpr2v6vxPw9h83X2AxJLabxB6WgXCwr/DbehgnATK09O7s7QvwmG4PzfqqllcfCC6r4QRs= Gruntfile.js000066400000000000000000000004471520170007300133530ustar00rootroot00000000000000const 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); } LICENSE000066400000000000000000000024401520170007300120560ustar00rootroot00000000000000 @license p3x-redis-ui-server v2018.9.8-5 The p3x-redis-ui-server package motor that is will be connected to the p3x-redis-ui-material web user interface. https://pages.corifeus.com/redis-ui-server Copyright (c) 2018 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.md000066400000000000000000000063301520170007300123320ustar00rootroot00000000000000[//]: #@corifeus-header [![Build Status](https://travis-ci.org/patrikx3/redis-ui-server.svg?branch=master)](https://travis-ci.org/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 will be connected to the p3x-redis-ui-material web user interface. v2018.9.11-8 This is an open-source project. Star this repository, if you like it, or even donate! Thank you so much! :) I run my own server with dynamic IP address, so, it may happen, that the server can not be reachable for about max 15 minutes, due to nature of the dynamic DNS. The server may also be unreachable, when I backup the SSD with Clonzilla (very rarely) or an electrical issue (but this should not happen again). When the server is down, please hang on for 15-30 minutes and the server will be back up. All my domains (patrikx3.com and corifeus.com) could have errors, since I am developing in my free time. However, it is usually stable. **Bugs are evident™ - MATRIX️** ### Node Version Requirement ``` >=8.11.4 ``` ### Built on Node ``` v10.10.0 ``` The ```async``` and ```await``` keywords are required. Install NodeJs: https://nodejs.org/en/download/package-manager/ # Description [//]: #@corifeus-header:end Please this is for a development package, for the full-blown package, please follow: https://pages.corifeus.com/redis-ui/ 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 use the `p3x-redis-ui-material` package based on built with Webpack. This package is name `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 ```bash npm run run ``` It uses `nodemon` and when any file is changed, it will re-load it. [//]: #@corifeus-footer --- [**P3X-REDIS-UI-SERVER**](https://pages.corifeus.com/redis-ui-server) Build v2018.9.11-8 [![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) [![JetBrains](https://cdn.corifeus.com/assets/svg/jetbrains-logo.svg)](https://www.jetbrains.com/) [![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/000077500000000000000000000000001520170007300130315ustar00rootroot00000000000000artifacts/boot/000077500000000000000000000000001520170007300137745ustar00rootroot00000000000000artifacts/boot/p3xrs.json000066400000000000000000000017421520170007300157520ustar00rootroot00000000000000{ "p3xrs": { "https2": { "paths-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.", "cert": "./artifacts/certs/cert.pem", "key": "./artifacts/certs/key.nopass.pem", "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" } }artifacts/certs/000077500000000000000000000000001520170007300141515ustar00rootroot00000000000000artifacts/certs/cert.pem000066400000000000000000000023211520170007300156070ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDYjCCAkqgAwIBAgIJAOTlMOjUD2KqMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQwIBcNMTgwODEyMTMwNjExWhgPMjExODA3MTkxMzA2MTFa MEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ggEKAoIBAQDbXKI46Pzljajx1XsRzyMYSiQIkmnqDlY7k8nO5hM9VFaTZIjKPj6B rhMER4v5QAHx0fqktNJwqMnHObecIfLHdZTxIuAiyZ+SSutO9jZ//g5fO2kxYnTk J9wHtPuIDzw76q7SGowr4Tovu7NOKQAxt7na2Z60+vGbXDUaRqepMMxwIDkVRWVK /zP+fehuRnz2Tu8Le0xxGXdHWtp0e8e3zXRUQCt4bTUsVMUNUIm7O1HpzElkkpCq OcDmOz2jt9TeqayoUbhpmKWwizHvCyOEl6RGFr96WocCiTzCQPhk65stUuYnnWqt Hd5ReHn2VOhtnODs7OL2EVac6xbYlxD/AgMBAAGjUzBRMB0GA1UdDgQWBBRlFzoA krIXN0NKSI+NyHMqP+PWuzAfBgNVHSMEGDAWgBRlFzoAkrIXN0NKSI+NyHMqP+PW uzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCaFU8JPTBl1dSD TFF83yOO8sUE3MjdQe0yh5+fqjeR0pnO7DLkDbVihzeZOCRX5hTjFifwLVLzaYHX ++yUiSoYtT3zBCzc2KsUk4LP3Ep2qmbvFwkodmfuxxsOW2KNajeudrl4yrFuWo+L 0v2/4OJ49bWben8nQQYALvdm/Kh60PLeVv14iM039uQTzwJCq95+QyIfYCfJHNDz DzHHb6xHXJ7GwB1jedTJ4kKXvPnFDpKJDFl18CNhSWVK2zXJIkIPK++gZIhS78RG yJr3lg75R37FkF5V9siHz++YZKsxnfJZ9e08zu14k6Zq/SawbhaqaRYldP0QihPF fD6T7+RC -----END CERTIFICATE----- artifacts/certs/key.nopass.pem000066400000000000000000000032131520170007300167450ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEA21yiOOj85Y2o8dV7Ec8jGEokCJJp6g5WO5PJzuYTPVRWk2SI yj4+ga4TBEeL+UAB8dH6pLTScKjJxzm3nCHyx3WU8SLgIsmfkkrrTvY2f/4OXztp MWJ05CfcB7T7iA88O+qu0hqMK+E6L7uzTikAMbe52tmetPrxm1w1GkanqTDMcCA5 FUVlSv8z/n3obkZ89k7vC3tMcRl3R1radHvHt810VEAreG01LFTFDVCJuztR6cxJ ZJKQqjnA5js9o7fU3qmsqFG4aZilsIsx7wsjhJekRha/elqHAok8wkD4ZOubLVLm J51qrR3eUXh59lTobZzg7Ozi9hFWnOsW2JcQ/wIDAQABAoIBAH3qj4fhXYGbLjYb F7jiUL/ZlxOAj+F1d1RdggMMmFCk/8k9dTO6RQEFXg8fzp41eqhHovs2fHGEwcZl QBsUdZq+yM8d+NCfMcS00puRwzj3KWLK5Jgfbv+kTa3y2kuAi0/NZ6pc6McrUs4h jUw5Rs5DKyEPuAUk4tkONcEue4Em3KinLVt1mixdEPwy0Vnrp+mZ8oXpBYwH/H30 5U5FUtI0vQTjII4chaz+/rjl7PRb3ar653ceTYTKAFajeZ5XjVhTJpTnwRi80zCy gNBSybZZnJe2VR1dXEavx+IA7droj5CAhrFF3Su9iMXuNluX9m97hxot0ckAyjJx 0ZcuZkECgYEA8XTAfdGsGDkpbwkFrJhMOJuI6pMRb1GTFFTQUpDJ0l0bAANn3lvg YzGblEEEPQCc7gkwNPM+avI4S4W6OnI0EVTEN3dYuPvJCsld4tALb7IVVmpPueME 06obDAvd3J/9t4X9c5rlJkhxnrZv9YGC9iluviPTYWY653xSiU432aECgYEA6JMw fZlWEcb9wOvxo6fjBaMlemj4F+znusDvszWagC5S/4Jgx93Y/ahZqbYWtDf0q+WB ZOYecbXbyqPh2oxHkzk2RoPSdewdFo+t8R30HPduXZwrwizktitRuIXSFGoA/OZo zL0ZnhR6IeX2ycmoIQVc47mVqHe5LX9j9Mz5Jp8CgYB6J9sKzH0IdELmDeZ0/0fw i5+lLWpwqxCCBLawTtgMOVZjl1WBRXIsPPi89He0dQ7vzm45G6bEbG7Wpn0k/Ioj CoOKnDKiw59eeamqCh7/D1WENpzHKndSroioe4hvCfJOWtdzGTY9PAFfjmTuC+u5 phSE1ZMnf5TlcBxBovTWgQKBgH7lgHmnJk0A1AdlNSzkN3wl7hcCGwuC2VUOXDrd rnTt0Z9T1dZ3dyO/rVxp261tMnRv/s+8tzO31DqWfG6wR4VOnAyRdE2fDKf4sSF0 BHOhlXhB86Y4tHmo3Sfe7CbzR6K1XM0JCGc2KQVB4gtrme6abHpcEgofeMwYS8OP 6wllAoGACGbXWsFXWMdX9vy+LXmMDUk8Fb/Gk+ObNzO57e7ExM0eXvU9SD7Bq9+k kNToSMlwzC6cgMNDjq2VqNlwwjHkJn6PorqTBFfHrgjqnpXH96NV0VUlOM3WJS4N GpCgbP9Zra3rRKV1ebggG+suMGOMx0yXXTlj7nqrKWb2q8y/uyA= -----END RSA PRIVATE KEY----- artifacts/certs/key.pem000066400000000000000000000034761520170007300154560ustar00rootroot00000000000000-----BEGIN ENCRYPTED PRIVATE KEY----- MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQI2hG1YbsnXMQCAggA MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECEFjsJXDiZUTBIIEyECH9mNz97sX ql2YqnNEu61g9N3dyutnebp1Wib/VGsXiTqsXzu3E9u0+WU9TQimQDHIFe5n2Y/A Q4l6mOg7nn/fYH+4mnHiKgoiADgvg6MDSBYnI+ibadl2oQMgOrleq8KDt7YKGCxi 5edMR9Ld/XRXF4lH4gzENfuOZ+xFRaJMjZPCPrJZdXQiPaYVPPZBLc40m1A0hstk W9TIU1U3MtMtkPcP+doKnJm1SudIFJUN4ZALUmGaIsta1ZJnCuyBxVqwcXFyc0X8 n11psast132N7BnFezKYC49fqfj6ry8BYGKsF/hkNTeym6QROyM2oqvpKcYD4dU2 zc/LlKRVmG6DA+wNLVkm/yVuSFixl+zmAs7pBI7lLrX8il9lis72VYzK33sZsNh4 0jNqfkUSDESxwRbZ1aUuDxP/ZtY5Ppnhuon8C+r3wtEjDd2mg+ZFPOHqzKBnw1W4 nVPQZQK+mOynDoHshm7gIreuscFVeDpm1hoKa1siHOVr2uB4zutEPeg+ud81usDs Cqb2tQaC3idKLY0xjcQw8WXWa30Kw5OuU+Q30VsZSYNxXMks5KG2Ls/ryVqQc397 NXuShZMH8FcAs4ArH+wfxolgFlD+xYdJ7M+6TkpUyvjI3BStJNju9297y47OMsMY SSdFNxjSjfpuDQMqPDMJwVWxQ10H58obpuEE5S322dAd4wGfPJ4V07I6Nyy7vqgZ 58Qpix4aoQ5rg224TobxuK5EpfdvSHgUEzl3RrFW0OjPEVKGxZgylj6X3RWbEzyu eFh0qUELPJh5yC8otur7bzYwH/H5H4DneKFCnFvQxOu2AFBoIsogFG+Wo9C8QFfw fOZp1P5T4xcBhzNkRGtSWrEHLl/8PCRPaziRtm+quEoGfuEt/zxW213WT9kkKfVX 68uM7WKrvsPYzWCtKoOoVc+y7uyNhQK3iCR8nBzsYNFX86GZbFGFZ2uqMwfm4QZ8 CmuMzJlJwmf3wn0VmEpjKIZXmbOAlp7R9drH7/auOgyoASGCbdQp6D5ZlC0mNwOx lZ0fBH20R80P80JW9OYskHRgmNFWgvmtR8aqCzAqj2XGFu9DztdZSxcwwWXpSQZH ICobGwM42RxKUQmVno8nQBPohTHklxDx8XKt9esULx6gH/84hP0+eI+G1pxTYll1 /SyXV2/jMpYHfaCT4NvEnnZGjWJyqz5JVu6kh5AyQ4ed+KoCfxG4LRbMAysRhChu FZxMO2WKV6UnA7MxCfixcp5nbJOvtmYGW8//PaRzkvoREbHK1giwgI6HVO29TumH QP88TtgdSeD0oGRZI4SoRzCNvHFvF+Adq/OUoC69ZV2YTsFVtXDCvX8vf0hm7thl 5yY1Cl6vA3IYZVLdWn6qNg6p0PoYCwSDVQaAjfiLE4gOwvsPez0rVeF+6nGbEDhN JiT6hyLQzsoVMWF+h5SRC6o5WOuFFjFYMGkbtPnvO+/AcT7xQcN30llGqA1lPGR/ RodjRWW+eFXSpGT5bFmZxPMbJcngcZHyGMOxbpxGM1WwqQOH3nBe14n4Dma0QM75 fx4UIx1F1TELqucVO6+yrah4x4aRQp2B/129G/7SdkVaAwO//NJnuV1NtJmcDZ+a B/ZJoprfTKL5lnwmpc8N0A== -----END ENCRYPTED PRIVATE KEY----- artifacts/docs/000077500000000000000000000000001520170007300137615ustar00rootroot00000000000000artifacts/docs/create-https-cert.md000066400000000000000000000030061520170007300176400ustar00rootroot00000000000000[//]: #@corifeus-header # 🏍️ The p3x-redis-ui-server package motor that is will be connected to the p3x-redis-ui-material web user interface. [//]: #@corifeus-header:end # Create HTTPS2 certificate Use PEM pass phrase: `123456789` ```bash #openssl req -newkey rsa:2048 -keyout localhost.key -out localhost.csr -passwd 123456789 openssl req -x509 -sha256 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 36500 openssl rsa -in key.pem -out key.nopass.pem ``` # Allow unauthorized TLS certificate ```bash process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; ``` [//]: #@corifeus-footer --- [**P3X-REDIS-UI-SERVER**](https://pages.corifeus.com/redis-ui-server) Build v2018.9.11-8 [![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) [![JetBrains](https://cdn.corifeus.com/assets/svg/jetbrains-logo.svg)](https://www.jetbrains.com/) [![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:endbin/000077500000000000000000000000001520170007300116215ustar00rootroot00000000000000bin/p3xrs.js000077500000000000000000000001031520170007300132330ustar00rootroot00000000000000#!/usr/bin/env node const boot = require('../src/lib/boot') boot() package.json000066400000000000000000000037541520170007300133500ustar00rootroot00000000000000{ "name": "p3x-redis-ui-server", "version": "2018.9.11-8", "description": "🏍️ The p3x-redis-ui-server package motor that is will be connected to the p3x-redis-ui-material web user interface.", "corifeus": { "icon": "fas fa-flag-checkered", "code": "Reverse", "opencollective": false, "build": true, "nodejs": "v10.10.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", "run": "nodemon --watch src --watch package.json --watch bin ./bin/p3xrs", "run-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": "^2018.9.8-0", "nodemon": "^1.18.4" }, "dependencies-saved": { "koa-body": "^4.0.4", "koa-router": "^7.4.0" }, "dependencies": { "chalk": "^2.4.1", "commander": "^2.18.0", "console-stamp": "^0.2.6", "corifeus-utils": "^2018.9.5-2", "ioredis": "^4.0.0", "koa": "^2.5.2", "koa-send": "^5.0.0", "koa-static": "^5.0.0", "socket.io": "^2.1.1", "spdy": "^3.4.7" }, "engines": { "node": ">=8.11.4" }, "homepage": "https://pages.corifeus.com/redis-ui-server" } redis-ui-server.iml000066400000000000000000000007441520170007300146060ustar00rootroot00000000000000 src/000077500000000000000000000000001520170007300116405ustar00rootroot00000000000000src/index.js000066400000000000000000000001251520170007300133030ustar00rootroot00000000000000module.exports = { lib: require('./lib'), services: require('./service'), } src/lib/000077500000000000000000000000001520170007300124065ustar00rootroot00000000000000src/lib/boot.js000066400000000000000000000011301520170007300137020ustar00rootroot00000000000000require('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.js000066400000000000000000000036621520170007300135220ustar00rootroot00000000000000const program = require('commander') const path = require('path') const fs = require('fs') const cli = () => { const pkg = require('../../package') 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) } 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) return true; } module.exports = cli;src/lib/console-stamp.js000066400000000000000000000017341520170007300155350ustar00rootroot00000000000000const 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.js000066400000000000000000000001721520170007300140530ustar00rootroot00000000000000module.exports = { boot: require('./boot'), cli: require('./cli'), consoleStamp: require('./console-stamp'), }src/service/000077500000000000000000000000001520170007300133005ustar00rootroot00000000000000src/service/index.js000066400000000000000000000000571520170007300147470ustar00rootroot00000000000000module.exports = { koa: require('./koa'), }src/service/koa/000077500000000000000000000000001520170007300140525ustar00rootroot00000000000000src/service/koa/index.js000066400000000000000000000060611520170007300155220ustar00rootroot00000000000000const Koa = require('koa'); //const Router = require('koa-router') const fs = require('fs').promises //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 = await Promise.all([ // key fs.readFile(keyFilename), // cert fs.readFile(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()); this.server = server; server.listen(p3xrs.cfg.https2.port || 7843); } } module.exports = koaServicesrc/service/socket.io/000077500000000000000000000000001520170007300151765ustar00rootroot00000000000000src/service/socket.io/index.js000066400000000000000000000006151520170007300166450ustar00rootroot00000000000000const 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/000077500000000000000000000000001520170007300166665ustar00rootroot00000000000000src/service/socket.io/request/connection-connect.js000066400000000000000000000103451520170007300230150ustar00rootroot00000000000000const 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.info(), sharedIoRedis.getStreamKeys({ redis: redis, }) ]) const databases = results[0] //console.log(databases) socket.emit(options.responseEvent, { status: 'ok', databases: parseInt(databases[1]), info: results[1], keys: results[2] }) } module.exports = async(options) => { const { socket, payload } = options; const { connection } = 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 redisConfig = Object.assign({}, options.payload.connection); delete redisConfig.name delete redisConfig.id let redis = new Redis(redisConfig) let didConnected = false const redisErrorFun = function(error) { console.info(consolePrefix, connection.id, connection.name, 'error' ) console.error(error) if (!didConnected) { socket.emit(options.responseEvent, { status: 'error', error: error }) } const disconnectedData = { connectionId: socket.connectionId, error: error, status: 'error', } socket.p3xrs.io.emit('redis-disconnected', disconnectedData) redis.disconnect() 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 socket.p3xrs.connectionId = connection.id socket.p3xrs.ioredis = redis await generateConnectInfo({ redis: redis, socket: socket, responseEvent: options.responseEvent }) } catch(e) { socket.emit(options.responseEvent, { status: 'error', error: error, }) } 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.js000066400000000000000000000030171520170007300226240ustar00rootroot00000000000000const 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.js000066400000000000000000000015621520170007300235160ustar00rootroot00000000000000const 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.js000066400000000000000000000031241520170007300223170ustar00rootroot00000000000000const 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) { 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.js000066400000000000000000000030551520170007300206710ustar00rootroot00000000000000const 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/del-tree.js000066400000000000000000000016711520170007300207320ustar00rootroot00000000000000const consolePrefix = 'socket.io 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 }) for(let key of keys) { console.info(consolePrefix, 'delete key ', key) await redis.del(key) } socket.emit(options.responseEvent, { status: 'ok', keys: await sharedIoRedis.getStreamKeys({ redis: socket.p3xrs.ioredis }) }) } catch(e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e, }) } }src/service/socket.io/request/redis-test-connection.js000066400000000000000000000015561520170007300234530ustar00rootroot00000000000000const Redis = require('ioredis') module.exports = async(options) => { const { socket } = options; const redisConfig = options.payload.model; 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.js000066400000000000000000000012431520170007300206620ustar00rootroot00000000000000const sharedIoRedis = require('../shared') //const consolePrefix = 'socket.io refresh redis' module.exports = async(options) => { const {socket } = options; const redis = socket.p3xrs.ioredis try { const results = await Promise.all([ redis.info(), sharedIoRedis.getStreamKeys({ redis: redis, }) ]) socket.emit(options.responseEvent, { status: 'ok', info: results[0], keys: results[1] }) } catch(e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e }) } }src/service/socket.io/request/save.js000066400000000000000000000006501520170007300201630ustar00rootroot00000000000000module.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.js000066400000000000000000000011271520170007300241230ustar00rootroot00000000000000const 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.js000066400000000000000000000105031520170007300170010ustar00rootroot00000000000000const 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.info(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 sendConnections = (options) => { const { socket } = options socket.p3xrs.io.emit('connections', { status: 'ok', connections: p3xrs.connections }) } const disconnectRedisIo = (options) => { const { socket } = options if (socket.p3xrs.ioredis !== undefined) { socket.p3xrs.ioredis.disconnect() socket.p3xrs.ioredis = undefined } } const getStreamKeys = (options) => { const { redis } = options return new Promise((resolve, reject) => { const stream = redis.scanStream({ match: options.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 ensureReadonlyConnections = () => { if (p3xrs.cfg.readonlyConnections === true) { const errorCode = new Error('Connections add/save/delete are readonly only') errorCode.code = 'readonly-connections' throw errorCode; } } 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 src/service/socket.io/socket.js000066400000000000000000000037021520170007300170260ustar00rootroot00000000000000const 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) }) socketIoShared.sendStatus({ socket: socket, }) socketIoShared.sendConnections({ socket: socket, }) }); }