├── .config.users.json
├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── app.json
├── flows.json
├── flows_cred.json
├── nodes
└── README.md
├── package.json
├── public
├── css
│ ├── simplegrid.css
│ └── style.css
├── favicon.ico
├── images
│ ├── ets-globe.png
│ ├── node-red-title-flow.png
│ ├── node-red.png
│ ├── nr-image-1.png
│ ├── save-button.png
│ └── tab.png
└── index.html
├── settings.js
└── utils
├── file-explorer-flow.json
└── save-all-changes-flow.json
/.config.users.json:
--------------------------------------------------------------------------------
1 | {
2 | "alerox": {
3 | "editor": {
4 | "view": {
5 | "view-show-grid": true,
6 | "view-snap-grid": true,
7 | "view-grid-size": 20,
8 | "view-node-status": true,
9 | "view-node-show-label": true,
10 | "view-show-tips": true,
11 | "view-show-welcome-tours": true
12 | },
13 | "tours": {
14 | "welcome": "latest"
15 | },
16 | "sidebar": {
17 | "order": [
18 | "info",
19 | "help",
20 | "debug",
21 | "dashboard",
22 | "config",
23 | "context"
24 | ]
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gitignore
2 | .DS_Store
3 | .bash_history
4 | .bash_logout
5 | .bashrc
6 | .cache
7 | .forever
8 | .node-gyp
9 | node_modules
10 | .npm
11 | .profile
12 | run-local.bat
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node node_modules/node-red/red.js --settings ./settings.js --userDir ./
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # nodered-heroku
2 | A wrapper for deploying [Node-RED](http://nodered.org) into the [Heroku](https://www.heroku.com).
3 | * DEMO: Flow Editor - [https://nodered-heroku.herokuapp.com/editor](https://nodered-heroku.herokuapp.com/editor)
4 | * DEMO: Dashboard UI - [https://nodered-heroku.herokuapp.com](https://nodered-heroku.herokuapp.com)
5 |
6 |
7 | ## Warning: Heroku doesn't automatically save flows, credentials and installed nodes
8 | ```
9 | [TL,DR] Use the SAVE Inject node in the first flow (see step 5).
10 | ```
11 | To overcome this, after having deployed the new flows, export All flows as *flows.json* file, and push it to the GitHub repo linked to Heroku. Do the same with *flows_cred.json* and *package.json* for credentials and nodes installed in Palette. Detail on step 5.
12 |
13 | ## 1. Deploying Node-RED to Heroku
14 | [](https://heroku.com/deploy?template=https://github.com/hybuild-project/nodered-heroku)
15 |
16 | ## 2. Set up GitHub repo and Heroku app
17 | * Fork this GitHub repo.
18 | * Set GitHub as deploy source on Heroku setting.
19 | * Enable Automatic Deployment, so that every time any file is pushed to GitHub repo, Heroku will rebuild Node-RED with updated files.
20 |
21 | ## 3. Password protect the flow editor
22 | Set username and password for Node-RED Flow Editor:
23 | * **NODE_RED_USERNAME** - the username to secure the Flow Editor with
24 | * **NODE_RED_PASSWORD** - the password to secure the Flow Editor with
25 |
26 | ## 4. Access Node-Red on Cloud
27 | * Flow Editor - [nodered-on-cloud.herokuapp.com/editor](https://nodered-on-cloud.herokuapp.com/editor)
28 | * Dashboard UI - [nodered-on-cloud.herokuapp.com/ui](https://nodered-on-cloud.herokuapp.com/ui)
29 | * Home page - [nodered-on-cloud.herokuapp.com](https://nodered-on-cloud.herokuapp.com)
30 |
31 | ## 5. Export all flows, credentials and installed nodes
32 | ### Manual mode (original)
33 | * In Editor, to export *flows.json*, click hamburger icon `☰` (top right), click Export, choose tab "All flows", then Download.
34 | * To export all the other files, browse the /app folder, e.g., with this [flow](https://flows.nodered.org/flow/44bc7ad491aacb4253dd8a5f757b5407) or the [modified version](utils/file-explorer-flow.json), and download all files.
35 | * Push *flows.json*, *flows_cred.json*, *package.json* to GitHub, so that Node-RED is rebuilt with the latest files at Heroku restart.
36 | ### Alternative mode (recommended)
37 | * Use the `SAVE` Inject node in the [first flow](utils/save-all-changes-flow.json) to directly push all files to GitHub.
38 |
39 | 
40 |
41 | ## Some included nodes
42 | * Dashboard UI - node-red-dashboard
43 | * MQTT - node-red-contrib-aedes
44 | * Blynk Cloud - node-red-contrib-blynk-ws
45 | * Email - node-red-node-email
46 | * Telegram - node-red-contrib-telegrambot-home
47 | * InfluxDB, MongoDB, Modbus, OPC UA, Netatmo, PostgresSQL, Wordmap, etc.
48 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Node-RED",
3 | "description": "A visual tool for wiring the Internet of Things, for Heroku deployment.",
4 | "keywords": [
5 | "Node-RED",
6 | "IoT",
7 | "Internet of Things",
8 | "Node.js",
9 | "Node",
10 | "NodeRED",
11 | "Nodejs",
12 | "Heroku",
13 | "Cloud"
14 | ],
15 | "website": "http://nodered.org/",
16 | "repository": "https://github.com/hybuild-project/nodered-heroku.git",
17 | "logo": "http://nodered.org/node-red.png",
18 | "success_url": "/editor",
19 | "env": {
20 | "NODE_RED_USERNAME": {
21 | "description": "Set Username for Node-RED UI editor.",
22 | "value": ""
23 | },
24 | "NODE_RED_PASSWORD": {
25 | "description": "Set Password for Node-RED UI editor.",
26 | "value": ""
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/flows.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sevenmojoe/nodered-heroku/8b0a6137d3aa78dcfa8e063e033b5d4a3e2e22af/flows.json
--------------------------------------------------------------------------------
/flows_cred.json:
--------------------------------------------------------------------------------
1 | {
2 | "a8d2d040.73334": {},
3 | "01b856875e0d074d": {},
4 | "0c550dcd627c0c5f": {
5 | "password": "${NETATMO_BEARER}"
6 | },
7 | "ee20ae5266a5e32c": {
8 | "token": "${BOT_TOKEN}"
9 | },
10 | "9c09e087.a48e7": {},
11 | "f08c8c22d21d93de": {
12 | "refreshToken": "${REFRESH_TOKEN}",
13 | "accessToken": "${ACCESS_TOKEN}",
14 | "email": "${EMAIL}",
15 | "cert": "${CERT}",
16 | "thingId": "${THING_ID}",
17 | "caCert": "${CA_CERT}",
18 | "server": "${SERVER}",
19 | "privateKey": "${PRIVATE_KEY}"
20 | },
21 | "2bb8dfa0.48854": {
22 | "user": "${ATLAS_USERNAME}",
23 | "password": "${ATLAS_PASSWORD}"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/nodes/README.md:
--------------------------------------------------------------------------------
1 | To add additional nodes, either:
2 | - drop them in this directory and add their dependencies to ../package.json
3 | - add their npm package name to ../package.json
4 |
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nodered-heroku",
3 | "version": "1.0.0",
4 | "author": {
5 | "name": "Alerox"
6 | },
7 | "description": "Node-RED on cloud, deployed in Heroku",
8 | "engines": {
9 | "node": "14.x"
10 | },
11 | "keywords": [
12 | "node",
13 | "red",
14 | "cloud",
15 | "heroku"
16 | ],
17 | "dependencies": {
18 | "feedparser": "2.2.10",
19 | "nano": "8.2.2",
20 | "node-red": "latest",
21 | "node-red-contrib-aedes": "latest",
22 | "node-red-contrib-postgresql": "latest",
23 | "node-red-contrib-qrcode-generator": "latest",
24 | "node-red-contrib-string": "latest",
25 | "node-red-contrib-telegrambot-home": "latest",
26 | "node-red-contrib-ui-led": "latest",
27 | "node-red-contrib-ui-media": "latest",
28 | "node-red-contrib-ui-time-scheduler": "latest",
29 | "node-red-dashboard": "latest",
30 | "node-red-node-base64": "latest",
31 | "node-red-node-mongodb": "latest",
32 | "node-red-node-ui-list": "latest",
33 | "node-red-node-ui-table": "latest",
34 | "node-red-contrib-simple-queue":"latest",
35 | "node-red-contrib-cron-plus":"latest",
36 | "node-red-contrib-uuid":"latest",
37 | "node-red-contrib-actionflows":"latest",
38 | "node-red-contrib-mongodb4":"latest",
39 | "node-red-contrib-mongoql":"latest",
40 | "redis": "3.0.2",
41 | "when": "3.7.8"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/public/css/simplegrid.css:
--------------------------------------------------------------------------------
1 | /*
2 | Simple Grid
3 | Learn More - http://dallasbass.com/simple-grid-a-lightweight-responsive-css-grid/
4 | Project Page - http://thisisdallas.github.com/Simple-Grid/
5 | Author - Dallas Bass
6 | Site - dallasbass.com
7 | */
8 |
9 | *, *:after, *:before {
10 | -webkit-box-sizing: border-box;
11 | -moz-box-sizing: border-box;
12 | box-sizing: border-box;
13 | }
14 |
15 | body {
16 | margin: 0px;
17 | }
18 |
19 | [class*='col-'] {
20 | float: left;
21 | padding-left: 17px;
22 | padding-right: 17px;
23 | }
24 |
25 | [class*='col-']:last-of-type {
26 | padding-right: 0px;
27 | }
28 | [class*='col-']:first-of-type {
29 | padding-left: 0px;
30 | }
31 |
32 | .grid {
33 | width: 100%;
34 | max-width: 1155px;
35 | min-width: 755px;
36 | margin: 0 auto;
37 | overflow: hidden;
38 | padding: 0px 75px 0 75px;
39 | }
40 |
41 | .grid:after {
42 | content: "";
43 | display: table;
44 | clear: both;
45 | }
46 |
47 |
48 | /* Content Columns */
49 |
50 | .col-1-1 {
51 | width: 100%;
52 | }
53 | .col-2-3, .col-8-12 {
54 | width: 66.66%;
55 | }
56 |
57 | .col-1-2, .col-6-12 {
58 | width: 50%;
59 | }
60 |
61 | .col-1-3, .col-4-12 {
62 | width: 33.33%;
63 | }
64 |
65 | .col-1-4, .col-3-12 {
66 | width: 25%;
67 | }
68 |
69 | .col-1-5 {
70 | width: 20%;
71 | }
72 |
73 | .col-1-6, .col-2-12 {
74 | width: 16.667%;
75 | }
76 |
77 | .col-1-7 {
78 | width: 14.28%;
79 | }
80 |
81 | .col-1-8 {
82 | width: 12.5%;
83 | }
84 |
85 | .col-1-9 {
86 | width: 11.1%;
87 | }
88 |
89 | .col-1-10 {
90 | width: 10%;
91 | }
92 |
93 | .col-1-11 {
94 | width: 9.09%;
95 | }
96 |
97 | .col-1-12 {
98 | width: 8.33%
99 | }
100 |
101 | /* Layout Columns */
102 |
103 | .col-11-12 {
104 | width: 91.66%
105 | }
106 |
107 | .col-10-12 {
108 | width: 83.333%;
109 | }
110 |
111 | .col-9-12 {
112 | width: 75%;
113 | }
114 |
115 | .col-5-12 {
116 | width: 41.66%;
117 | }
118 |
119 | .col-7-12 {
120 | width: 58.33%
121 | }
122 |
123 | @media handheld, only screen and (max-width: 767px) {
124 |
125 |
126 | .grid {
127 | width: 100%;
128 | min-width: 0;
129 | margin-left: 0px;
130 | margin-right: 0px;
131 | padding-left: 0px;
132 | padding-right: 0px;
133 | }
134 |
135 | [class*='col-'] {
136 | width: auto;
137 | float: none;
138 | margin-left: 0px;
139 | margin-right: 0px;
140 | margin-top: 10px;
141 | margin-bottom: 10px;
142 | padding-left: 20px !important;
143 | padding-right: 20px !important;
144 | }
145 | }
--------------------------------------------------------------------------------
/public/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | color: #555;
3 | }
4 | a {
5 | text-decoration: none;
6 | }
7 | .title, .row2, .nodes {
8 | background: #eee;
9 | }
10 |
11 |
12 | .blurb a {
13 | text-decoration: underline;
14 | color: #555;
15 | }
16 | a.button1 {
17 | text-decoration: none;
18 | display: inline-block;
19 | padding: 30px 40px;
20 | border-radius: 5px;
21 | background: #aa6767;
22 | color: #eee;
23 | }
24 | a.button1:hover {
25 | background: #7F4545;
26 | }
27 | a.button2 {
28 | text-decoration: none;
29 | display: inline-block;
30 | padding: 30px 40px;
31 | border-radius: 5px;
32 | background: #67aaaa;
33 | color: #eee;
34 | }
35 | a.button2:hover {
36 | background: #457F7F;
37 | }
38 | .row3, .row4, .row5 {
39 | background: #fff;
40 | }
41 |
42 | .row9 {
43 | background: #676767;
44 | }
45 | .footer {
46 | background: #333;
47 | }
48 |
49 | .footer .grid {
50 | color: #eee;
51 | padding: 20px 0;
52 | font-family: Arial;
53 | }
54 | .footer a {
55 | color: #eee;
56 | }
57 |
58 | .footer .content {
59 | height: auto;
60 | min-height: 0;
61 | margin: 20px auto;
62 | }
63 |
64 | .footer .headline {
65 | margin: 80px auto 20px auto;
66 | }
67 | .footer {
68 | text-align: center;
69 | }
70 |
71 | .ets-link {
72 | margin: 10px 0px;
73 |
74 | }
75 | .ets-globe {
76 | width: 60px;
77 | vertical-align: middle;
78 | }
79 |
80 | .content {
81 | margin: 80px 0;
82 | }
83 |
84 | .blurb {
85 | font-family: Arial;
86 | font-size: 16px;
87 | line-height: 1.6em;
88 | }
89 | .blurb p {
90 | margin-top: 0;
91 | }
92 | .blurb h3, .nodes h3 {
93 | font-family: "Roboto Slab";
94 | font-size: 24px;
95 | font-weight: normal;
96 | margin-top: 0;
97 | margin-bottom: 0.5em;
98 | }
99 | .blurb h4 {
100 | font-family: "Roboto Slab";
101 | font-size: 18px;
102 | font-weight: normal;
103 | margin-top: 0.8em;
104 | margin-bottom: 0.5em;
105 | }
106 | .feature {
107 | max-width: 485px;
108 | margin-left: auto;
109 | margin-right: auto;
110 | text-align: center;
111 | }
112 |
113 | .row3 .feature, .row4 .feature, .row5 .feature {
114 | border: none;
115 | }
116 | .feature img {
117 | max-width: 445px;
118 | width: 100%;
119 | }
120 | .title .content {
121 | margin: 80px 0 20px 0;
122 | height: 280px;
123 | text-align: center;
124 | }
125 | .title h1 {
126 | font-size: 36px;
127 | font-family: "Roboto Slab";
128 | font-weight: bold;
129 | margin-bottom: 10px;
130 | color: #676767;
131 | }
132 | .title h2 {
133 | margin-top: 0px;
134 | font-size: 20px;
135 | font-family: "Roboto Slab";
136 | font-weight: normal;
137 | color: #555;
138 | }
139 | .title img {
140 | margin: auto;
141 | max-width: 769px;
142 | width: 100%;
143 | }
144 |
145 | .nodes .content {
146 | text-align: center;
147 | height: auto;
148 | min-height: 0;
149 | margin: 40px 0 40px 0;
150 | }
151 | .nodes .grid {
152 | padding-top: 40px;
153 | padding-bottom: 40px;
154 | }
155 |
156 | .nodes h3 {
157 | text-align: center;
158 | }
159 | .nodes h4 {
160 | font-family: "Roboto Slab";
161 | text-align: center;
162 | font-weight: normal;
163 | margin-top: 3px;
164 | height: 50px;
165 | }
166 |
167 | .header {
168 | font-family: "Roboto Slab";
169 | font-size: 20px;
170 | line-height: 50px;
171 | color: #999;
172 | padding: 0px 10px;
173 | height: 50px;
174 | background: #333;
175 | }
176 | .header-content {
177 | width: 100%;
178 | max-width: 1155px;
179 | min-width: 755px;
180 | margin: 0 auto;
181 | }
182 | .header-content .logo {
183 | vertical-align: middle;
184 | height: 20px;
185 | }
186 | .header-content a {
187 | color: #999;
188 | }
189 | .header-content ul {
190 | float: right;
191 | list-style-type: none;
192 | margin: -0;
193 | padding: 0;
194 | background: #333;
195 | }
196 | .header-content li {
197 | display: inline-block;
198 | margin: 0;
199 | padding: 0;
200 | }
201 | .header-content li a {
202 | display: block;
203 | padding: 0 15px 0 15px;
204 | }
205 | .header-content li.current {
206 | background: url(images/tab.png) 50% bottom no-repeat;
207 | }
208 | .header-content li.current a {
209 | color: #fff;
210 | }
211 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sevenmojoe/nodered-heroku/8b0a6137d3aa78dcfa8e063e033b5d4a3e2e22af/public/favicon.ico
--------------------------------------------------------------------------------
/public/images/ets-globe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sevenmojoe/nodered-heroku/8b0a6137d3aa78dcfa8e063e033b5d4a3e2e22af/public/images/ets-globe.png
--------------------------------------------------------------------------------
/public/images/node-red-title-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sevenmojoe/nodered-heroku/8b0a6137d3aa78dcfa8e063e033b5d4a3e2e22af/public/images/node-red-title-flow.png
--------------------------------------------------------------------------------
/public/images/node-red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sevenmojoe/nodered-heroku/8b0a6137d3aa78dcfa8e063e033b5d4a3e2e22af/public/images/node-red.png
--------------------------------------------------------------------------------
/public/images/nr-image-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sevenmojoe/nodered-heroku/8b0a6137d3aa78dcfa8e063e033b5d4a3e2e22af/public/images/nr-image-1.png
--------------------------------------------------------------------------------
/public/images/save-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sevenmojoe/nodered-heroku/8b0a6137d3aa78dcfa8e063e033b5d4a3e2e22af/public/images/save-button.png
--------------------------------------------------------------------------------
/public/images/tab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sevenmojoe/nodered-heroku/8b0a6137d3aa78dcfa8e063e033b5d4a3e2e22af/public/images/tab.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
This template instance of Node-RED is enough to get you started creating flows.
67 |
68 |
You may want to customise it for your needs,
69 | for example replacing this introduction page with your own, adding http authentication to the
70 | flow editor or adding new nodes to
71 | the palette.
72 |
73 |
Password protect the flow editor
74 |
By default, the editor is open for anyone to access and modify flows. To password-protect the
75 | editor:
76 |
Add the following user-defined variables.
77 |
78 |
NODE_RED_USERNAME - the username to secure the editor with
79 |
NODE_RED_PASSWORD - the password to secure the editor with
80 |
81 |
Adding new nodes to the palette
82 |
83 |
There is a growing collection of additional nodes that can be added to the Node-RED editor.
84 | You can search for available nodes in the Node-RED library.
86 |
The required node package are added in the file package.json to the
87 | dependencies section. The format is:
88 |
"node-red-package-name":"x.y.x"
where x.y.z is the desired
89 | version number.
90 |
91 |
92 | See next section to save the added nodes.
93 |
94 |
95 |
Saving deployed flows and installed nodes
96 |
97 |
Heroku doesn't automatically save flows, credentials and installed nodes, so they may be
98 | lost when dynos are restarted. To overcome this, changes must be pushed to GitHub so as
99 | Node-RED is rebuilt correctly with the changed files when Heroku restarts.
100 |
To do this, use the SAVE Inject node in the first Flow to push
101 | flows.json, flows_cred.json and package.json to
102 | GitHub, after new flows are deployed and/or new nodes installed into the Palette.
103 |
104 |
105 |
106 |
Upgrading the version of Node-RED
107 |
108 |
This boilerplate is configured to grab the latest stable release of Node-RED whenever the
109 | application is pushed into Heroku.
110 |
111 |
Changing the static web content
112 |
113 |
The page you are reading now is served as static content from the application. This can be
114 | replaced with whatever content you want in the public directory.
115 |
116 |
Remove static web content and serve the flow editor from the root path
117 |
118 |
In the file settings.js, delete the httpStatic and
119 | httpAdminRoot entries.
120 |
121 |
122 |
123 |
124 |
125 |
126 |
136 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/settings.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | **/
17 |
18 | var path = require('path');
19 | var when = require('when');
20 |
21 | process.env.HOSTNAME = require('os').hostname();
22 | //process.env.BOT_TOKEN = "2097247350:AAHvGZR34e34Y0MjKtvFRFLs1qcysirD2rA";
23 | //... useless... use Config Vars in Heroku Dashboard | Settings, instead!
24 |
25 | /**
26 | * This is the default settings file provided by Node-RED.
27 | *
28 | * It can contain any valid JavaScript code that will get run when Node-RED
29 | * is started.
30 | *
31 | * Lines that start with // are commented out.
32 | * Each entry should be separated from the entries above and below by a comma ','
33 | *
34 | * For more information about individual settings, refer to the documentation:
35 | * https://nodered.org/docs/user-guide/runtime/configuration
36 | *
37 | * The settings are split into the following sections:
38 | * - Flow File and User Directory Settings
39 | * - Security
40 | * - Server Settings
41 | * - Runtime Settings
42 | * - Editor Settings
43 | * - Node Settings
44 | *
45 | **/
46 |
47 | var settings = module.exports = {
48 |
49 | // [v1.x] Blacklist the non-bluemix friendly nodes
50 | // nodesExcludes:[ '66-mongodb.js','75-exec.js','35-arduino.js','36-rpi-gpio.js','25-serial.js','28-tail.js','50-file.js','31-tcpin.js','32-udp.js','23-watch.js' ],
51 |
52 | // [v1.x] Enable module reinstalls on start-up; this ensures modules installed
53 | // post-deploy are restored after a restage
54 | // autoInstallModules: true,
55 |
56 | // [v1.x] You can protect the user interface with a userid and password by using the following property
57 | // the password must be an md5 hash eg.. 5f4dcc3b5aa765d61d8327deb882cf99 ('password')
58 | // httpAdminAuth: {user:"user",pass:"5f4dcc3b5aa765d61d8327deb882cf99"},
59 |
60 | /*******************************************************************************
61 | * Flow File and User Directory Settings
62 | * - flowFile
63 | * - credentialSecret
64 | * - flowFilePretty
65 | * - userDir
66 | * - nodesDir
67 | ******************************************************************************/
68 |
69 | /** The file containing the flows. If not set, defaults to flows_.json **/
70 | // Never change flow's file
71 | flowFile: 'flows.json',
72 |
73 | /** By default, credentials are encrypted in storage using a generated key. To
74 | * specify your own secret, set the following property.
75 | * If you want to disable encryption of credentials, set this property to false.
76 | * Note: once you set this property, do not change it - doing so will prevent
77 | * node-red from being able to decrypt your existing credentials and they will be
78 | * lost.
79 | */
80 | //credentialSecret: "a-secret-key",
81 | // Disabled Credential Secret
82 | credentialSecret: false,
83 |
84 | /** By default, the flow JSON will be formatted over multiple lines making
85 | * it easier to compare changes when using version control.
86 | * To disable pretty-printing of the JSON set the following property to false.
87 | */
88 | flowFilePretty: true,
89 |
90 | /** By default, all user data is stored in a directory called `.node-red` under
91 | * the user's home directory. To use a different location, the following
92 | * property can be used
93 | */
94 | //userDir: '/home/nol/.node-red/',
95 |
96 | /** Node-RED scans the `nodes` directory in the userDir to find local node files.
97 | * The following property can be used to specify an additional directory to scan.
98 | */
99 | //nodesDir: '/home/nol/.node-red/nodes',
100 | // Add the nodes in
101 | nodesDir: path.join(__dirname, "nodes"),
102 |
103 | /*******************************************************************************
104 | * Security
105 | * - adminAuth
106 | * - https
107 | * - httpsRefreshInterval
108 | * - requireHttps
109 | * - httpNodeAuth
110 | * - httpStaticAuth
111 | ******************************************************************************/
112 |
113 | /** To password protect the Node-RED editor and admin API, the following
114 | * property can be used. See http://nodered.org/docs/security.html for details.
115 | */
116 | //adminAuth: {
117 | // type: "credentials",
118 | // users: [{
119 | // username: "admin",
120 | // password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.",
121 | // permissions: "*"
122 | // }]
123 | //},
124 |
125 | /** The following property can be used to enable HTTPS
126 | * This property can be either an object, containing both a (private) key
127 | * and a (public) certificate, or a function that returns such an object.
128 | * See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
129 | * for details of its contents.
130 | */
131 |
132 | /** Option 1: static object */
133 | //https: {
134 | // key: require("fs").readFileSync('privkey.pem'),
135 | // cert: require("fs").readFileSync('cert.pem')
136 | //},
137 |
138 | /** Option 2: function that returns the HTTP configuration object */
139 | // https: function() {
140 | // // This function should return the options object, or a Promise
141 | // // that resolves to the options object
142 | // return {
143 | // key: require("fs").readFileSync('privkey.pem'),
144 | // cert: require("fs").readFileSync('cert.pem')
145 | // }
146 | // },
147 |
148 | /** If the `https` setting is a function, the following setting can be used
149 | * to set how often, in hours, the function will be called. That can be used
150 | * to refresh any certificates.
151 | */
152 | //httpsRefreshInterval : 12,
153 |
154 | /** The following property can be used to cause insecure HTTP connections to
155 | * be redirected to HTTPS.
156 | */
157 | //requireHttps: true,
158 |
159 | /** To password protect the node-defined HTTP endpoints (httpNodeRoot),
160 | * including node-red-dashboard, or the static content (httpStatic), the
161 | * following properties can be used.
162 | * The `pass` field is a bcrypt hash of the password.
163 | * See http://nodered.org/docs/security.html#generating-the-password-hash
164 | */
165 | //httpNodeAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."},
166 | //httpStaticAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."},
167 |
168 | /*******************************************************************************
169 | * Server Settings
170 | * - uiPort
171 | * - uiHost
172 | * - apiMaxLength
173 | * - httpServerOptions
174 | * - httpAdminRoot
175 | * - httpAdminMiddleware
176 | * - httpNodeRoot
177 | * - httpNodeCors
178 | * - httpNodeMiddleware
179 | * - httpStatic
180 | ******************************************************************************/
181 |
182 | /** the tcp port that the Node-RED web server is listening on */
183 | uiPort: process.env.PORT || 1880,
184 |
185 | /** By default, the Node-RED UI accepts connections on all IPv4 interfaces.
186 | * To listen on all IPv6 addresses, set uiHost to "::",
187 | * The following property can be used to listen on a specific interface. For
188 | * example, the following would only allow connections from the local machine.
189 | */
190 | //uiHost: "127.0.0.1",
191 |
192 | /** The maximum size of HTTP request that will be accepted by the runtime api.
193 | * Default: 5mb
194 | */
195 | //apiMaxLength: '5mb',
196 |
197 | /** The following property can be used to pass custom options to the Express.js
198 | * server used by Node-RED. For a full list of available options, refer
199 | * to http://expressjs.com/en/api.html#app.settings.table
200 | */
201 | //httpServerOptions: { },
202 |
203 | /** By default, the Node-RED UI is available at http://localhost:1880/
204 | * The following property can be used to specify a different root path.
205 | * If set to false, this is disabled.
206 | */
207 | //httpAdminRoot: '/admin',
208 | // Move the admin UI
209 | httpAdminRoot: '/editor',
210 |
211 | /** The following property can be used to add a custom middleware function
212 | * in front of all admin http routes. For example, to set custom http
213 | * headers. It can be a single function or an array of middleware functions.
214 | */
215 | // httpAdminMiddleware: function(req,res,next) {
216 | // // Set the X-Frame-Options header to limit where the editor
217 | // // can be embedded
218 | // //res.set('X-Frame-Options', 'sameorigin');
219 | // next();
220 | // },
221 |
222 | /** Some nodes, such as HTTP In, can be used to listen for incoming http requests.
223 | * By default, these are served relative to '/'. The following property
224 | * can be used to specifiy a different root path. If set to false, this is
225 | * disabled.
226 | */
227 | //httpNodeRoot: '/red-nodes',
228 |
229 | /** The following property can be used to configure cross-origin resource sharing
230 | * in the HTTP nodes.
231 | * See https://github.com/troygoode/node-cors#configuration-options for
232 | * details on its contents. The following is a basic permissive set of options:
233 | */
234 | //httpNodeCors: {
235 | // origin: "*",
236 | // methods: "GET,PUT,POST,DELETE"
237 | //},
238 | httpNodeCors: {
239 | origin: "*",
240 | methods: "GET,PUT,POST,DELETE"
241 | },
242 |
243 | /** If you need to set an http proxy please set an environment variable
244 | * called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system.
245 | * For example - http_proxy=http://myproxy.com:8080
246 | * (Setting it here will have no effect)
247 | * You may also specify no_proxy (or NO_PROXY) to supply a comma separated
248 | * list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk
249 | */
250 |
251 | /** The following property can be used to add a custom middleware function
252 | * in front of all http in nodes. This allows custom authentication to be
253 | * applied to all http in nodes, or any other sort of common request processing.
254 | * It can be a single function or an array of middleware functions.
255 | */
256 | //httpNodeMiddleware: function(req,res,next) {
257 | // // Handle/reject the request, or pass it on to the http in node by calling next();
258 | // // Optionally skip our rawBodyParser by setting this to true;
259 | // //req.skipRawBodyParser = true;
260 | // next();
261 | //},
262 |
263 | /** When httpAdminRoot is used to move the UI to a different root path, the
264 | * following property can be used to identify a directory of static content
265 | * that should be served at http://localhost:1880/.
266 | */
267 | //httpStatic: '/home/nol/node-red-static/',
268 | // Serve up the welcome page
269 | httpStatic: path.join(__dirname, "public"),
270 |
271 | /*******************************************************************************
272 | * Runtime Settings
273 | * - lang
274 | * - logging
275 | * - contextStorage
276 | * - exportGlobalContextKeys
277 | * - externalModules
278 | ******************************************************************************/
279 |
280 | /** Uncomment the following to run node-red in your preferred language.
281 | * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko
282 | * Some languages are more complete than others.
283 | */
284 | // lang: "de",
285 |
286 | /** Configure the logging output */
287 | logging: {
288 | /** Only console logging is currently supported */
289 | console: {
290 | /** Level of logging to be recorded. Options are:
291 | * fatal - only those errors which make the application unusable should be recorded
292 | * error - record errors which are deemed fatal for a particular request + fatal errors
293 | * warn - record problems which are non fatal + errors + fatal errors
294 | * info - record information about the general running of the application + warn + error + fatal errors
295 | * debug - record information which is more verbose than info + info + warn + error + fatal errors
296 | * trace - record very detailed logging + debug + info + warn + error + fatal errors
297 | * off - turn off all logging (doesn't affect metrics or audit)
298 | */
299 | //level: "info",
300 | /** Whether or not to include metric events in the log output */
301 | //metrics: false,
302 | /** Whether or not to include audit events in the log output */
303 | //audit: false
304 | }
305 | },
306 |
307 | /** Context Storage
308 | * The following property can be used to enable context storage. The configuration
309 | * provided here will enable file-based context that flushes to disk every 30 seconds.
310 | * Refer to the documentation for further options: https://nodered.org/docs/api/context/
311 | */
312 | //contextStorage: {
313 | // default: {
314 | // module:"localfilesystem"
315 | // },
316 | //},
317 |
318 | /** `global.keys()` returns a list of all properties set in global context.
319 | * This allows them to be displayed in the Context Sidebar within the editor.
320 | * In some circumstances it is not desirable to expose them to the editor. The
321 | * following property can be used to hide any property set in `functionGlobalContext`
322 | * from being list by `global.keys()`.
323 | * By default, the property is set to false to avoid accidental exposure of
324 | * their values. Setting this to true will cause the keys to be listed.
325 | */
326 | //exportGlobalContextKeys: false,
327 | exportGlobalContextKeys: true,
328 |
329 | /** Configure how the runtime will handle external npm modules.
330 | * This covers:
331 | * - whether the editor will allow new node modules to be installed
332 | * - whether nodes, such as the Function node are allowed to have their
333 | * own dynamically configured dependencies.
334 | * The allow/denyList options can be used to limit what modules the runtime
335 | * will install/load. It can use '*' as a wildcard that matches anything.
336 | */
337 | externalModules: {
338 | // autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */
339 | // Enable module reinstalls on start-up; this ensures modules installed post-deploy are restored after a restage
340 | autoInstall: true,
341 | // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */
342 | // palette: { /** Configuration for the Palette Manager */
343 | // allowInstall: true, /** Enable the Palette Manager in the editor */
344 | // allowUpdate: true, /** Allow modules to be updated in the Palette Manager */
345 | // allowUpload: true, /** Allow module tgz files to be uploaded and installed */
346 | // allowList: ['*'],
347 | // denyList: [],
348 | // allowUpdateList: ['*'],
349 | // denyUpdateList: []
350 | // },
351 | // modules: { /** Configuration for node-specified modules */
352 | // allowInstall: true,
353 | // allowList: [],
354 | // denyList: []
355 | // }
356 | },
357 |
358 |
359 | /*******************************************************************************
360 | * Editor Settings
361 | * - disableEditor
362 | * - editorTheme
363 | ******************************************************************************/
364 |
365 | /** The following property can be used to disable the editor. The admin API
366 | * is not affected by this option. To disable both the editor and the admin
367 | * API, use either the httpRoot or httpAdminRoot properties
368 | */
369 | //disableEditor: false,
370 |
371 | /** Customising the editor
372 | * See https://nodered.org/docs/user-guide/runtime/configuration#editor-themes
373 | * for all available options.
374 | */
375 | editorTheme: {
376 | /** The following property can be used to set a custom theme for the editor.
377 | * See https://github.com/node-red-contrib-themes/theme-collection for
378 | * a collection of themes to chose from.
379 | */
380 | //theme: "",
381 | palette: {
382 | /** The following property can be used to order the categories in the editor
383 | * palette. If a node's category is not in the list, the category will get
384 | * added to the end of the palette.
385 | * If not set, the following default order is used:
386 | */
387 | //categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'],
388 | },
389 | projects: {
390 | /** To enable the Projects feature, set this value to true */
391 | enabled: false,
392 | workflow: {
393 | /** Set the default projects workflow mode.
394 | * - manual - you must manually commit changes
395 | * - auto - changes are automatically committed
396 | * This can be overridden per-user from the 'Git config'
397 | * section of 'User Settings' within the editor
398 | */
399 | mode: "manual"
400 | }
401 | },
402 | codeEditor: {
403 | /** Select the text editor component used by the editor.
404 | * Defaults to "ace", but can be set to "ace" or "monaco"
405 | */
406 | //lib: "ace",
407 | lib: "monaco",
408 | options: {
409 | /** The follow options only apply if the editor is set to "monaco"
410 | *
411 | * theme - must match the file name of a theme in
412 | * packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/theme
413 | * e.g. "tomorrow-night", "upstream-sunburst", "github", "my-theme"
414 | */
415 | theme: "vs",
416 | /** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc.
417 | * for the full list, see https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandaloneeditorconstructionoptions.html
418 | */
419 | //fontSize: 14,
420 | //fontFamily: "Cascadia Code, Fira Code, Consolas, 'Courier New', monospace",
421 | //fontLigatures: true,
422 | }
423 | }
424 | },
425 |
426 | /*******************************************************************************
427 | * Node Settings
428 | * - fileWorkingDirectory
429 | * - functionGlobalContext
430 | * - functionExternalModules
431 | * - nodeMessageBufferMaxLength
432 | * - ui (for use with Node-RED Dashboard)
433 | * - debugUseColors
434 | * - debugMaxLength
435 | * - execMaxBufferSize
436 | * - httpRequestTimeout
437 | * - mqttReconnectTime
438 | * - serialReconnectTime
439 | * - socketReconnectTime
440 | * - socketTimeout
441 | * - tcpMsgQueueSize
442 | * - inboundWebSocketTimeout
443 | * - tlsConfigDisableLocalFiles
444 | * - webSocketNodeVerifyClient
445 | ******************************************************************************/
446 |
447 | /** The working directory to handle relative file paths from within the File nodes
448 | * defaults to the working directory of the Node-RED process.
449 | */
450 | //fileWorkingDirectory: "",
451 |
452 | /** Allow the Function node to load additional npm modules directly */
453 | functionExternalModules: true,
454 |
455 | /** The following property can be used to set predefined values in Global Context.
456 | * This allows extra node modules to be made available with in Function node.
457 | * For example, the following:
458 | * functionGlobalContext: { os:require('os') }
459 | * will allow the `os` module to be accessed in a Function node using:
460 | * global.get("os")
461 | */
462 | functionGlobalContext: {
463 | // os:require('os'),
464 | },
465 |
466 | /** The maximum number of messages nodes will buffer internally as part of their
467 | * operation. This applies across a range of nodes that operate on message sequences.
468 | * defaults to no limit. A value of 0 also means no limit is applied.
469 | */
470 | //nodeMessageBufferMaxLength: 0,
471 |
472 | /** If you installed the optional node-red-dashboard you can set it's path
473 | * relative to httpNodeRoot
474 | * Other optional properties include
475 | * readOnly:{boolean},
476 | * middleware:{function or array}, (req,res,next) - http middleware
477 | * ioMiddleware:{function or array}, (socket,next) - socket.io middleware
478 | */
479 | //ui: { path: "ui" },
480 | // Move the dashboard UI
481 | ui: { path: "/ui" },
482 |
483 | /** Colourise the console output of the debug node */
484 | //debugUseColors: true,
485 |
486 | /** The maximum length, in characters, of any message sent to the debug sidebar tab */
487 | // debugMaxLength: 1000,
488 | debugMaxLength: 10000000,
489 |
490 | /** Maximum buffer size for the exec node. Defaults to 10Mb */
491 | //execMaxBufferSize: 10000000,
492 |
493 | /** Timeout in milliseconds for HTTP request connections. Defaults to 120s */
494 | //httpRequestTimeout: 120000,
495 |
496 | /** Retry time in milliseconds for MQTT connections */
497 | mqttReconnectTime: 15000,
498 |
499 | /** Retry time in milliseconds for Serial port connections */
500 | serialReconnectTime: 15000,
501 |
502 | /** Retry time in milliseconds for TCP socket connections */
503 | //socketReconnectTime: 10000,
504 |
505 | /** Timeout in milliseconds for TCP server socket connections. Defaults to no timeout */
506 | //socketTimeout: 120000,
507 |
508 | /** Maximum number of messages to wait in queue while attempting to connect to TCP socket
509 | * defaults to 1000
510 | */
511 | //tcpMsgQueueSize: 2000,
512 |
513 | /** Timeout in milliseconds for inbound WebSocket connections that do not
514 | * match any configured node. Defaults to 5000
515 | */
516 | //inboundWebSocketTimeout: 5000,
517 |
518 | /** To disable the option for using local files for storing keys and
519 | * certificates in the TLS configuration node, set this to true.
520 | */
521 | //tlsConfigDisableLocalFiles: true,
522 |
523 | /** The following property can be used to verify websocket connection attempts.
524 | * This allows, for example, the HTTP request headers to be checked to ensure
525 | * they include valid authentication information.
526 | */
527 | //webSocketNodeVerifyClient: function(info) {
528 | // /** 'info' has three properties:
529 | // * - origin : the value in the Origin header
530 | // * - req : the HTTP request
531 | // * - secure : true if req.connection.authorized or req.connection.encrypted is set
532 | // *
533 | // * The function should return true if the connection should be accepted, false otherwise.
534 | // *
535 | // * Alternatively, if this function is defined to accept a second argument, callback,
536 | // * it can be used to verify the client asynchronously.
537 | // * The callback takes three arguments:
538 | // * - result : boolean, whether to accept the connection or not
539 | // * - code : if result is false, the HTTP error status to return
540 | // * - reason: if result is false, the HTTP reason string to return
541 | // */
542 | //},
543 | }
544 |
545 | if (process.env.NODE_RED_USERNAME && process.env.NODE_RED_PASSWORD) {
546 | settings.adminAuth = {
547 | type: "credentials",
548 | users: function (username) {
549 | if (process.env.NODE_RED_USERNAME == username) {
550 | return when.resolve({ username: username, permissions: "*" });
551 | } else {
552 | return when.resolve(null);
553 | }
554 | },
555 | authenticate: function (username, password) {
556 | if (process.env.NODE_RED_USERNAME == username &&
557 | process.env.NODE_RED_PASSWORD == password) {
558 | return when.resolve({ username: username, permissions: "*" });
559 | } else {
560 | return when.resolve(null);
561 | }
562 | }
563 | }
564 | }
565 |
--------------------------------------------------------------------------------
/utils/file-explorer-flow.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "1f0274a4dfdeb367",
4 | "type": "tab",
5 | "label": "FS",
6 | "disabled": false,
7 | "info": ""
8 | },
9 | {
10 | "id": "53035f704bb7e6be",
11 | "type": "inject",
12 | "z": "1f0274a4dfdeb367",
13 | "name": "Init",
14 | "props": [
15 | {
16 | "p": "payload"
17 | },
18 | {
19 | "p": "topic",
20 | "vt": "str"
21 | }
22 | ],
23 | "repeat": "",
24 | "crontab": "",
25 | "once": true,
26 | "onceDelay": 0.1,
27 | "topic": "change",
28 | "payload": "/app",
29 | "payloadType": "str",
30 | "x": 90,
31 | "y": 340,
32 | "wires": [
33 | [
34 | "be8f219082382d87",
35 | "85090a46f8fa54c4"
36 | ]
37 | ]
38 | },
39 | {
40 | "id": "ced80261c7808b0f",
41 | "type": "ui_button",
42 | "z": "1f0274a4dfdeb367",
43 | "name": "",
44 | "group": "160e81fb.f1c86e",
45 | "order": 5,
46 | "width": 2,
47 | "height": 1,
48 | "passthru": true,
49 | "label": "Refresh",
50 | "tooltip": "",
51 | "color": "",
52 | "bgcolor": "",
53 | "className": "",
54 | "icon": "refresh",
55 | "payload": "",
56 | "payloadType": "str",
57 | "topic": "refresh",
58 | "topicType": "str",
59 | "x": 80,
60 | "y": 160,
61 | "wires": [
62 | [
63 | "be8f219082382d87"
64 | ]
65 | ]
66 | },
67 | {
68 | "id": "31aa125dbda0cefa",
69 | "type": "ui_button",
70 | "z": "1f0274a4dfdeb367",
71 | "name": "",
72 | "group": "160e81fb.f1c86e",
73 | "order": 4,
74 | "width": 2,
75 | "height": 1,
76 | "passthru": false,
77 | "label": "Delete",
78 | "tooltip": "",
79 | "color": "",
80 | "bgcolor": "",
81 | "icon": "delete",
82 | "payload": "",
83 | "payloadType": "str",
84 | "topic": "",
85 | "x": 70,
86 | "y": 400,
87 | "wires": [
88 | [
89 | "f7cd5aa1b1cc81c8"
90 | ]
91 | ]
92 | },
93 | {
94 | "id": "f7cd5aa1b1cc81c8",
95 | "type": "function",
96 | "z": "1f0274a4dfdeb367",
97 | "name": "Get file",
98 | "func": "// Get the filename from the flow context\nlet filename = flow.get(\"fileselected\");\n\n// check, if the filename is undefined that means it does not exist yet, nothing is selected yet\n// return: do not output anything\nif (filename === undefined) {\n return;\n}\n\n// return the filename to the file-in node to delete\nmsg.filename = filename;\n\nif (msg.filename.replace(/^.*(\\\\|\\/|\\:)/, '')[0] !== \".\") {\n // Only do this if this is a file, we don't delete folders\n return msg;\n}",
99 | "outputs": 1,
100 | "noerr": 0,
101 | "initialize": "",
102 | "finalize": "",
103 | "libs": [],
104 | "x": 280,
105 | "y": 400,
106 | "wires": [
107 | [
108 | "0b135f82c4167c37"
109 | ]
110 | ]
111 | },
112 | {
113 | "id": "9abed70c1a84e163",
114 | "type": "file",
115 | "z": "1f0274a4dfdeb367",
116 | "name": "Delete file",
117 | "filename": "",
118 | "appendNewline": true,
119 | "createDir": false,
120 | "overwriteFile": "delete",
121 | "encoding": "none",
122 | "x": 940,
123 | "y": 400,
124 | "wires": [
125 | []
126 | ]
127 | },
128 | {
129 | "id": "6d1fb9900e550934",
130 | "type": "http in",
131 | "z": "1f0274a4dfdeb367",
132 | "name": "",
133 | "url": "/download",
134 | "method": "get",
135 | "upload": false,
136 | "swaggerDoc": "",
137 | "x": 100,
138 | "y": 520,
139 | "wires": [
140 | [
141 | "f4845b5f01b3459e"
142 | ]
143 | ]
144 | },
145 | {
146 | "id": "4426891007434b2f",
147 | "type": "http response",
148 | "z": "1f0274a4dfdeb367",
149 | "name": "",
150 | "statusCode": "",
151 | "headers": {},
152 | "x": 790,
153 | "y": 520,
154 | "wires": []
155 | },
156 | {
157 | "id": "f4845b5f01b3459e",
158 | "type": "function",
159 | "z": "1f0274a4dfdeb367",
160 | "name": "Extract file",
161 | "func": "msg.filename = msg.req.query.filename;\nmsg.contentdisposition = \"attachment; filename=\\\"\" + msg.req.query.filename.replace(/^.*(\\\\|\\/|\\:)/, '') + \"\\\"\";\nreturn msg;\n",
162 | "outputs": 1,
163 | "noerr": 0,
164 | "initialize": "",
165 | "finalize": "",
166 | "libs": [],
167 | "x": 290,
168 | "y": 520,
169 | "wires": [
170 | [
171 | "61f9b4a564ad6ffc"
172 | ]
173 | ],
174 | "outputLabels": [
175 | "Folder selected"
176 | ]
177 | },
178 | {
179 | "id": "61f9b4a564ad6ffc",
180 | "type": "file in",
181 | "z": "1f0274a4dfdeb367",
182 | "name": "",
183 | "filename": "",
184 | "format": "",
185 | "chunk": false,
186 | "sendError": false,
187 | "encoding": "none",
188 | "x": 440,
189 | "y": 520,
190 | "wires": [
191 | [
192 | "301011cc86b7bb5a"
193 | ]
194 | ]
195 | },
196 | {
197 | "id": "301011cc86b7bb5a",
198 | "type": "change",
199 | "z": "1f0274a4dfdeb367",
200 | "name": "set headers",
201 | "rules": [
202 | {
203 | "t": "set",
204 | "p": "headers",
205 | "pt": "msg",
206 | "to": "{}",
207 | "tot": "json"
208 | },
209 | {
210 | "t": "set",
211 | "p": "headers.content-type",
212 | "pt": "msg",
213 | "to": "text/csv",
214 | "tot": "str"
215 | },
216 | {
217 | "t": "set",
218 | "p": "headers.Content-Disposition",
219 | "pt": "msg",
220 | "to": "contentdisposition",
221 | "tot": "msg"
222 | }
223 | ],
224 | "action": "",
225 | "property": "",
226 | "from": "",
227 | "to": "",
228 | "reg": false,
229 | "x": 630,
230 | "y": 520,
231 | "wires": [
232 | [
233 | "4426891007434b2f"
234 | ]
235 | ]
236 | },
237 | {
238 | "id": "0c138e847437e572",
239 | "type": "ui_button",
240 | "z": "1f0274a4dfdeb367",
241 | "name": "",
242 | "group": "160e81fb.f1c86e",
243 | "order": 6,
244 | "width": 2,
245 | "height": 1,
246 | "passthru": false,
247 | "label": "Graph",
248 | "tooltip": "",
249 | "color": "",
250 | "bgcolor": "",
251 | "icon": "show_chart",
252 | "payload": "",
253 | "payloadType": "str",
254 | "topic": "",
255 | "topicType": "str",
256 | "x": 70,
257 | "y": 460,
258 | "wires": [
259 | [
260 | "49f1528d3aa277ab"
261 | ]
262 | ]
263 | },
264 | {
265 | "id": "49f1528d3aa277ab",
266 | "type": "function",
267 | "z": "1f0274a4dfdeb367",
268 | "name": "Get file",
269 | "func": "// Get the filename from the flow context\nlet filename = flow.get(\"fileselected\");\n\n// check, if the filename is undefined that means it does not exist yet, nothing is selected yet\n// return: do not output anything\nif (filename === undefined) {\n return;\n}\n\n// return the filename to the file-in node to delete\nmsg.filename = filename;\n\nif (msg.filename.replace(/^.*(\\\\|\\/|\\:)/, '')[0] !== \".\") {\n // Only do this if this is a file, we don't delete folders\n return msg;\n}",
270 | "outputs": 1,
271 | "noerr": 0,
272 | "initialize": "",
273 | "finalize": "",
274 | "libs": [],
275 | "x": 280,
276 | "y": 460,
277 | "wires": [
278 | [
279 | "e04a6b965975de4c"
280 | ]
281 | ]
282 | },
283 | {
284 | "id": "e04a6b965975de4c",
285 | "type": "file in",
286 | "z": "1f0274a4dfdeb367",
287 | "name": "",
288 | "filename": "",
289 | "format": "utf8",
290 | "chunk": false,
291 | "sendError": false,
292 | "encoding": "none",
293 | "x": 440,
294 | "y": 460,
295 | "wires": [
296 | [
297 | "3d4282e9e0897475"
298 | ]
299 | ]
300 | },
301 | {
302 | "id": "3d4282e9e0897475",
303 | "type": "csv",
304 | "z": "1f0274a4dfdeb367",
305 | "name": "",
306 | "sep": ",",
307 | "hdrin": true,
308 | "hdrout": "",
309 | "multi": "mult",
310 | "ret": "\\n",
311 | "temp": "",
312 | "skip": "0",
313 | "strings": true,
314 | "include_empty_strings": false,
315 | "include_null_values": false,
316 | "x": 610,
317 | "y": 460,
318 | "wires": [
319 | [
320 | "ac74a4bce6617ee6"
321 | ]
322 | ]
323 | },
324 | {
325 | "id": "ac74a4bce6617ee6",
326 | "type": "function",
327 | "z": "1f0274a4dfdeb367",
328 | "name": "Plot",
329 | "func": "var chart = [{\n \"series\":[],\n \"data\":[],\n \"labels\":[msg.filename]\n}];\n\n\n/*\nvar pressure = [];\nvar out2 = [];\n\nfor (var i=0; i[\") + msg.payload[i].name.replace(/^.*(\\\\|\\/|\\:)/, '') + (msg.payload[i].stat.isFile ? \"\" : \"]\");\n// msg.payload[i].link = msg.payload[i].stat.isDirectory ? \"\" : \"/download?filename=\" + msg.payload[i].name;\n msg.payload[i].link = msg.payload[i].stat.isDirectory ? \"\" : \"\";\n}\nreturn msg;\n",
814 | "outputs": 1,
815 | "noerr": 0,
816 | "initialize": "",
817 | "finalize": "",
818 | "libs": [],
819 | "x": 430,
820 | "y": 160,
821 | "wires": [
822 | [
823 | "6479c9674ee4c649"
824 | ]
825 | ]
826 | },
827 | {
828 | "id": "ca2e646fd2bfb668",
829 | "type": "ui_table",
830 | "z": "1f0274a4dfdeb367",
831 | "group": "160e81fb.f1c86e",
832 | "name": "",
833 | "order": 10,
834 | "width": 16,
835 | "height": "10",
836 | "columns": [
837 | {
838 | "field": "fname",
839 | "title": "Name",
840 | "width": "55%",
841 | "align": "left",
842 | "formatter": "html",
843 | "formatterParams": {
844 | "target": "_blank"
845 | }
846 | },
847 | {
848 | "field": "stat.size",
849 | "title": "Size",
850 | "width": "",
851 | "align": "left",
852 | "formatter": "plaintext",
853 | "formatterParams": {
854 | "target": "_blank"
855 | }
856 | },
857 | {
858 | "field": "stat.created",
859 | "title": "Created",
860 | "width": "15%",
861 | "align": "left",
862 | "formatter": "plaintext",
863 | "formatterParams": {
864 | "target": "_blank"
865 | }
866 | },
867 | {
868 | "field": "stat.changed",
869 | "title": "Changed",
870 | "width": "15%",
871 | "align": "left",
872 | "formatter": "plaintext",
873 | "formatterParams": {
874 | "target": "_blank"
875 | }
876 | },
877 | {
878 | "field": "link",
879 | "title": "",
880 | "width": "1%",
881 | "align": "left",
882 | "formatter": "html",
883 | "formatterParams": {
884 | "target": "_blank"
885 | }
886 | }
887 | ],
888 | "outputs": 1,
889 | "cts": true,
890 | "x": 450,
891 | "y": 40,
892 | "wires": [
893 | [
894 | "9d3c9ab668d4d29d",
895 | "05802030cf171e1a"
896 | ]
897 | ]
898 | },
899 | {
900 | "id": "965cd37574ac4720",
901 | "type": "debug",
902 | "z": "1f0274a4dfdeb367",
903 | "name": "",
904 | "active": true,
905 | "tosidebar": true,
906 | "console": false,
907 | "tostatus": false,
908 | "complete": "true",
909 | "targetType": "full",
910 | "statusVal": "",
911 | "statusType": "auto",
912 | "x": 1210,
913 | "y": 560,
914 | "wires": []
915 | },
916 | {
917 | "id": "6479c9674ee4c649",
918 | "type": "sort",
919 | "z": "1f0274a4dfdeb367",
920 | "name": "",
921 | "order": "ascending",
922 | "as_num": false,
923 | "target": "payload",
924 | "targetType": "msg",
925 | "msgKey": "$lowercase(fname)\t",
926 | "msgKeyType": "jsonata",
927 | "seqKey": "payload",
928 | "seqKeyType": "msg",
929 | "x": 290,
930 | "y": 100,
931 | "wires": [
932 | [
933 | "ca2e646fd2bfb668",
934 | "6de7a91d24be29e1"
935 | ]
936 | ]
937 | },
938 | {
939 | "id": "9d3c9ab668d4d29d",
940 | "type": "switch",
941 | "z": "1f0274a4dfdeb367",
942 | "name": "is file?",
943 | "property": "payload.stat.isFile",
944 | "propertyType": "msg",
945 | "rules": [
946 | {
947 | "t": "true"
948 | },
949 | {
950 | "t": "false"
951 | }
952 | ],
953 | "checkall": "true",
954 | "repair": false,
955 | "outputs": 2,
956 | "x": 610,
957 | "y": 100,
958 | "wires": [
959 | [
960 | "5f1be4ecc1b1bfe1"
961 | ],
962 | [
963 | "8acb2b7c94f075e9"
964 | ]
965 | ]
966 | },
967 | {
968 | "id": "0dbef6831f2ca330",
969 | "type": "yaml",
970 | "z": "1f0274a4dfdeb367",
971 | "property": "payload",
972 | "name": "",
973 | "x": 790,
974 | "y": 40,
975 | "wires": [
976 | [
977 | "ffa3ba810c0b280e"
978 | ]
979 | ]
980 | },
981 | {
982 | "id": "ffa3ba810c0b280e",
983 | "type": "ui_template",
984 | "z": "1f0274a4dfdeb367",
985 | "group": "26e8fe8026c92abb",
986 | "name": "Properties",
987 | "order": 1,
988 | "width": 6,
989 | "height": "7",
990 | "format": "",
991 | "storeOutMessages": true,
992 | "fwdInMessages": true,
993 | "resendOnRefresh": true,
994 | "templateScope": "local",
995 | "className": "",
996 | "x": 930,
997 | "y": 40,
998 | "wires": [
999 | []
1000 | ]
1001 | },
1002 | {
1003 | "id": "38da03d9810a48c7",
1004 | "type": "inject",
1005 | "z": "1f0274a4dfdeb367",
1006 | "name": "selectable",
1007 | "props": [
1008 | {
1009 | "p": "payload"
1010 | },
1011 | {
1012 | "p": "ui_control",
1013 | "v": "{\"tabulator\":{\"selectable\":1}}",
1014 | "vt": "json"
1015 | }
1016 | ],
1017 | "repeat": "",
1018 | "crontab": "",
1019 | "once": true,
1020 | "onceDelay": 0.1,
1021 | "topic": "",
1022 | "payload": "{}",
1023 | "payloadType": "json",
1024 | "x": 110,
1025 | "y": 40,
1026 | "wires": [
1027 | [
1028 | "ca2e646fd2bfb668"
1029 | ]
1030 | ]
1031 | },
1032 | {
1033 | "id": "bdd0d955d05693da",
1034 | "type": "file in",
1035 | "z": "1f0274a4dfdeb367",
1036 | "name": "",
1037 | "filename": "",
1038 | "format": "utf8",
1039 | "chunk": false,
1040 | "sendError": false,
1041 | "encoding": "none",
1042 | "allProps": false,
1043 | "x": 800,
1044 | "y": 160,
1045 | "wires": [
1046 | [
1047 | "863f5a110f457b23"
1048 | ]
1049 | ]
1050 | },
1051 | {
1052 | "id": "863f5a110f457b23",
1053 | "type": "ui_template",
1054 | "z": "1f0274a4dfdeb367",
1055 | "group": "4128a70510a0cd4e",
1056 | "name": "Preview",
1057 | "order": 1,
1058 | "width": 6,
1059 | "height": "11",
1060 | "format": "",
1061 | "storeOutMessages": true,
1062 | "fwdInMessages": true,
1063 | "resendOnRefresh": true,
1064 | "templateScope": "local",
1065 | "className": "",
1066 | "x": 940,
1067 | "y": 160,
1068 | "wires": [
1069 | []
1070 | ]
1071 | },
1072 | {
1073 | "id": "05802030cf171e1a",
1074 | "type": "change",
1075 | "z": "1f0274a4dfdeb367",
1076 | "name": "clean",
1077 | "rules": [
1078 | {
1079 | "t": "delete",
1080 | "p": "payload.link",
1081 | "pt": "msg"
1082 | },
1083 | {
1084 | "t": "delete",
1085 | "p": "payload.fname",
1086 | "pt": "msg"
1087 | }
1088 | ],
1089 | "action": "",
1090 | "property": "",
1091 | "from": "",
1092 | "to": "",
1093 | "reg": false,
1094 | "x": 610,
1095 | "y": 40,
1096 | "wires": [
1097 | [
1098 | "0dbef6831f2ca330"
1099 | ]
1100 | ]
1101 | },
1102 | {
1103 | "id": "83fba8629d1025e7",
1104 | "type": "ui_chart",
1105 | "z": "1f0274a4dfdeb367",
1106 | "name": "",
1107 | "group": "26e8fe8026c92abb",
1108 | "order": 2,
1109 | "width": 6,
1110 | "height": "4",
1111 | "label": "File sizes",
1112 | "chartType": "line",
1113 | "legend": "false",
1114 | "xformat": "HH:mm:ss",
1115 | "interpolate": "linear",
1116 | "nodata": "",
1117 | "dot": false,
1118 | "ymin": "",
1119 | "ymax": "",
1120 | "removeOlder": "4",
1121 | "removeOlderPoints": "",
1122 | "removeOlderUnit": "3600",
1123 | "cutout": 0,
1124 | "useOneColor": false,
1125 | "useUTC": false,
1126 | "colors": [
1127 | "#1f77b4",
1128 | "#aec7e8",
1129 | "#ff7f0e",
1130 | "#2ca02c",
1131 | "#98df8a",
1132 | "#d62728",
1133 | "#ff9896",
1134 | "#9467bd",
1135 | "#c5b0d5"
1136 | ],
1137 | "outputs": 1,
1138 | "useDifferentColor": false,
1139 | "className": "",
1140 | "x": 620,
1141 | "y": 240,
1142 | "wires": [
1143 | []
1144 | ]
1145 | },
1146 | {
1147 | "id": "5be20cfc9f21ed75",
1148 | "type": "change",
1149 | "z": "1f0274a4dfdeb367",
1150 | "name": "graph",
1151 | "rules": [
1152 | {
1153 | "t": "set",
1154 | "p": "topic",
1155 | "pt": "msg",
1156 | "to": "payload.fname",
1157 | "tot": "msg"
1158 | },
1159 | {
1160 | "t": "move",
1161 | "p": "payload.stat.size",
1162 | "pt": "msg",
1163 | "to": "payload",
1164 | "tot": "msg"
1165 | }
1166 | ],
1167 | "action": "",
1168 | "property": "",
1169 | "from": "",
1170 | "to": "",
1171 | "reg": false,
1172 | "x": 610,
1173 | "y": 160,
1174 | "wires": [
1175 | [
1176 | "83fba8629d1025e7"
1177 | ]
1178 | ]
1179 | },
1180 | {
1181 | "id": "6de7a91d24be29e1",
1182 | "type": "split",
1183 | "z": "1f0274a4dfdeb367",
1184 | "name": "",
1185 | "splt": "\\n",
1186 | "spltType": "str",
1187 | "arraySplt": 1,
1188 | "arraySpltType": "len",
1189 | "stream": false,
1190 | "addname": "",
1191 | "x": 450,
1192 | "y": 100,
1193 | "wires": [
1194 | [
1195 | "5be20cfc9f21ed75"
1196 | ]
1197 | ]
1198 | },
1199 | {
1200 | "id": "65f1960c7832cc0c",
1201 | "type": "inject",
1202 | "z": "1f0274a4dfdeb367",
1203 | "name": "",
1204 | "props": [],
1205 | "repeat": "",
1206 | "crontab": "*/1 0-23 * * *",
1207 | "once": false,
1208 | "onceDelay": 0.1,
1209 | "topic": "",
1210 | "x": 90,
1211 | "y": 100,
1212 | "wires": [
1213 | [
1214 | "ced80261c7808b0f"
1215 | ]
1216 | ]
1217 | },
1218 | {
1219 | "id": "160e81fb.f1c86e",
1220 | "type": "ui_group",
1221 | "name": "File Browser",
1222 | "tab": "b63d1f91.68095",
1223 | "order": 1,
1224 | "disp": false,
1225 | "width": "16",
1226 | "collapse": false,
1227 | "className": ""
1228 | },
1229 | {
1230 | "id": "ddb72df8452ba7d7",
1231 | "type": "ui_group",
1232 | "name": "Desktop",
1233 | "tab": "b63d1f91.68095",
1234 | "order": 4,
1235 | "disp": true,
1236 | "width": 24,
1237 | "collapse": false
1238 | },
1239 | {
1240 | "id": "af2fe563cfd2cfa6",
1241 | "type": "ui_group",
1242 | "name": "Mobile",
1243 | "tab": "b63d1f91.68095",
1244 | "order": 5,
1245 | "disp": true,
1246 | "width": "6",
1247 | "collapse": false
1248 | },
1249 | {
1250 | "id": "26e8fe8026c92abb",
1251 | "type": "ui_group",
1252 | "name": "Properties",
1253 | "tab": "b63d1f91.68095",
1254 | "order": 2,
1255 | "disp": true,
1256 | "width": "6",
1257 | "collapse": false,
1258 | "className": ""
1259 | },
1260 | {
1261 | "id": "4128a70510a0cd4e",
1262 | "type": "ui_group",
1263 | "name": "Preview",
1264 | "tab": "b63d1f91.68095",
1265 | "order": 3,
1266 | "disp": true,
1267 | "width": "6",
1268 | "collapse": false,
1269 | "className": ""
1270 | },
1271 | {
1272 | "id": "b63d1f91.68095",
1273 | "type": "ui_tab",
1274 | "name": "Files",
1275 | "icon": "folder_open",
1276 | "disabled": false,
1277 | "hidden": false
1278 | }
1279 | ]
1280 |
--------------------------------------------------------------------------------
/utils/save-all-changes-flow.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "3c1975f87cd0cabf",
4 | "type": "tab",
5 | "label": "Save all changes ",
6 | "disabled": false,
7 | "info": "",
8 | "env": []
9 | },
10 | {
11 | "id": "9c09e087.a48e7",
12 | "type": "http request",
13 | "z": "3c1975f87cd0cabf",
14 | "name": "PUT",
15 | "method": "PUT",
16 | "ret": "obj",
17 | "paytoqs": "ignore",
18 | "url": "https://api.github.com/repos/{{gitUsername}}/{{gitRepo}}/contents/{{filename}}",
19 | "tls": "",
20 | "persist": false,
21 | "proxy": "",
22 | "authType": "",
23 | "senderr": false,
24 | "x": 590,
25 | "y": 280,
26 | "wires": [
27 | [
28 | "666e944b12fe9cb2"
29 | ]
30 | ]
31 | },
32 | {
33 | "id": "1dc8605f.7ac4e",
34 | "type": "change",
35 | "z": "3c1975f87cd0cabf",
36 | "name": "set headers",
37 | "rules": [
38 | {
39 | "t": "set",
40 | "p": "headers",
41 | "pt": "msg",
42 | "to": "{\"User-Agent\":\"PostmanRuntime/7.29.0\"}",
43 | "tot": "json"
44 | },
45 | {
46 | "t": "set",
47 | "p": "headers.Authorization",
48 | "pt": "msg",
49 | "to": "Authorization",
50 | "tot": "msg"
51 | }
52 | ],
53 | "action": "",
54 | "property": "",
55 | "from": "",
56 | "to": "",
57 | "reg": false,
58 | "x": 870,
59 | "y": 220,
60 | "wires": [
61 | [
62 | "9c09e087.a48e7"
63 | ]
64 | ]
65 | },
66 | {
67 | "id": "ba9ea225.c2fbe",
68 | "type": "http request",
69 | "z": "3c1975f87cd0cabf",
70 | "name": "GET",
71 | "method": "GET",
72 | "ret": "obj",
73 | "paytoqs": "ignore",
74 | "url": "https://api.github.com/repos/{{gitUsername}}/{{gitRepo}}/contents/{{filename}}",
75 | "tls": "",
76 | "persist": false,
77 | "proxy": "",
78 | "authType": "",
79 | "senderr": false,
80 | "x": 590,
81 | "y": 100,
82 | "wires": [
83 | [
84 | "e585584fd416aab4",
85 | "666e944b12fe9cb2"
86 | ]
87 | ]
88 | },
89 | {
90 | "id": "ae8ddf10.868fd",
91 | "type": "change",
92 | "z": "3c1975f87cd0cabf",
93 | "name": "set msg vars",
94 | "rules": [
95 | {
96 | "t": "set",
97 | "p": "headers.User-Agent",
98 | "pt": "msg",
99 | "to": "PostmanRuntime/7.29.0",
100 | "tot": "str"
101 | },
102 | {
103 | "t": "set",
104 | "p": "headers.Authorization",
105 | "pt": "msg",
106 | "to": "Authorization",
107 | "tot": "msg"
108 | },
109 | {
110 | "t": "set",
111 | "p": "gitRepo",
112 | "pt": "msg",
113 | "to": "GITHUB_REPO",
114 | "tot": "env"
115 | },
116 | {
117 | "t": "set",
118 | "p": "gitUsername",
119 | "pt": "msg",
120 | "to": "GITHUB_USERNAME",
121 | "tot": "env"
122 | }
123 | ],
124 | "action": "",
125 | "property": "",
126 | "from": "",
127 | "to": "",
128 | "reg": false,
129 | "x": 600,
130 | "y": 40,
131 | "wires": [
132 | [
133 | "ba9ea225.c2fbe"
134 | ]
135 | ]
136 | },
137 | {
138 | "id": "819e400b.96d61",
139 | "type": "comment",
140 | "z": "3c1975f87cd0cabf",
141 | "name": "GitHub Token",
142 | "info": "Set GITHUB_TOKEN in [Heroku Config Vars](https://dashboard.heroku.com/apps/nodered-on-cloud/settings).\nSee [Personal access tokens](https://github.com/settings/tokens) page (repo).",
143 | "x": 110,
144 | "y": 280,
145 | "wires": []
146 | },
147 | {
148 | "id": "f4f0342516f4afea",
149 | "type": "file in",
150 | "z": "3c1975f87cd0cabf",
151 | "name": "",
152 | "filename": "",
153 | "format": "utf8",
154 | "chunk": false,
155 | "sendError": false,
156 | "encoding": "none",
157 | "allProps": false,
158 | "x": 880,
159 | "y": 160,
160 | "wires": [
161 | [
162 | "6a2bc486f2838313"
163 | ]
164 | ]
165 | },
166 | {
167 | "id": "e585584fd416aab4",
168 | "type": "change",
169 | "z": "3c1975f87cd0cabf",
170 | "name": "",
171 | "rules": [
172 | {
173 | "t": "move",
174 | "p": "payload.sha",
175 | "pt": "msg",
176 | "to": "temp",
177 | "tot": "msg"
178 | }
179 | ],
180 | "action": "",
181 | "property": "",
182 | "from": "",
183 | "to": "",
184 | "reg": false,
185 | "x": 650,
186 | "y": 160,
187 | "wires": [
188 | [
189 | "f4f0342516f4afea"
190 | ]
191 | ]
192 | },
193 | {
194 | "id": "6a2bc486f2838313",
195 | "type": "change",
196 | "z": "3c1975f87cd0cabf",
197 | "name": "set msg.payload",
198 | "rules": [
199 | {
200 | "t": "set",
201 | "p": "payload",
202 | "pt": "msg",
203 | "to": "{\"content\": $base64encode(payload)}",
204 | "tot": "jsonata"
205 | },
206 | {
207 | "t": "set",
208 | "p": "payload.sha",
209 | "pt": "msg",
210 | "to": "temp",
211 | "tot": "msg"
212 | },
213 | {
214 | "t": "set",
215 | "p": "payload.message",
216 | "pt": "msg",
217 | "to": "Commit from nodered-on-cloud",
218 | "tot": "str"
219 | },
220 | {
221 | "t": "delete",
222 | "p": "temp",
223 | "pt": "msg"
224 | }
225 | ],
226 | "action": "",
227 | "property": "",
228 | "from": "",
229 | "to": "",
230 | "reg": false,
231 | "x": 620,
232 | "y": 220,
233 | "wires": [
234 | [
235 | "1dc8605f.7ac4e"
236 | ]
237 | ]
238 | },
239 | {
240 | "id": "f92e1e8dffe158f7",
241 | "type": "change",
242 | "z": "3c1975f87cd0cabf",
243 | "name": "flows.json",
244 | "rules": [
245 | {
246 | "t": "set",
247 | "p": "filename",
248 | "pt": "msg",
249 | "to": "flows.json",
250 | "tot": "str"
251 | }
252 | ],
253 | "action": "",
254 | "property": "",
255 | "from": "",
256 | "to": "",
257 | "reg": false,
258 | "x": 320,
259 | "y": 40,
260 | "wires": [
261 | [
262 | "ae8ddf10.868fd"
263 | ]
264 | ]
265 | },
266 | {
267 | "id": "98b5b9035834b822",
268 | "type": "change",
269 | "z": "3c1975f87cd0cabf",
270 | "name": "flows_cred.json",
271 | "rules": [
272 | {
273 | "t": "set",
274 | "p": "filename",
275 | "pt": "msg",
276 | "to": "flows_cred.json",
277 | "tot": "str"
278 | }
279 | ],
280 | "action": "",
281 | "property": "",
282 | "from": "",
283 | "to": "",
284 | "reg": false,
285 | "x": 340,
286 | "y": 140,
287 | "wires": [
288 | [
289 | "ae8ddf10.868fd"
290 | ]
291 | ]
292 | },
293 | {
294 | "id": "7a7584df89bbce4a",
295 | "type": "inject",
296 | "z": "3c1975f87cd0cabf",
297 | "name": "SAVE",
298 | "props": [],
299 | "repeat": "",
300 | "crontab": "",
301 | "once": false,
302 | "onceDelay": 0.1,
303 | "topic": "",
304 | "x": 110,
305 | "y": 40,
306 | "wires": [
307 | [
308 | "88f6fe95e0373dfa"
309 | ]
310 | ]
311 | },
312 | {
313 | "id": "71c40b7254660299",
314 | "type": "change",
315 | "z": "3c1975f87cd0cabf",
316 | "name": "package.json",
317 | "rules": [
318 | {
319 | "t": "set",
320 | "p": "filename",
321 | "pt": "msg",
322 | "to": "package.json",
323 | "tot": "str"
324 | }
325 | ],
326 | "action": "",
327 | "property": "",
328 | "from": "",
329 | "to": "",
330 | "reg": false,
331 | "x": 330,
332 | "y": 240,
333 | "wires": [
334 | [
335 | "ae8ddf10.868fd"
336 | ]
337 | ]
338 | },
339 | {
340 | "id": "666e944b12fe9cb2",
341 | "type": "debug",
342 | "z": "3c1975f87cd0cabf",
343 | "name": "",
344 | "active": true,
345 | "tosidebar": true,
346 | "console": false,
347 | "tostatus": false,
348 | "complete": "true",
349 | "targetType": "full",
350 | "statusVal": "",
351 | "statusType": "auto",
352 | "x": 810,
353 | "y": 420,
354 | "wires": []
355 | },
356 | {
357 | "id": "81154a985f4a1efe",
358 | "type": "inject",
359 | "z": "3c1975f87cd0cabf",
360 | "name": "",
361 | "props": [
362 | {
363 | "p": "payload"
364 | },
365 | {
366 | "p": "topic",
367 | "vt": "str"
368 | }
369 | ],
370 | "repeat": "",
371 | "crontab": "",
372 | "once": false,
373 | "onceDelay": 0.1,
374 | "topic": "",
375 | "payload": "",
376 | "payloadType": "date",
377 | "x": 460,
378 | "y": 360,
379 | "wires": [
380 | [
381 | "ae8ddf10.868fd"
382 | ]
383 | ]
384 | },
385 | {
386 | "id": "88f6fe95e0373dfa",
387 | "type": "function",
388 | "z": "3c1975f87cd0cabf",
389 | "name": "Authorization",
390 | "func": "msg.Authorization = 'Basic ' + Buffer.from(env.get('GITHUB_USERNAME') + \":\" + env.get('GITHUB_TOKEN')).toString('base64');\n\nreturn msg;",
391 | "outputs": 1,
392 | "noerr": 0,
393 | "initialize": "",
394 | "finalize": "",
395 | "libs": [],
396 | "x": 110,
397 | "y": 100,
398 | "wires": [
399 | [
400 | "f92e1e8dffe158f7",
401 | "98b5b9035834b822",
402 | "71c40b7254660299"
403 | ]
404 | ]
405 | }
406 | ]
407 |
--------------------------------------------------------------------------------