├── .gitignore
├── LICENSE
├── README.md
├── icons
└── redis.png
├── package-lock.json
├── package.json
├── redis.html
└── redis.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 | /nbproject/private/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 @AvilaCwb
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # node-red-contrib-redis
2 | Node Red client for Redis with pub/sub, list, lua scripting, ssl, cluster, custom commands, instance injection and other commands support.
3 |
4 | Connection Options parameter receives IORedis object or string (https://github.com/luin/ioredis#connect-to-redis).
5 |
6 | Now uses same connection per config name and open new if you set block connection option.
7 |
8 | Roadmap:
9 | - (ok)Stream Support
10 | - (ok)Flow or Global redis instance injection to use on function Node.
11 | - (wip)Better Validations
12 | - (ok)Custom Commands support(Modules), with instance or Cmd.
13 |
14 | See the Sample flow before ask how this module works.
15 |
16 | Please test and make feedback.
17 |
18 | I need contributors...
19 |
20 |
21 |
22 | Redis Commands:
23 |
24 | 
25 |
26 | Payload -> Redis
27 |
28 | 
29 |
30 | Redis Queue:
31 |
32 | 
33 |
34 | Sample flow:
35 | ```javascript
36 | [{"id":"31f1fcb.a6a4a04","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":240,"wires":[]},{"id":"80d063ee.78deb8","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":240,"wires":[["4cb7b20e.778d34"]]},{"id":"4cb7b20e.778d34","type":"redis-out","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"rpush","name":"","topic":"test","x":450,"y":200,"wires":[]},{"id":"902b8385.2fdd9","type":"redis-in","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"blpop","name":"","topic":"test","timeout":0,"x":450,"y":240,"wires":[["31f1fcb.a6a4a04"]]},{"id":"6373a8a4.82bad","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"{\"a\":1,\"b\":2}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":200,"wires":[["4cb7b20e.778d34"]]},{"id":"f248c9ea.674658","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"getset","name":"","topic":"timestamp","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":430,"y":300,"wires":[["3351713f.42e916"]]},{"id":"424ef610.bbb3a8","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":300,"wires":[["f248c9ea.674658"]]},{"id":"3351713f.42e916","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":300,"wires":[]},{"id":"ba433622.8c6178","type":"catch","z":"f80412b1.e2ee8","name":"","scope":null,"uncaught":false,"x":460,"y":140,"wires":[["ec2978af.3e0458"]]},{"id":"ec2978af.3e0458","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":140,"wires":[]},{"id":"eccd31de.55e3a","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"set","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":380,"y":360,"wires":[["6ac31b1f.b393c4"]]},{"id":"46f5d0c0.8a7198","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"[\"key\",\"value\"]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":360,"wires":[["eccd31de.55e3a"]]},{"id":"6ac31b1f.b393c4","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":360,"wires":[]},{"id":"9f960d71.749988","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"getset","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":390,"y":420,"wires":[["7cef7ea5.dba3b"]]},{"id":"f16ad786.8b294","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"key","payload":"[\"value\"]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":420,"wires":[["9f960d71.749988"]]},{"id":"7cef7ea5.dba3b","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":420,"wires":[]},{"id":"7e45a50c.240f7c","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"hmset","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":390,"y":540,"wires":[["122ff416.a56b8c"]]},{"id":"488a7f5b.a40628","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"myHash","payload":"{\"a\":1,\"b\":2}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":540,"wires":[["7e45a50c.240f7c"]]},{"id":"122ff416.a56b8c","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":540,"wires":[]},{"id":"aff97bc7.84a578","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":600,"wires":[["1676f695.aa7189"]]},{"id":"2f4295b5.f78d42","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":600,"wires":[]},{"id":"1676f695.aa7189","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"hgetall","name":"","topic":"myHash","params":"{}","paramsType":"json","payloadType":"json","block":false,"x":420,"y":600,"wires":[["2f4295b5.f78d42"]]},{"id":"66098945.141118","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":660,"wires":[["e42d2b3b.6bb"]]},{"id":"e23fbe8.d7505c","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":660,"wires":[]},{"id":"e42d2b3b.6bb","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"sadd","name":"","topic":"mySet","params":"[\"memberA\",\"memberB\",\"memberC\"]","paramsType":"json","payloadType":"json","block":false,"x":410,"y":660,"wires":[["e23fbe8.d7505c"]]},{"id":"2163c482.b8a244","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"get","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":380,"y":480,"wires":[["d2ea99d4.a4c51"]]},{"id":"b5f040ea.800be8","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"key","payload":"[]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":480,"wires":[["2163c482.b8a244"]]},{"id":"d2ea99d4.a4c51","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":480,"wires":[]},{"id":"15f62858.26419","type":"redis-lua-script","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","name":"test","keyval":0,"func":"local text = \"Hello World\"\nreturn text","stored":true,"block":false,"x":370,"y":780,"wires":[["4a40fc2d.b70e2c"]]},{"id":"4f1fc69d.539ee","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":780,"wires":[["15f62858.26419"]]},{"id":"4a40fc2d.b70e2c","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":780,"wires":[]},{"id":"6ebb1fdf.2fce","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":720,"wires":[["5a3d77f4.920fd8"]]},{"id":"bac6944e.4e07","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":720,"wires":[]},{"id":"5a3d77f4.920fd8","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"sismember","name":"","topic":"mySet","params":"[\"memberA\"]","paramsType":"json","payloadType":"json","block":false,"x":430,"y":720,"wires":[["bac6944e.4e07"]]},{"id":"c1c3a5a6.c289f","type":"redis-lua-script","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","name":"test2","keyval":0,"func":"local text = \"Hello2222 World2222\"\nreturn text","stored":false,"block":false,"x":370,"y":840,"wires":[["d8bd4107.683c08"]]},{"id":"9013d649.88c35","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":840,"wires":[["c1c3a5a6.c289f"]]},{"id":"d8bd4107.683c08","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":840,"wires":[]},{"id":"cb44db96.c64bb8","type":"redis-in","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"psubscribe","name":"","topic":"TOPIC:*","timeout":0,"x":460,"y":20,"wires":[["78033f6c.31797"]]},{"id":"78033f6c.31797","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":20,"wires":[]},{"id":"dac5fcff.4c4cb","type":"redis-in","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"subscribe","name":"","topic":"TOPIC:OK","timeout":0,"x":460,"y":80,"wires":[["6f01eb54.501e7c"]]},{"id":"6f01eb54.501e7c","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":80,"wires":[]},{"id":"e0d2924b.210708","type":"redis-out","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"publish","name":"","topic":"TOPIC:OK","x":190,"y":140,"wires":[]},{"id":"d1fc59dc.bd4958","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"{\"a\":1,\"b\":2}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":40,"wires":[["e0d2924b.210708"]]},{"id":"3e05b777.781bd8","type":"redis-command","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","command":"del","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":380,"y":900,"wires":[["fd036d2b.dc6d38"]]},{"id":"6924006.91553","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"key","payload":"[]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":900,"wires":[["3e05b777.781bd8"]]},{"id":"fd036d2b.dc6d38","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":900,"wires":[]},{"id":"a13d2797.8a7ff","type":"redis-instance","z":"f80412b1.e2ee8","server":"a0efbb89.5e42d8","name":"","topic":"redis","location":"flow","block":false,"x":130,"y":960,"wires":[]},{"id":"2a93e7ce.341078","type":"inject","z":"f80412b1.e2ee8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":1000,"wires":[["d89709ef.fd9368"]]},{"id":"d89709ef.fd9368","type":"function","z":"f80412b1.e2ee8","name":"","func":"let redis = context.flow.get('redis');\n\nredis.info().then((data)=>{\n msg.payload = data\n node.send(msg);\n})\n\n/*\nredis.call(\"anycmd\").then((data)=>{\n msg.payload = data\n node.send(msg);\n})*/","outputs":1,"noerr":0,"x":370,"y":1000,"wires":[["f2e9c3a0.8f181"]]},{"id":"f2e9c3a0.8f181","type":"debug","z":"f80412b1.e2ee8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":690,"y":1000,"wires":[]},{"id":"a0efbb89.5e42d8","type":"redis-config","z":"","name":"local","options":"{}","cluster":false,"optionsType":"json"}]
37 | ```
38 |
39 | Interact with Redis Lua script sample flow:
40 | ```javascript
41 | [{"id":"db136c5d713d3cd0","type":"inject","z":"1a15e2364209aeb3","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":220,"y":200,"wires":[["5f41ce33e1d008a9"]]},{"id":"4948edd4b85e148f","type":"inject","z":"1a15e2364209aeb3","name":"zadd 1","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 1, \"{\\\"test\\\":1,\\\"hello\\\":\\\"world1\\\", \\\"hexstr\\\": \\\"000102\\\"}\"]","payloadType":"json","x":210,"y":380,"wires":[["f9ccc3e1983f8ec7"]]},{"id":"89734db2e2b504d9","type":"inject","z":"1a15e2364209aeb3","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":220,"y":580,"wires":[["ad21bea98339b79e"]]},{"id":"5f41ce33e1d008a9","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: hello world","keyval":0,"func":"\nreturn \"hello world\"","stored":false,"block":false,"x":600,"y":200,"wires":[["44f2c4e99a1e521d"]]},{"id":"44f2c4e99a1e521d","type":"debug","z":"1a15e2364209aeb3","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":870,"y":520,"wires":[]},{"id":"29035e2612073aa9","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: ping pong","keyval":0,"func":"local foo = redis.call('ping')\nreturn foo","stored":false,"block":false,"x":590,"y":260,"wires":[["44f2c4e99a1e521d"]]},{"id":"b9f91d2f3fceb024","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: set","keyval":"1","func":"local foo = redis.call('SET', KEYS[1], ARGV[1])\nreturn foo","stored":false,"block":false,"x":570,"y":320,"wires":[["44f2c4e99a1e521d"]]},{"id":"cfb9a1d9d6235c96","type":"inject","z":"1a15e2364209aeb3","name":"set key value","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"key\", \"value\"]","payloadType":"json","x":230,"y":320,"wires":[["b9f91d2f3fceb024"]]},{"id":"f9ccc3e1983f8ec7","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: zadd","keyval":"1","func":"local foo = redis.call('ZADD', KEYS[1], ARGV[1], ARGV[2])\nreturn foo","stored":false,"block":false,"x":580,"y":380,"wires":[["44f2c4e99a1e521d"]]},{"id":"75a5d81a80da3959","type":"inject","z":"1a15e2364209aeb3","name":"zadd 2","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 2, \"{\\\"test\\\":2,\\\"hello\\\":\\\"world2\\\", \\\"hexstr\\\": \\\"030405\\\"}\"]","payloadType":"json","x":210,"y":420,"wires":[["f9ccc3e1983f8ec7"]]},{"id":"c32955b956a9c79a","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: cjson decode","keyval":"1","func":"local foo = cjson.decode(ARGV[2])\n\nreturn foo.test","stored":false,"block":false,"x":600,"y":520,"wires":[["44f2c4e99a1e521d"]]},{"id":"84bd8bb2222e0517","type":"inject","z":"1a15e2364209aeb3","name":"cjson","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 3, \"{\\\"test\\\":3,\\\"hello\\\":\\\"world3\\\", \\\"hexstr\\\": \\\"060708\\\"}\"]","payloadType":"json","x":210,"y":520,"wires":[["c32955b956a9c79a"]]},{"id":"8d8b9d844348773c","type":"inject","z":"1a15e2364209aeb3","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":220,"y":260,"wires":[["29035e2612073aa9"]]},{"id":"15959eedb0e32c26","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: substring","keyval":"1","func":"local foo = tonumber(string.sub(KEYS[1], -2, -1))\n\nreturn foo","stored":false,"block":false,"x":590,"y":640,"wires":[["44f2c4e99a1e521d"]]},{"id":"b66f5d8e92e9fbbf","type":"inject","z":"1a15e2364209aeb3","name":"substring","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 3, \"{\\\"test\\\":3,\\\"hello\\\":\\\"world3\\\", \\\"hexstr\\\": \\\"060708\\\"}\"]","payloadType":"json","x":220,"y":640,"wires":[["15959eedb0e32c26"]]},{"id":"2a7fac1573b38b98","type":"inject","z":"1a15e2364209aeb3","name":"zadd 3","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 3, \"{\\\"test\\\":3,\\\"hello\\\":\\\"world3\\\", \\\"hexstr\\\": \\\"060708\\\"}\"]","payloadType":"json","x":210,"y":460,"wires":[["f9ccc3e1983f8ec7"]]},{"id":"ad21bea98339b79e","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: cjson encode","keyval":"0","func":"local foo = {}\n\nfoo.field1 = 1\nfoo.field2 = \"hello world\"\nfoo.field3 = {}\nfoo.field3.name = \"paul\"\n\nreturn cjson.encode(foo)","stored":false,"block":false,"x":600,"y":580,"wires":[["41d9477be860b123"]]},{"id":"41d9477be860b123","type":"json","z":"1a15e2364209aeb3","name":"","property":"payload","action":"","pretty":false,"x":790,"y":580,"wires":[["44f2c4e99a1e521d"]]},{"id":"b6378268209d3f41","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: zcount","keyval":"1","func":"local elementCount = redis.call('ZCOUNT', KEYS[1], \"-inf\", \"+inf\")\n\nreturn elementCount","stored":false,"block":false,"x":580,"y":700,"wires":[["44f2c4e99a1e521d"]]},{"id":"1efddadd63d15077","type":"inject","z":"1a15e2364209aeb3","name":"zcount","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 4, \"{\\\"test\\\":4,\\\"hello\\\":\\\"world4\\\", \\\"hexstr\\\": \\\"090a0b\\\"}\"]","payloadType":"json","x":210,"y":700,"wires":[["b6378268209d3f41"]]},{"id":"e49699a6dde3b6de","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: zrange","keyval":"1","func":"local elementCount = redis.call('ZRANGE', KEYS[1], 0, 3)\n\nreturn elementCount","stored":false,"block":false,"x":580,"y":760,"wires":[["44f2c4e99a1e521d"]]},{"id":"5da7c2d8aea3b596","type":"inject","z":"1a15e2364209aeb3","name":"zrange","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 4, \"{\\\"test\\\":4,\\\"hello\\\":\\\"world4\\\", \\\"hexstr\\\": \\\"090a0b\\\"}\"]","payloadType":"json","x":210,"y":760,"wires":[["e49699a6dde3b6de"]]},{"id":"e9aa29233106c2c8","type":"inject","z":"1a15e2364209aeb3","name":"concat","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"device:96a4:1a87:04\", 4, \"{\\\"test\\\":4,\\\"hello\\\":\\\"world4\\\", \\\"hexstr\\\": \\\"090a0b\\\"}\"]","payloadType":"json","x":210,"y":820,"wires":[["f513666fe9a2c2ca"]]},{"id":"f513666fe9a2c2ca","type":"redis-lua-script","z":"1a15e2364209aeb3","server":"08ae710220cd5170","name":"redis lua: complex","keyval":"1","func":"local response = \"\"\nlocal elementCount = redis.call('ZRANGE', KEYS[1], 0, 3)\n\nfor i=1,3,1\ndo\n response = response .. cjson.decode(elementCount[i]).hexstr\nend\n\nreturn response","stored":false,"block":false,"x":590,"y":820,"wires":[["44f2c4e99a1e521d"]]},{"id":"08ae710220cd5170","type":"redis-config","name":"Local","options":"{}","cluster":false,"optionsType":"json"}]
42 | ```
43 |
--------------------------------------------------------------------------------
/icons/redis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chameleonbr/node-red-contrib-redis/5865f2dc7755b1a81d654b7392015da22ce5021f/icons/redis.png
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-red-contrib-redis",
3 | "version": "1.3.9",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "async": {
8 | "version": "3.2.2",
9 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz",
10 | "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g=="
11 | },
12 | "cluster-key-slot": {
13 | "version": "1.1.0",
14 | "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz",
15 | "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw=="
16 | },
17 | "debug": {
18 | "version": "4.3.4",
19 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
20 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
21 | "requires": {
22 | "ms": "2.1.2"
23 | }
24 | },
25 | "denque": {
26 | "version": "1.5.1",
27 | "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
28 | "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw=="
29 | },
30 | "ioredis": {
31 | "version": "4.28.5",
32 | "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.5.tgz",
33 | "integrity": "sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==",
34 | "requires": {
35 | "cluster-key-slot": "^1.1.0",
36 | "debug": "^4.3.1",
37 | "denque": "^1.1.0",
38 | "lodash.defaults": "^4.2.0",
39 | "lodash.flatten": "^4.4.0",
40 | "lodash.isarguments": "^3.1.0",
41 | "p-map": "^2.1.0",
42 | "redis-commands": "1.7.0",
43 | "redis-errors": "^1.2.0",
44 | "redis-parser": "^3.0.0",
45 | "standard-as-callback": "^2.1.0"
46 | }
47 | },
48 | "lodash.defaults": {
49 | "version": "4.2.0",
50 | "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
51 | "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
52 | },
53 | "lodash.flatten": {
54 | "version": "4.4.0",
55 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
56 | "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
57 | },
58 | "lodash.isarguments": {
59 | "version": "3.1.0",
60 | "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
61 | "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo="
62 | },
63 | "ms": {
64 | "version": "2.1.2",
65 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
66 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
67 | },
68 | "p-map": {
69 | "version": "2.1.0",
70 | "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
71 | "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="
72 | },
73 | "redis-commands": {
74 | "version": "1.7.0",
75 | "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
76 | "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
77 | },
78 | "redis-errors": {
79 | "version": "1.2.0",
80 | "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
81 | "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60="
82 | },
83 | "redis-parser": {
84 | "version": "3.0.0",
85 | "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
86 | "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=",
87 | "requires": {
88 | "redis-errors": "^1.0.0"
89 | }
90 | },
91 | "standard-as-callback": {
92 | "version": "2.1.0",
93 | "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
94 | "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-red-contrib-redis",
3 | "version": "1.4.0",
4 | "description": "Node Red client for Redis with pub/sub, list, lua scripting and other commands support.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/chameleonbr/node-red-contrib-redis.git"
12 | },
13 | "keywords": [
14 | "redis",
15 | "node-red",
16 | "node"
17 | ],
18 | "node-red": {
19 | "nodes": {
20 | "redis": "redis.js"
21 | }
22 | },
23 | "author": {
24 | "name": "Andre Alexandre Avila"
25 | },
26 | "license": "MIT",
27 | "bugs": {
28 | "url": "https://github.com/chameleonbr/node-red-contrib-redis/issues"
29 | },
30 | "homepage": "https://github.com/chameleonbr/node-red-contrib-redis#readme",
31 | "maintainers": [
32 | {
33 | "name": "chameleonbr",
34 | "email": "chameleonbr@gmail.com"
35 | }
36 | ],
37 | "dependencies": {
38 | "async": "^3.2.0",
39 | "ioredis": "^5.4.1"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/redis.html:
--------------------------------------------------------------------------------
1 |
35 |
36 |
51 |
52 |
89 |
90 |
121 |
122 |
128 |
129 |
130 |
163 |
164 |
192 |
193 |
199 |
200 |
248 |
249 |
646 |
647 |
651 |
652 |
653 |
733 |
734 |
766 |
767 |
776 |
777 |
778 |
779 |
810 |
811 |
832 |
833 |
--------------------------------------------------------------------------------
/redis.js:
--------------------------------------------------------------------------------
1 | module.exports = function (RED) {
2 | "use strict";
3 | const Redis = require("ioredis");
4 | const async = require("async");
5 | let connections = {};
6 | let usedConn = {};
7 |
8 | function RedisConfig(n) {
9 | RED.nodes.createNode(this, n);
10 | this.name = n.name;
11 | this.cluster = n.cluster;
12 | if (this.optionsType === "") {
13 | this.options = n.options;
14 | } else {
15 | RED.util.evaluateNodeProperty(n.options, n.optionsType,this,undefined,(err,value) => {
16 | if(!err) {
17 | // Check if value is a string and optionsType is "env"
18 | if (typeof value === 'string' && n.optionsType === "env") {
19 | try {
20 | this.options = JSON.parse(value); // Attempt to parse JSON
21 | } catch (e) {
22 | console.warn("Failed to parse env as JSON string in redis-config node, use plain value:", e);
23 | this.options = value; // Keep the value as is if it's not valid JSON
24 | }
25 | } else {
26 | this.options = value;
27 | }
28 | }
29 | });
30 | }
31 | }
32 | RED.nodes.registerType("redis-config", RedisConfig);
33 |
34 | function RedisIn(n) {
35 | RED.nodes.createNode(this, n);
36 | this.server = RED.nodes.getNode(n.server);
37 | this.command = n.command;
38 | this.name = n.name;
39 | this.topic = n.topic;
40 | this.obj = n.obj;
41 | this.timeout = n.timeout;
42 | let node = this;
43 | let client = getConn(this.server, n.id);
44 | let running = true;
45 |
46 | node.on("close", async (undeploy, done) => {
47 | node.status({});
48 | disconnect(node.id);
49 | client = null;
50 | running = false;
51 | done();
52 | });
53 |
54 | if (node.command === "psubscribe") {
55 | client.on("pmessage", function (pattern, channel, message) {
56 | var payload = null;
57 | try {
58 | if(node.obj){
59 | payload = JSON.parse(message);
60 | }else{
61 | payload = message;
62 | }
63 | } catch (err) {
64 | payload = message;
65 | } finally {
66 | node.send({
67 | pattern: pattern,
68 | topic: channel,
69 | payload: payload,
70 | });
71 | }
72 | });
73 | client[node.command](node.topic, (err, count) => {
74 | node.status({
75 | fill: "green",
76 | shape: "dot",
77 | text: "connected",
78 | });
79 | });
80 | } else if (node.command === "subscribe") {
81 | client.on("message", function (channel, message) {
82 | var payload = null;
83 | try {
84 | if(node.obj){
85 | payload = JSON.parse(message);
86 | }else{
87 | payload = message;
88 | }
89 | } catch (err) {
90 | payload = message;
91 | } finally {
92 | node.send({
93 | topic: channel,
94 | payload: payload,
95 | });
96 | }
97 | });
98 | client[node.command](node.topic, (err, count) => {
99 | node.status({
100 | fill: "green",
101 | shape: "dot",
102 | text: "connected",
103 | });
104 | });
105 | } else {
106 | async.whilst(
107 | (cb) => {
108 | cb(null, running);
109 | },
110 | (cb) => {
111 | client[node.command](node.topic, Number(node.timeout))
112 | .then((data) => {
113 | if (data !== null && data.length == 2) {
114 | var payload = null;
115 | try {
116 | if(node.obj){
117 | payload = JSON.parse(data[1]);
118 | }else{
119 | payload = data[1];
120 | }
121 | } catch (err) {
122 | payload = data[1];
123 | } finally {
124 | node.send({
125 | topic: node.topic,
126 | payload: payload,
127 | });
128 | }
129 | }
130 | cb(null);
131 | })
132 | .catch((e) => {
133 | RED.log.info(e.message);
134 | running = false;
135 | });
136 | },
137 | () => {}
138 | );
139 | }
140 | node.status({
141 | fill: "green",
142 | shape: "dot",
143 | text: "connected",
144 | });
145 | }
146 |
147 | RED.nodes.registerType("redis-in", RedisIn);
148 |
149 | function RedisOut(n) {
150 | RED.nodes.createNode(this, n);
151 | this.server = RED.nodes.getNode(n.server);
152 | this.command = n.command;
153 | this.name = n.name;
154 | this.topic = n.topic;
155 | this.obj = n.obj;
156 | var node = this;
157 |
158 | let client = getConn(this.server, node.server.name);
159 |
160 | node.on("close", function (done) {
161 | node.status({});
162 | disconnect( node.server.name);
163 | client = null;
164 | done();
165 | });
166 |
167 | node.on("input", function (msg, send, done) {
168 | var topic;
169 | send = send || function() { node.send.apply(node,arguments) }
170 | done = done || function(err) { if(err)node.error(err, msg); }
171 | if (msg.topic !== undefined && msg.topic !== "") {
172 | topic = msg.topic;
173 | } else {
174 | topic = node.topic;
175 | }
176 | if (topic === "") {
177 | done(new Error("Missing topic, please send topic on msg or set Topic on node."));
178 | } else {
179 | try {
180 | if(node.obj){
181 | client[node.command](topic, JSON.stringify(msg.payload));
182 | }else{
183 | client[node.command](topic, msg.payload);
184 | }
185 | done();
186 | } catch (err) {
187 | done(err);
188 | }
189 | }
190 | });
191 | }
192 | RED.nodes.registerType("redis-out", RedisOut);
193 |
194 | function RedisCmd(n) {
195 | RED.nodes.createNode(this, n);
196 | this.server = RED.nodes.getNode(n.server);
197 | this.command = n.command;
198 | this.name = n.name;
199 | this.topic = n.topic;
200 | this.params = n.params;
201 | var node = this;
202 | this.block = n.block || false;
203 | let id = this.block ? n.id : this.server.name;
204 |
205 | let client = getConn(this.server, id);
206 |
207 | node.on("close", function (done) {
208 | node.status({});
209 | disconnect(id);
210 | client = null;
211 | done();
212 | });
213 |
214 | node.on("input", function (msg, send, done) {
215 | let topic = undefined;
216 | send = send || function() { node.send.apply(node,arguments) }
217 | done = done || function(err) { if(err)node.error(err, msg); }
218 |
219 | if (msg.topic !== undefined && msg.topic !== "") {
220 | topic = msg.topic;
221 | } else if (node.topic && node.topic !== "") {
222 | try {
223 | topic = node.topic;
224 | } catch (e) {
225 | topic = undefined;
226 | }
227 | }
228 | let payload = undefined;
229 |
230 | if (msg.payload) {
231 | let type = typeof msg.payload;
232 | switch (type) {
233 | case "string":
234 | if (msg.payload.length > 0) {
235 | payload = msg.payload;
236 | }
237 | break;
238 | case "object":
239 | if (Array.isArray(msg.payload)) {
240 | if (msg.payload.length > 0) {
241 | payload = msg.payload;
242 | }
243 | break;
244 | }
245 | if (Object.keys(msg.payload).length > 0) {
246 | payload = msg.payload;
247 | }
248 | break;
249 | }
250 | } else if (
251 | node.params &&
252 | node.params !== "" &&
253 | node.params !== "[]" &&
254 | node.params !== "{}"
255 | ) {
256 | try {
257 | payload = JSON.parse(node.params);
258 | } catch (e) {
259 | payload = undefined;
260 | }
261 | }
262 |
263 | let response = function (err, res) {
264 | if (err) {
265 | done(err);
266 | } else {
267 | msg.payload = res;
268 | send(msg);
269 | done();
270 | }
271 | };
272 |
273 | if (!payload) {
274 | payload = topic;
275 | topic = undefined;
276 | }
277 | if (topic) {
278 | client.call(node.command, topic, payload, response);
279 | } else if (payload) {
280 | client.call(node.command, payload, response);
281 | } else {
282 | client.call(node.command, response);
283 | }
284 | });
285 | }
286 | RED.nodes.registerType("redis-command", RedisCmd);
287 |
288 | function RedisLua(n) {
289 | RED.nodes.createNode(this, n);
290 | this.server = RED.nodes.getNode(n.server);
291 | this.func = n.func;
292 | this.name = n.name;
293 | this.keyval = n.keyval;
294 | this.stored = n.stored;
295 | this.sha1 = "";
296 | this.command = "eval";
297 | var node = this;
298 | this.block = n.block || false;
299 | let id = this.block ? n.id : n.server.name;
300 |
301 | let client = getConn(this.server, id);
302 |
303 | node.on("close", function (done) {
304 | node.status({});
305 | disconnect(id);
306 | client = null;
307 | done();
308 | });
309 | if (node.stored) {
310 | client.script("load", node.func, function (err, res) {
311 | if (err) {
312 | node.status({
313 | fill: "red",
314 | shape: "dot",
315 | text: "script not loaded",
316 | });
317 | } else {
318 | node.status({
319 | fill: "green",
320 | shape: "dot",
321 | text: "script loaded",
322 | });
323 | node.sha1 = res;
324 | }
325 | });
326 | }
327 |
328 | node.on("input", function (msg, send, done) {
329 | send = send || function() { node.send.apply(node,arguments) }
330 | done = done || function(err) { if(err)node.error(err, msg); }
331 | if (node.keyval > 0 && !Array.isArray(msg.payload)) {
332 | throw Error("Payload is not Array");
333 | }
334 |
335 | var args = null;
336 | if (node.stored) {
337 | node.command = "evalsha";
338 | args = [node.sha1, node.keyval].concat(msg.payload);
339 | } else {
340 | args = [node.func, node.keyval].concat(msg.payload);
341 | }
342 | client[node.command](args, function (err, res) {
343 | if (err) {
344 | done(err);
345 | } else {
346 | msg.payload = res;
347 | send(msg);
348 | done();
349 | }
350 | });
351 | });
352 | }
353 | RED.nodes.registerType("redis-lua-script", RedisLua);
354 |
355 | function RedisInstance(n) {
356 | RED.nodes.createNode(this, n);
357 | this.server = RED.nodes.getNode(n.server);
358 | this.location = n.location;
359 | this.name = n.name;
360 | this.topic = n.topic;
361 | let id = n.id;
362 | var node = this;
363 | let client = getConn(this.server, id);
364 |
365 | this.context()[node.location].set(node.topic, client);
366 | node.status({
367 | fill: "green",
368 | shape: "dot",
369 | text: "ready",
370 | });
371 |
372 | node.on("close", function (done) {
373 | node.status({});
374 | this.context()[node.location].set(node.topic, null);
375 | disconnect(id);
376 | client = null;
377 | done();
378 | });
379 | }
380 | RED.nodes.registerType("redis-instance", RedisInstance);
381 |
382 | function getConn(config, id) {
383 | if (connections[id]) {
384 | usedConn[id]++;
385 | return connections[id];
386 | }
387 |
388 | let options = config.options;
389 |
390 | if (!options) {
391 | return config.error(
392 | "Missing options in the redis config - Are you upgrading from old version?",
393 | null
394 | );
395 | }
396 | try {
397 | if (config.cluster) {
398 | connections[id] = new Redis.Cluster(options);
399 | } else {
400 | connections[id] = new Redis(options);
401 | }
402 |
403 | connections[id].on("error", (e) => {
404 | config.error(e, null);
405 | });
406 |
407 | if (usedConn[id] === undefined) {
408 | usedConn[id] = 1;
409 | }
410 | return connections[id];
411 | } catch (e) {
412 | config.error(e.message, null);
413 | }
414 | }
415 |
416 | function disconnect(id) {
417 | if (usedConn[id] !== undefined) {
418 | usedConn[id]--;
419 | }
420 | if (connections[id] && usedConn[id] <= 0) {
421 | connections[id].disconnect();
422 | delete connections[id];
423 | }
424 | }
425 | };
426 |
--------------------------------------------------------------------------------