Compare commits

...

97 Commits

Author SHA1 Message Date
Dave Conway-Jones
93d5db9752 Redo Delete unused configs button for new dev layout 2018-09-26 21:43:16 +01:00
Nick O'Leary
d887ab126b Add --safe mode flag to allow starting without flows running 2018-09-25 11:20:50 +01:00
Nick O'Leary
fba339f666 Prevent github from suppressing diffs 2018-09-24 22:02:18 +01:00
Nick O'Leary
09d41a9708 Merge pull request #1896 from node-red/delay-flush
Delay node - add msg.flush option
2018-09-24 21:53:28 +01:00
Dave Conway-Jones
4630a162af Add msg.flush mode to delay node
to spew out contents rather than dump
(as per Trello item)
2018-09-23 17:31:11 +01:00
Nick O'Leary
80a15089b4 Add markdown-preview to expandable editor for ndoe descriptions 2018-09-21 17:21:04 +01:00
Nick O'Leary
8edf399631 Add set-package-version script to bump all version numbers 2018-09-21 14:27:56 +01:00
Nick O'Leary
d5ffd1432f Fix up version tests to handle tagged releases 2018-09-21 14:06:16 +01:00
Nick O'Leary
e73bf03615 Bump package to 0.20.0-alpha.0 to avoid accidental publish 2018-09-21 13:58:38 +01:00
Nick O'Leary
665fe0e01e Merge pull request #1890 from node-red/repackage
Restructure Node-RED into multiple modules
2018-09-21 13:50:31 +01:00
Nick O'Leary
a866aa9c18 Merge branch 'dev' into repackage 2018-09-21 11:47:34 +01:00
Nick O'Leary
d9089b798c Update info side bar with node description section 2018-09-21 11:38:48 +01:00
Nick O'Leary
d34ebd4d1b Fix i18n api changes in runtime/nodes 2018-09-18 10:31:44 +01:00
Nick O'Leary
2aae76c9bc Merge branch 'dev' into repackage 2018-09-17 14:49:11 +01:00
Nick O'Leary
5fc3ca0e23 Merge pull request #1884 from node-red-hitachi/dev-redirect-bugfix
Prohibit http-request node from sending cookies to the different domain when redirected
2018-09-17 14:15:18 +01:00
Dave Conway-Jones
5bb27109bf Config search (#1880)
* Let nodes be findable direct from config node info

* add link to search as <a> tag

* don't expose onclick in sidebar
2018-09-17 11:35:00 +01:00
Nick O'Leary
7406ab6017 Merge branch 'master' into dev 2018-09-17 11:33:36 +01:00
Nick O'Leary
f30ff7a2fd Redesign node edit dialog to tabbed style 2018-09-14 10:54:24 +01:00
Osamu Katada
62b2adab78 Bugfix. http-request node 2018-09-10 10:47:05 +09:00
Nick O'Leary
0529eed0c9 Merge pull request #1856 from kazuhitoyokoi/master-nodedescriptionproperty
Add node description property UI
2018-09-07 12:23:41 +01:00
Nick O'Leary
8c169dc82b Add 'restart flows' option to deploy menu 2018-09-06 15:27:01 +01:00
Nick O'Leary
195342f7db Fix grunt dev actions for json files 2018-09-06 15:16:43 +01:00
Nick O'Leary
cfaaef7860 Merge branch 'dev' into repackage 2018-09-06 10:31:36 +01:00
Nick O'Leary
e939d5e96e Merge branch 'master' into dev 2018-09-06 10:28:07 +01:00
Nick O'Leary
9f4628cf0a Merge pull request #1873 from node-red-hitachi/jp-fix-message
Fix message catalogue for projects
2018-09-04 21:59:26 +01:00
Nick O'Leary
ec4d24af91 Merge pull request #1872 from node-red-hitachi/uitest-mqtt
Add UI testing code for MQTT node
2018-09-04 21:58:29 +01:00
Nick O'Leary
51373f59e2 Merge branch 'dev' into repackage 2018-09-04 11:41:03 +01:00
Nick O'Leary
6cc56879d3 Merge branch 'master' into dev 2018-09-04 11:37:45 +01:00
Hiroyasu Nishiyama
da89460830 Update message catalogue for Projects 2018-09-04 15:08:43 +09:00
Yuma Matsuura
9e006d42bb Add UI testing code for mqtt node 2018-09-04 14:13:34 +09:00
Nick O'Leary
4c02bab4ee Merge branch 'dev' into repackage 2018-08-31 21:20:21 +01:00
Nick O'Leary
5800ed41f1 Handle expected closed status event in gpio tests 2018-08-31 21:19:26 +01:00
Nick O'Leary
18b5b4901f Merge branch 'dev' into repackage 2018-08-31 21:18:23 +01:00
Nick O'Leary
368418cf56 Merge branch 'master' into dev 2018-08-31 21:02:20 +01:00
Nick O'Leary
3e6cadf3d8 Merge branch 'master' into dev 2018-08-31 11:26:54 +01:00
Nick O'Leary
0c5a76b391 Add env.get to Function node 2018-08-30 22:42:30 +01:00
Nick O'Leary
bf1afcfe8a Update grunt release task for new package structure
The task still creates .dist/node-red-xyz.zip as an archive
of the full release. It no longer creates .dist/node-red-xyz/ as
a directory containing the to-be-published module. Instead
npm publish should be run in each /packages/node_modules/**
directory in turn.
2018-08-30 15:28:24 +01:00
Nick O'Leary
2980818f0d Add inter-module dependencies to package.json 2018-08-30 12:56:31 +01:00
Nick O'Leary
9da58dbaf0 Move index.mst out of npmignored src dir 2018-08-30 12:44:33 +01:00
Nick O'Leary
55d71659f8 Merge branch 'master' into repackage 2018-08-29 09:55:40 +01:00
Nick O'Leary
8d5b546763 Update module package versions 2018-08-28 13:57:04 +01:00
Nick O'Leary
19c9707d62 Get jdsoc tagging right for util module 2018-08-28 13:45:38 +01:00
Nick O'Leary
79e004a040 Merge branch 'master' into repackage 2018-08-24 14:13:09 +01:00
Nick O'Leary
48308db45b Rework jsdoc format and pull in jsdoc-nr-template 2018-08-24 13:02:06 +01:00
Nick O'Leary
3f37e96f78 Restore subflow category ui lost in the merge 2018-08-23 21:00:19 +01:00
Nick O'Leary
4e21a5e557 Start documenting apis 2018-08-22 10:00:03 +01:00
Nick O'Leary
19fa69811b Add LICENSE to each package 2018-08-21 13:57:59 +01:00
Nick O'Leary
0ddb4c625d Add README files to each package 2018-08-21 13:43:11 +01:00
Nick O'Leary
36dc1d2f97 Move .npmignore into editor-client package 2018-08-20 22:04:29 +01:00
Nick O'Leary
11fa2cb35d Fixup registry/lib/localfilesystem_spec after locales move 2018-08-20 21:22:47 +01:00
Nick O'Leary
546f07156f Move node locales and tidy up package.json files 2018-08-20 20:31:29 +01:00
Nick O'Leary
7e7117632d Fixup grunt docs task 2018-08-20 17:03:38 +01:00
Nick O'Leary
954226da0d Fix up promises in tests, remove package-lock 2018-08-20 16:45:50 +01:00
Nick O'Leary
38a1291c5b Fixup all the tests 2018-08-20 16:17:24 +01:00
Nick O'Leary
998bf92ad4 Move tests to reflect package structure 2018-08-19 11:28:03 +01:00
Nick O'Leary
974ba40f28 Add scripts/verify-package-dependencies.js 2018-08-19 00:44:17 +01:00
Nick O'Leary
e57d8ba0ef pull out editor-client and editor-api 2018-08-17 22:10:54 +01:00
Nick O'Leary
6b79c6135f Merge branch 'master' into repackage 2018-08-16 20:43:15 +01:00
Nick O'Leary
a747d8c2d5 Move core node icons into node package 2018-08-15 23:12:51 +01:00
Nick O'Leary
a3aec6b939 Merge branch 'master' into repackage 2018-08-15 20:46:56 +01:00
Kazuhito Yokoi
bba57f8d2b Add node description property UI 2018-08-06 21:39:37 +09:00
Nick O'Leary
4312a01707 remove editor/public 2018-08-04 22:30:41 +01:00
Nick O'Leary
ecd8f97d8b WIP: move all the code 2018-08-04 22:23:06 +01:00
Nick O'Leary
06abe63fb1 Merge branch 'master' into runtime-api 2018-07-30 10:13:51 +01:00
Nick O'Leary
9d507b09ca Skip context tests until they migrate to runtimeAPI structure 2018-07-29 23:54:43 +01:00
Nick O'Leary
9c4a712dc7 Merge branch 'master' into runtime-api 2018-07-29 23:47:19 +01:00
Nick O'Leary
0835fdd0d1 Merge branch '0.19' into runtime-api 2018-06-06 21:59:46 +01:00
Nick O'Leary
522360dcb7 merge to latest 2018-05-23 12:45:29 +01:00
Nick O'Leary
979713c4db merge 0.19 2018-05-21 12:28:06 +01:00
Nick O'Leary
e41d5c249f WIP - url rewriting to support debug 2018-05-14 14:32:58 +01:00
Nick O'Leary
f82a779817 merge master 2018-05-14 09:14:35 +01:00
Nick O'Leary
df8a8ea204 Connect comms to apiRootUrl 2018-05-11 22:13:13 +01:00
Nick O'Leary
8957d33e49 Merge branch 'master' into runtime-api 2018-05-11 14:17:46 +01:00
Nick O'Leary
28fe1e4c8f Allow the editor to use a custom admin api url root 2018-05-11 13:26:26 +01:00
Nick O'Leary
0c7f4e2168 Merge 0.18.5 2018-05-10 21:45:25 +01:00
Nick O'Leary
b22956bd99 Remove old locales test 2018-05-01 12:28:16 +01:00
Nick O'Leary
42516206d9 Move module message catalogs under runtime api 2018-05-01 12:28:16 +01:00
Nick O'Leary
fc4edde6e6 Add runtime-api tests 2018-05-01 12:28:15 +01:00
Nick O'Leary
54cc04fd96 Tweak the initialisation of the editor js 2018-05-01 12:28:15 +01:00
Nick O'Leary
80062b6a62 Move type editors into their own files 2018-05-01 12:28:15 +01:00
Nick O'Leary
99af79fcf3 Add missing test resources 2018-05-01 12:28:15 +01:00
Nick O'Leary
11d87205d7 Move node registry to its own top level dir 2018-05-01 12:28:15 +01:00
Nick O'Leary
5866d414ce Replace some instances of when with Promise 2018-05-01 12:28:15 +01:00
Nick O'Leary
9a972b0b8a Increase test coverage 2018-05-01 12:28:15 +01:00
Nick O'Leary
e6aeeea8c1 Add better docs tasks 2018-05-01 12:28:15 +01:00
Nick O'Leary
5d064aa1d7 Fixup all the tests 2018-05-01 12:28:15 +01:00
Nick O'Leary
34832d5942 Fix up runtime tests 2018-05-01 12:28:15 +01:00
Nick O'Leary
e3b1179a21 Start bringing the tests back from the brink 2018-05-01 12:28:15 +01:00
Nick O'Leary
f94a36613c Split comms across api and runtime 2018-05-01 12:28:14 +01:00
Nick O'Leary
efc3cc24f4 Fixup projects import after file move 2018-05-01 12:28:14 +01:00
Nick O'Leary
b47f8aaf70 Rename projects.js 2018-05-01 12:28:14 +01:00
Nick O'Leary
94ca4607bc Add projects to runtime-api 2018-05-01 12:28:14 +01:00
Nick O'Leary
2dab1d3e6e Fix up merge issue on api/nodes 2018-05-01 12:28:14 +01:00
Nick O'Leary
825b0fb22f Update locales module to new structure 2018-05-01 12:28:14 +01:00
Nick O'Leary
1cdb039ea2 Move log and i18n to their own utils module 2018-05-01 12:28:14 +01:00
Nick O'Leary
7409cb3abb Separate library api and runtime components 2018-05-01 12:28:14 +01:00
Nick O'Leary
e8e8f70c27 WIP: create new runtime-api 2018-05-01 12:28:14 +01:00
649 changed files with 13257 additions and 4621 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
/packages/node_modules/** linguist-generated=false

4
.gitignore vendored
View File

@@ -17,3 +17,7 @@ node_modules
public
locales/zz-ZZ
nodes/core/locales/zz-ZZ
!packages/node_modules
packages/node_modules/@node-red/editor-client/public
!test/**/node_modules
docs

View File

@@ -1,7 +0,0 @@
.settings
.jshintignore
.jshintrc
.project
.tern-project
.travis.yml
.git

View File

@@ -8,5 +8,3 @@ matrix:
before_script:
- npm install -g istanbul coveralls
- node_js: "8"
- node_js: "6"
- node_js: "4"

View File

@@ -42,7 +42,7 @@ module.exports = function(grunt) {
reporter: 'spec'
},
all: { src: ['test/**/*_spec.js'] },
core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
core: { src: ["test/_spec.js","test/unit/**/*_spec.js"]},
nodes: { src: ["test/nodes/**/*_spec.js"]}
},
webdriver: {
@@ -59,8 +59,8 @@ module.exports = function(grunt) {
reportFormats: ['lcov','html'],
print: 'both'
},
all: { src: ["test/_spec.js","test/red/**/*_spec.js","test/nodes/**/*_spec.js"] },
core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
all: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js","test/nodes/**/*_spec.js"] },
core: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js"]},
nodes: { src: ["test/nodes/**/*_spec.js"]}
},
jshint: {
@@ -80,16 +80,14 @@ module.exports = function(grunt) {
all: [
'Gruntfile.js',
'red.js',
'red/**/*.js',
'nodes/core/*/*.js',
'editor/js/**/*.js'
'packages/**/*.js'
],
core: {
files: {
src: [
'Gruntfile.js',
'red.js',
'red/**/*.js'
'packages/**/*.js',
]
}
},
@@ -120,81 +118,81 @@ module.exports = function(grunt) {
src: [
// Ensure editor source files are concatenated in
// the right order
"editor/js/red.js",
"editor/js/events.js",
"editor/js/i18n.js",
"editor/js/settings.js",
"editor/js/user.js",
"editor/js/comms.js",
"editor/js/text/bidi.js",
"editor/js/text/format.js",
"editor/js/ui/state.js",
"editor/js/nodes.js",
"editor/js/history.js",
"editor/js/validators.js",
"editor/js/ui/utils.js",
"editor/js/ui/common/editableList.js",
"editor/js/ui/common/checkboxSet.js",
"editor/js/ui/common/menu.js",
"editor/js/ui/common/panels.js",
"editor/js/ui/common/popover.js",
"editor/js/ui/common/searchBox.js",
"editor/js/ui/common/tabs.js",
"editor/js/ui/common/stack.js",
"editor/js/ui/common/typedInput.js",
"editor/js/ui/actions.js",
"editor/js/ui/deploy.js",
"editor/js/ui/diff.js",
"editor/js/ui/keyboard.js",
"editor/js/ui/workspaces.js",
"editor/js/ui/view.js",
"editor/js/ui/view-navigator.js",
"editor/js/ui/sidebar.js",
"editor/js/ui/palette.js",
"editor/js/ui/tab-info.js",
"editor/js/ui/tab-config.js",
"editor/js/ui/tab-context.js",
"editor/js/ui/palette-editor.js",
"editor/js/ui/editor.js",
"editor/js/ui/editors/*.js",
"editor/js/ui/tray.js",
"editor/js/ui/clipboard.js",
"editor/js/ui/library.js",
"editor/js/ui/notifications.js",
"editor/js/ui/search.js",
"editor/js/ui/typeSearch.js",
"editor/js/ui/subflow.js",
"editor/js/ui/userSettings.js",
"editor/js/ui/projects/projects.js",
"editor/js/ui/projects/projectSettings.js",
"editor/js/ui/projects/projectUserSettings.js",
"editor/js/ui/projects/tab-versionControl.js",
"editor/js/ui/touch/radialMenu.js"
"packages/node_modules/@node-red/editor-client/src/js/red.js",
"packages/node_modules/@node-red/editor-client/src/js/events.js",
"packages/node_modules/@node-red/editor-client/src/js/i18n.js",
"packages/node_modules/@node-red/editor-client/src/js/settings.js",
"packages/node_modules/@node-red/editor-client/src/js/user.js",
"packages/node_modules/@node-red/editor-client/src/js/comms.js",
"packages/node_modules/@node-red/editor-client/src/js/text/bidi.js",
"packages/node_modules/@node-red/editor-client/src/js/text/format.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/state.js",
"packages/node_modules/@node-red/editor-client/src/js/nodes.js",
"packages/node_modules/@node-red/editor-client/src/js/history.js",
"packages/node_modules/@node-red/editor-client/src/js/validators.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/utils.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/checkboxSet.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/searchBox.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/stack.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/actions.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/diff.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view-navigator.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/palette.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/editor.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/*.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tray.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/library.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/notifications.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/search.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectUserSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js"
],
dest: "public/red/red.js"
dest: "packages/node_modules/@node-red/editor-client/public/red/red.js"
},
vendor: {
files: {
"public/vendor/vendor.js": [
"editor/vendor/jquery/js/jquery-1.11.3.min.js",
"editor/vendor/bootstrap/js/bootstrap.min.js",
"editor/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js",
"editor/vendor/jquery/js/jquery.ui.touch-punch.min.js",
"editor/vendor/marked/marked.min.js",
"editor/vendor/d3/d3.v3.min.js",
"editor/vendor/i18next/i18next.min.js"
"packages/node_modules/@node-red/editor-client/public/vendor/vendor.js": [
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-1.11.3.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/bootstrap/js/bootstrap.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery.ui.touch-punch.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/marked/marked.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/d3/d3.v3.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/i18next/i18next.min.js"
],
"public/vendor/vendor.css": [
"packages/node_modules/@node-red/editor-client/public/vendor/vendor.css": [
// TODO: resolve relative resource paths in
// bootstrap/FA/jquery
],
"public/vendor/jsonata/jsonata.min.js": [
"packages/node_modules/@node-red/editor-client/public/vendor/jsonata/jsonata.min.js": [
"node_modules/jsonata/jsonata-es5.min.js",
"editor/vendor/jsonata/formatter.js"
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js"
],
"public/vendor/ace/worker-jsonata.js": [
"packages/node_modules/@node-red/editor-client/public/vendor/ace/worker-jsonata.js": [
"node_modules/jsonata/jsonata-es5.min.js",
"editor/vendor/jsonata/worker-jsonata.js"
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/worker-jsonata.js"
]
}
}
@@ -202,10 +200,10 @@ module.exports = function(grunt) {
uglify: {
build: {
files: {
'public/red/red.min.js': 'public/red/red.js',
'public/red/main.min.js': 'public/red/main.js',
'public/vendor/ace/mode-jsonata.js': 'editor/vendor/jsonata/mode-jsonata.js',
'public/vendor/ace/snippets/jsonata.js': 'editor/vendor/jsonata/snippets-jsonata.js'
'packages/node_modules/@node-red/editor-client/public/red/red.min.js': 'packages/node_modules/@node-red/editor-client/public/red/red.js',
'packages/node_modules/@node-red/editor-client/public/red/main.min.js': 'packages/node_modules/@node-red/editor-client/public/red/main.js',
'packages/node_modules/@node-red/editor-client/public/vendor/ace/mode-jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/mode-jsonata.js',
'packages/node_modules/@node-red/editor-client/public/vendor/ace/snippets/jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/snippets-jsonata.js'
}
}
},
@@ -215,50 +213,50 @@ module.exports = function(grunt) {
outputStyle: 'compressed'
},
files: [{
dest: 'public/red/style.min.css',
src: 'editor/sass/style.scss'
dest: 'packages/node_modules/@node-red/editor-client/public/red/style.min.css',
src: 'packages/node_modules/@node-red/editor-client/src/sass/style.scss'
},
{
dest: 'public/vendor/bootstrap/css/bootstrap.min.css',
src: 'editor/vendor/bootstrap/css/bootstrap.css'
dest: 'packages/node_modules/@node-red/editor-client/public/vendor/bootstrap/css/bootstrap.min.css',
src: 'packages/node_modules/@node-red/editor-client/src/vendor/bootstrap/css/bootstrap.css'
}]
}
},
jsonlint: {
messages: {
src: [
'nodes/core/locales/en-US/messages.json',
'red/api/locales/en-US/editor.json',
'red/runtime/locales/en-US/runtime.json'
'packages/node_modules/@node-red/nodes/locales/**/*.json',
'packages/node_modules/@node-red/editor-api/lib/editor/locales/**/*.json',
'packages/node_modules/@node-red/runtime/locales/**/*.json'
]
},
keymaps: {
src: [
'editor/js/keymap.json'
'packages/node_modules/@node-red/editor-client/src/js/keymap.json'
]
}
},
attachCopyright: {
js: {
src: [
'public/red/red.min.js',
'public/red/main.min.js'
'packages/node_modules/@node-red/editor-client/public/red/red.min.js',
'packages/node_modules/@node-red/editor-client/public/red/main.min.js'
]
},
css: {
src: [
'public/red/style.min.css'
'packages/node_modules/@node-red/editor-client/public/red/style.min.css'
]
}
},
clean: {
build: {
src: [
"public/red",
"public/index.html",
"public/favicon.ico",
"public/icons",
"public/vendor"
"packages/node_modules/@node-red/editor-client/public/red",
"packages/node_modules/@node-red/editor-client/public/index.html",
"packages/node_modules/@node-red/editor-client/public/favicon.ico",
"packages/node_modules/@node-red/editor-client/public/icons",
"packages/node_modules/@node-red/editor-client/public/vendor"
]
},
release: {
@@ -270,27 +268,27 @@ module.exports = function(grunt) {
watch: {
js: {
files: [
'editor/js/**/*.js'
'packages/node_modules/@node-red/editor-client/src/js/**/*.js'
],
tasks: ['copy:build','concat','uglify','attachCopyright:js']
},
sass: {
files: [
'editor/sass/**/*.scss'
'packages/node_modules/@node-red/editor-client/src/sass/**/*.scss'
],
tasks: ['sass','attachCopyright:css']
},
json: {
files: [
'nodes/core/locales/en-US/messages.json',
'red/api/locales/en-US/editor.json',
'red/runtime/locales/en-US/runtime.json'
'packages/node_modules/@node-red/nodes/locales/**/*.json',
'packages/node_modules/@node-red/editor-api/lib/editor/locales/**/*.json',
'packages/node_modules/@node-red/runtime/locales/**/*.json'
],
tasks: ['jsonlint:messages']
},
keymaps: {
files: [
'editor/js/keymap.json'
'packages/node_modules/@node-red/editor-client/src/js/keymap.json'
],
tasks: ['jsonlint:keymaps','copy:build']
},
@@ -305,12 +303,13 @@ module.exports = function(grunt) {
nodemon: {
/* uses .nodemonignore */
dev: {
script: 'red.js',
script: 'packages/node_modules/node-red/red.js',
options: {
args: nodemonArgs,
ext: 'js,html,json',
watch: [
'red','nodes'
'packages/node_modules',
'!packages/node_modules/@node-red/editor-client'
]
}
}
@@ -329,21 +328,21 @@ module.exports = function(grunt) {
build: {
files:[
{
src: 'editor/js/main.js',
dest: 'public/red/main.js'
src: 'packages/node_modules/@node-red/editor-client/src/js/main.js',
dest: 'packages/node_modules/@node-red/editor-client/public/red/main.js'
},
{
src: 'editor/js/keymap.json',
dest: 'public/red/keymap.json'
src: 'packages/node_modules/@node-red/editor-client/src/js/keymap.json',
dest: 'packages/node_modules/@node-red/editor-client/public/red/keymap.json'
},
{
cwd: 'editor/images',
cwd: 'packages/node_modules/@node-red/editor-client/src/images',
src: '**',
expand: true,
dest: 'public/red/images/'
dest: 'packages/node_modules/@node-red/editor-client/public/red/images/'
},
{
cwd: 'editor/vendor',
cwd: 'packages/node_modules/@node-red/editor-client/src/vendor',
src: [
'ace/**',
//'bootstrap/css/**',
@@ -352,46 +351,25 @@ module.exports = function(grunt) {
'font-awesome/**'
],
expand: true,
dest: 'public/vendor/'
dest: 'packages/node_modules/@node-red/editor-client/public/vendor/'
},
{
cwd: 'editor/icons',
cwd: 'packages/node_modules/@node-red/editor-client/src/icons',
src: '**',
expand: true,
dest: 'public/icons/'
dest: 'packages/node_modules/@node-red/editor-client/public/icons/'
},
{
expand: true,
src: ['editor/index.html','editor/favicon.ico'],
dest: 'public/',
src: ['packages/node_modules/@node-red/editor-client/src/index.html','packages/node_modules/@node-red/editor-client/src/favicon.ico'],
dest: 'packages/node_modules/@node-red/editor-client/public/',
flatten: true
},
{
src: 'CHANGELOG.md',
dest: 'public/red/about'
dest: 'packages/node_modules/@node-red/editor-client/public/red/about'
}
]
},
release: {
files: [{
mode: true,
expand: true,
src: [
'*.md',
'LICENSE',
'package.json',
'settings.js',
'red.js',
'lib/.gitignore',
'nodes/*.demo',
'nodes/core/**',
'red/**',
'public/**',
'editor/templates/**',
'bin/**'
],
dest: path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>')
}]
}
},
chmod: {
@@ -400,8 +378,8 @@ module.exports = function(grunt) {
},
release: {
src: [
path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/nodes/core/hardware/nrgpio*'),
path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/red/runtime/storage/localfilesystem/projects/git/node-red-*sh')
"packages/node_modules/@node-red/nodes/core/hardware/nrgpio",
"packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/node-red-*sh"
]
}
},
@@ -411,8 +389,43 @@ module.exports = function(grunt) {
archive: '<%= paths.dist %>/node-red-<%= pkg.version %>.zip'
},
expand: true,
cwd: '<%= paths.dist %>/',
src: ['node-red-<%= pkg.version %>/**']
cwd: 'packages/node_modules/',
src: [
'**',
'!@node-red/editor-client/src/**'
]
}
},
jsdoc : {
runtimeAPI: {
src: 'packages/node_modules/@node-red/runtime/lib/api/*.js',
options: {
destination: 'docs',
configure: './jsdoc.json'
}
},
nodeREDUtil: {
src: 'packages/node_modules/@node-red/util/**/*.js',
options: {
destination: 'packages/node_modules/@node-red/util/docs',
configure: './jsdoc.json'
}
}
},
jsdoc2md: {
runtimeAPI: {
options: {
separators: true
},
src: 'packages/node_modules/@node-red/runtime/lib/api/*.js',
dest: 'docs/runtime-api.md'
},
nodeREDUtil: {
options: {
separators: true
},
src: 'packages/node_modules/@node-red/util/**/*.js',
dest: 'packages/node_modules/@node-red/util/docs/api.md'
}
}
});
@@ -432,6 +445,8 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-jsonlint');
grunt.loadNpmTasks('grunt-mocha-istanbul');
grunt.loadNpmTasks('grunt-webdriver');
grunt.loadNpmTasks('grunt-jsdoc');
grunt.loadNpmTasks('grunt-jsdoc-to-markdown');
grunt.registerMultiTask('attachCopyright', function() {
var files = this.data.src;
@@ -473,6 +488,15 @@ module.exports = function(grunt) {
}
});
grunt.registerTask('verifyPackageDependencies', function() {
var verifyDependencies = require("./scripts/verify-package-dependencies.js");
var failures = verifyDependencies();
if (failures.length > 0) {
failures.forEach(f => grunt.log.error(f));
grunt.fail.fatal("Failed to verify package dependencies");
}
});
grunt.registerTask('setDevEnv',
'Sets NODE_ENV=development so non-minified assets are used',
function () {
@@ -481,7 +505,7 @@ module.exports = function(grunt) {
grunt.registerTask('default',
'Builds editor content then runs code style checks and unit tests on all components',
['build','jshint:editor','mocha_istanbul:all']);
['build','verifyPackageDependencies','jshint:editor','mocha_istanbul:all']);
grunt.registerTask('test-core',
'Runs code style check and unit tests on core runtime code',
@@ -509,9 +533,13 @@ module.exports = function(grunt) {
grunt.registerTask('release',
'Create distribution zip file',
['build','clean:release','copy:release','chmod:release','compress:release']);
['build','verifyPackageDependencies','clean:release','chmod:release','compress:release']);
grunt.registerTask('coverage',
'Run Istanbul code test coverage task',
['build','mocha_istanbul:all']);
grunt.registerTask('docs',
'Generates API documentation',
['jsdoc','jsdoc2md']);
};

View File

@@ -44,9 +44,6 @@ If you want to run the latest code from git, here's how to get started:
4. Run
npm start
or
node red.js
## Contributing

View File

@@ -1,81 +0,0 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.panels = (function() {
function createPanel(options) {
var container = options.container || $("#"+options.id);
var children = container.children();
if (children.length !== 2) {
throw new Error("Container must have exactly two children");
}
container.addClass("red-ui-panels");
var separator = $('<div class="red-ui-panels-separator"></div>').insertAfter(children[0]);
var startPosition;
var panelHeights = [];
var modifiedHeights = false;
var panelRatio;
separator.draggable({
axis: "y",
containment: container,
scroll: false,
start:function(event,ui) {
var height = container.height();
startPosition = ui.position.top;
panelHeights = [$(children[0]).height(),$(children[1]).height()];
},
drag: function(event,ui) {
var height = container.height();
var delta = ui.position.top-startPosition;
var newHeights = [panelHeights[0]+delta,panelHeights[1]-delta];
$(children[0]).height(newHeights[0]);
$(children[1]).height(newHeights[1]);
if (options.resize) {
options.resize(newHeights[0],newHeights[1]);
}
ui.position.top -= delta;
panelRatio = newHeights[0]/height;
},
stop:function(event,ui) {
modifiedHeights = true;
}
});
return {
resize: function(height) {
var panelHeights = [$(children[0]).height(),$(children[1]).height()];
container.height(height);
if (modifiedHeights) {
var topPanelHeight = panelRatio*height;
var bottomPanelHeight = height - topPanelHeight - 48;
panelHeights = [topPanelHeight,bottomPanelHeight];
$(children[0]).height(panelHeights[0]);
$(children[1]).height(panelHeights[1]);
}
if (options.resize) {
options.resize(panelHeights[0],panelHeights[1]);
}
}
}
}
return {
create: createPanel
}
})();

26
jsdoc.json Normal file
View File

@@ -0,0 +1,26 @@
{
"opts": {
"template": "./node_modules/jsdoc-nr-template",
"destination": "./docs",
"recurse": true
},
"tags": {
"allowUnknownTags": false,
"dictionaries": ["jsdoc"]
},
"source": {
"_include": [
"./packages/node_modules/@node-red/runtime/lib/api"
]
},
"templates": {
"systemName": "Node-RED Runtime API",
"theme":"yeti",
"footer": "",
"copyright": "Released under the Apache License v2.0",
"default": {
"outputSourceFiles": false
}
},
"plugins": ["plugins/markdown"]
}

1
lib/.gitignore vendored
View File

@@ -1 +0,0 @@
*

View File

@@ -1,159 +0,0 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var util = require("util");
var mqtt = require("./mqtt");
var settings = require(process.env.NODE_RED_HOME+"/red/red").settings;
util.log("[warn] nodes/core/io/lib/mqttConnectionPool.js is deprecated and will be removed in a future release of Node-RED. Please report this usage to the Node-RED mailing list.");
var connections = {};
function matchTopic(ts,t) {
if (ts == "#") {
return true;
}
var re = new RegExp("^"+ts.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
return re.test(t);
}
module.exports = {
get: function(broker,port,clientid,username,password,will) {
var id = "["+(username||"")+":"+(password||"")+"]["+(clientid||"")+"]@"+broker+":"+port;
if (!connections[id]) {
connections[id] = function() {
var uid = (1+Math.random()*4294967295).toString(16);
var client = mqtt.createClient(port,broker);
client.uid = uid;
client.setMaxListeners(0);
var options = {keepalive:15};
options.clientId = clientid || 'mqtt_' + (1+Math.random()*4294967295).toString(16);
options.username = username;
options.password = password;
options.will = will;
var queue = [];
var subscriptions = {};
var connecting = false;
var obj = {
_instances: 0,
publish: function(msg) {
if (client.isConnected()) {
client.publish(msg.topic,msg.payload,msg.qos,msg.retain);
} else {
if (!connecting) {
connecting = true;
client.connect(options);
}
queue.push(msg);
}
},
subscribe: function(topic,qos,callback,ref) {
ref = ref||0;
subscriptions[topic] = subscriptions[topic]||{};
var sub = {
topic:topic,
qos:qos,
handler:function(mtopic,mpayload,mqos,mretain) {
if (matchTopic(topic,mtopic)) {
callback(mtopic,mpayload,mqos,mretain);
}
},
ref: ref
};
subscriptions[topic][ref] = sub;
client.on('message',sub.handler);
if (client.isConnected()) {
client.subscribe(topic,qos);
}
},
unsubscribe: function(topic,ref) {
ref = ref||0;
var sub = subscriptions[topic];
if (sub) {
if (sub[ref]) {
client.removeListener('message',sub[ref].handler);
delete sub[ref];
}
if (Object.keys(sub).length == 0) {
delete subscriptions[topic];
client.unsubscribe(topic);
}
}
},
on: function(a,b){
client.on(a,b);
},
once: function(a,b){
client.once(a,b);
},
connect: function() {
if (client && !client.isConnected() && !connecting) {
connecting = true;
client.connect(options);
}
},
disconnect: function(ref) {
this._instances -= 1;
if (this._instances == 0) {
client.disconnect();
client = null;
delete connections[id];
}
},
isConnected: function() {
return client.isConnected();
}
};
client.on('connect',function() {
if (client) {
util.log('[mqtt] ['+uid+'] connected to broker tcp://'+broker+':'+port);
connecting = false;
for (var s in subscriptions) {
var topic = s;
var qos = 0;
for (var r in subscriptions[s]) {
qos = Math.max(qos,subscriptions[s][r].qos);
}
client.subscribe(topic,qos);
}
//console.log("connected - publishing",queue.length,"messages");
while(queue.length) {
var msg = queue.shift();
//console.log(msg);
client.publish(msg.topic,msg.payload,msg.qos,msg.retain);
}
}
});
client.on('connectionlost', function(err) {
util.log('[mqtt] ['+uid+'] connection lost to broker tcp://'+broker+':'+port);
connecting = false;
setTimeout(function() {
obj.connect();
}, settings.mqttReconnectTime||5000);
});
client.on('disconnect', function() {
connecting = false;
util.log('[mqtt] ['+uid+'] disconnected from broker tcp://'+broker+':'+port);
});
return obj
}();
}
connections[id]._instances += 1;
return connections[id];
}
};

View File

@@ -1,120 +1,116 @@
{
"name": "node-red",
"version": "0.19.4",
"description": "A visual tool for wiring the Internet of Things",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/node-red/node-red.git"
},
"main": "red/red.js",
"scripts": {
"start": "node red.js",
"test": "grunt",
"build": "grunt build"
},
"bin": {
"node-red": "./red.js",
"node-red-pi": "bin/node-red-pi"
},
"contributors": [
{
"name": "Nick O'Leary"
"name": "node-red",
"version": "0.20.0-alpha.0",
"description": "A visual tool for wiring the Internet of Things",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/node-red/node-red.git"
},
{
"name": "Dave Conway-Jones"
"private": "true",
"scripts": {
"start": "node packages/node_modules/node-red/red.js",
"test": "grunt",
"build": "grunt build",
"docs": "grunt docs"
},
"contributors": [
{
"name": "Nick O'Leary"
},
{
"name": "Dave Conway-Jones"
}
],
"dependencies": {
"ajv": "6.5.3",
"basic-auth": "2.0.0",
"bcryptjs": "2.4.3",
"body-parser": "1.18.3",
"cheerio": "0.22.0",
"clone": "2.1.2",
"cookie": "0.3.1",
"cookie-parser": "1.4.3",
"cors": "2.8.4",
"cron": "1.4.1",
"denque": "1.3.0",
"express": "4.16.3",
"express-session": "1.15.6",
"fs-extra": "5.0.0",
"fs.notify": "0.0.4",
"hash-sum": "1.0.2",
"https-proxy-agent": "2.2.1",
"i18next": "11.6.0",
"is-utf8": "0.2.1",
"js-yaml": "3.12.0",
"json-stringify-safe": "5.0.1",
"jsonata": "1.5.4",
"media-typer": "0.3.0",
"memorystore": "1.6.0",
"mime": "1.4.1",
"mqtt": "2.18.8",
"multer": "1.3.1",
"mustache": "2.3.2",
"node-red-node-email": "0.1.*",
"node-red-node-feedparser": "^0.1.12",
"node-red-node-rbe": "0.2.*",
"node-red-node-twitter": "^1.1.0",
"nopt": "4.0.1",
"oauth2orize": "1.11.0",
"on-headers": "1.0.1",
"passport": "0.4.0",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"raw-body": "2.3.3",
"request": "2.88.0",
"semver": "5.5.1",
"sentiment": "2.1.0",
"uglify-js": "3.4.9",
"when": "3.7.8",
"ws": "1.1.5",
"xml2js": "0.4.19"
},
"optionalDependencies": {
"bcrypt": "~2.0.0"
},
"devDependencies": {
"chromedriver": "^2.41.0",
"grunt": "~1.0.3",
"grunt-chmod": "~1.1.1",
"grunt-cli": "~1.3.1",
"grunt-concurrent": "~2.3.1",
"grunt-contrib-clean": "~1.1.0",
"grunt-contrib-compress": "~1.4.0",
"grunt-contrib-concat": "~1.0.1",
"grunt-contrib-copy": "~1.0.0",
"grunt-contrib-jshint": "~1.1.0",
"grunt-contrib-uglify": "~3.4.0",
"grunt-contrib-watch": "~1.1.0",
"grunt-jsdoc": "^2.2.1",
"grunt-jsdoc-to-markdown": "^4.0.0",
"grunt-jsonlint": "~1.1.0",
"grunt-mocha-istanbul": "5.0.2",
"grunt-nodemon": "~0.4.2",
"grunt-sass": "~2.0.0",
"grunt-simple-mocha": "~0.4.1",
"grunt-webdriver": "^2.0.3",
"http-proxy": "^1.16.2",
"istanbul": "0.4.5",
"minami": "1.2.3",
"mocha": "^5.2.0",
"should": "^8.4.0",
"sinon": "1.17.7",
"stoppable": "^1.0.6",
"supertest": "3.1.0",
"wdio-chromedriver-service": "^0.1.3",
"wdio-mocha-framework": "^0.6.2",
"wdio-spec-reporter": "^0.1.5",
"webdriverio": "^4.13.1",
"node-red-node-test-helper": "node-red/node-red-node-test-helper",
"jsdoc-nr-template": "node-red/jsdoc-nr-template"
},
"engines": {
"node": ">=8"
}
],
"keywords": [
"editor",
"messaging",
"iot",
"flow"
],
"dependencies": {
"ajv": "6.5.3",
"basic-auth": "2.0.0",
"bcryptjs": "2.4.3",
"body-parser": "1.18.3",
"cheerio": "0.22.0",
"clone": "2.1.2",
"cookie": "0.3.1",
"cookie-parser": "1.4.3",
"cors": "2.8.4",
"cron": "1.4.1",
"denque": "1.3.0",
"express": "4.16.3",
"express-session": "1.15.6",
"fs-extra": "5.0.0",
"fs.notify": "0.0.4",
"hash-sum": "1.0.2",
"https-proxy-agent": "2.2.1",
"i18next": "11.6.0",
"is-utf8": "0.2.1",
"js-yaml": "3.12.0",
"json-stringify-safe": "5.0.1",
"jsonata": "1.5.4",
"media-typer": "0.3.0",
"memorystore": "1.6.0",
"mqtt": "2.18.8",
"multer": "1.3.1",
"mustache": "2.3.2",
"node-red-node-email": "0.1.*",
"node-red-node-feedparser": "^0.1.12",
"node-red-node-rbe": "0.2.*",
"node-red-node-twitter": "^1.1.0",
"nopt": "4.0.1",
"oauth2orize": "1.11.0",
"on-headers": "1.0.1",
"passport": "0.4.0",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"raw-body": "2.3.3",
"request": "2.88.0",
"semver": "5.5.1",
"sentiment": "2.1.0",
"uglify-js": "3.4.9",
"when": "3.7.8",
"ws": "1.1.5",
"xml2js": "0.4.19"
},
"optionalDependencies": {
"bcrypt": "~2.0.0"
},
"devDependencies": {
"chromedriver": "^2.41.0",
"grunt": "~1.0.3",
"grunt-chmod": "~1.1.1",
"grunt-cli": "~1.3.1",
"grunt-concurrent": "~2.3.1",
"grunt-contrib-clean": "~1.1.0",
"grunt-contrib-compress": "~1.4.0",
"grunt-contrib-concat": "~1.0.1",
"grunt-contrib-copy": "~1.0.0",
"grunt-contrib-jshint": "~1.1.0",
"grunt-contrib-uglify": "~3.4.0",
"grunt-contrib-watch": "~1.1.0",
"grunt-jsonlint": "~1.1.0",
"grunt-mocha-istanbul": "5.0.2",
"grunt-nodemon": "~0.4.2",
"grunt-sass": "~2.0.0",
"grunt-simple-mocha": "~0.4.1",
"grunt-webdriver": "^2.0.3",
"http-proxy": "^1.16.2",
"istanbul": "0.4.5",
"mocha": "^5.2.0",
"node-red-node-test-helper": "0.1.7",
"should": "^8.4.0",
"sinon": "1.17.7",
"stoppable": "^1.0.6",
"supertest": "3.1.0",
"wdio-chromedriver-service": "^0.1.3",
"wdio-mocha-framework": "^0.6.2",
"wdio-spec-reporter": "^0.1.5",
"webdriverio": "^4.13.1"
},
"engines": {
"node": ">=4"
}
}

View File

@@ -0,0 +1,178 @@
Copyright JS Foundation and other contributors, http://js.foundation
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@@ -0,0 +1,12 @@
@node-red/editor-api
====================
Node-RED editor api module.
This provides an Express application that can be used to serve the Node-RED
editor.
### Source
The main Node-RED modules are maintained as a monorepo on [GitHub](https://github.com/node-red/node-red).

View File

@@ -0,0 +1,41 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var apiUtils = require("../util");
var runtimeAPI;
module.exports = {
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI;
},
get: function(req,res) {
var opts = {
user: req.user,
scope: req.params.scope,
id: req.params.id,
key: req.params[0],
store: req.query['store']
}
runtimeAPI.context.getValue(opts).then(function(result) {
res.json(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
}
}

View File

@@ -0,0 +1,69 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var runtimeAPI;
var apiUtils = require("../util");
module.exports = {
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI;
},
get: function(req,res) {
var opts = {
user: req.user,
id: req.params.id
}
runtimeAPI.flows.getFlow(opts).then(function(result) {
return res.json(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
},
post: function(req,res) {
var opts = {
user: req.user,
flow: req.body
}
runtimeAPI.flows.addFlow(opts).then(function(id) {
return res.json({id:id});
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
},
put: function(req,res) {
var opts = {
user: req.user,
id: req.params.id,
flow: req.body
}
runtimeAPI.flows.updateFlow(opts).then(function(id) {
return res.json({id:id});
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
},
delete: function(req,res) {
var opts = {
user: req.user,
id: req.params.id
}
runtimeAPI.flows.deleteFlow(opts).then(function() {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var runtimeAPI;
var apiUtils = require("../util");
module.exports = {
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI;
},
get: function(req,res) {
var version = req.get("Node-RED-API-Version")||"v1";
if (!/^v[12]$/.test(version)) {
return res.status(400).json({code:"invalid_api_version", message:"Invalid API Version requested"});
}
var opts = {
user: req.user
}
runtimeAPI.flows.getFlows(opts).then(function(result) {
if (version === "v1") {
res.json(result.flows);
} else if (version === "v2") {
res.json(result);
}
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
},
post: function(req,res) {
var version = req.get("Node-RED-API-Version")||"v1";
if (!/^v[12]$/.test(version)) {
return res.status(400).json({code:"invalid_api_version", message:"Invalid API Version requested"});
}
var opts = {
user: req.user,
deploymentType: req.get("Node-RED-Deployment-Type")||"full"
}
if (opts.deploymentType !== 'reload') {
if (version === "v1") {
opts.flows = {flows: req.body}
} else {
opts.flows = req.body;
}
}
runtimeAPI.flows.setFlows(opts).then(function(result) {
if (version === "v1") {
res.status(204).end();
} else {
res.json(result);
}
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
}
}

View File

@@ -25,11 +25,11 @@ var auth = require("../auth");
var apiUtil = require("../util");
module.exports = {
init: function(runtime) {
flows.init(runtime);
flow.init(runtime);
nodes.init(runtime);
context.init(runtime);
init: function(runtimeAPI) {
flows.init(runtimeAPI);
flow.init(runtimeAPI);
nodes.init(runtimeAPI);
context.init(runtimeAPI);
var needsPermission = auth.needsPermission;
@@ -48,6 +48,8 @@ module.exports = {
// Nodes
adminApp.get("/nodes",needsPermission("nodes.read"),nodes.getAll,apiUtil.errorHandler);
adminApp.post("/nodes",needsPermission("nodes.write"),nodes.post,apiUtil.errorHandler);
adminApp.get(/\/nodes\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalogs,apiUtil.errorHandler);
adminApp.get(/\/nodes\/((@[^\/]+\/)?[^\/]+\/[^\/]+)\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalog,apiUtil.errorHandler);
adminApp.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.read"),nodes.getModule,apiUtil.errorHandler);
adminApp.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.write"),nodes.putModule,apiUtil.errorHandler);
adminApp.delete(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.write"),nodes.delete,apiUtil.errorHandler);

View File

@@ -0,0 +1,173 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var apiUtils = require("../util");
var runtimeAPI;
module.exports = {
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI;
},
getAll: function(req,res) {
var opts = {
user: req.user
}
if (req.get("accept") == "application/json") {
runtimeAPI.nodes.getNodeList(opts).then(function(list) {
res.json(list);
})
} else {
opts.lang = apiUtils.determineLangFromHeaders(req.acceptsLanguages());
runtimeAPI.nodes.getNodeConfigs(opts).then(function(configs) {
res.send(configs);
})
}
},
post: function(req,res) {
var opts = {
user: req.user,
module: req.body.module,
version: req.body.version
}
runtimeAPI.nodes.addModule(opts).then(function(info) {
res.json(info);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
},
delete: function(req,res) {
var opts = {
user: req.user,
module: req.params[0]
}
runtimeAPI.nodes.removeModule(opts).then(function() {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
},
getSet: function(req,res) {
var opts = {
user: req.user,
id: req.params[0] + "/" + req.params[2]
}
if (req.get("accept") === "application/json") {
runtimeAPI.nodes.getNodeInfo(opts).then(function(result) {
res.send(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
} else {
opts.lang = apiUtils.determineLangFromHeaders(req.acceptsLanguages());
runtimeAPI.nodes.getNodeConfig(opts).then(function(result) {
return res.send(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
}
},
getModule: function(req,res) {
var opts = {
user: req.user,
module: req.params[0]
}
runtimeAPI.nodes.getModuleInfo(opts).then(function(result) {
res.send(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
},
putSet: function(req,res) {
var body = req.body;
if (!body.hasOwnProperty("enabled")) {
// log.audit({event: "nodes.module.set",error:"invalid_request"},req);
res.status(400).json({code:"invalid_request", message:"Invalid request"});
return;
}
var opts = {
user: req.user,
id: req.params[0] + "/" + req.params[2],
enabled: body.enabled
}
runtimeAPI.nodes.setNodeSetState(opts).then(function(result) {
res.send(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
},
putModule: function(req,res) {
var body = req.body;
if (!body.hasOwnProperty("enabled")) {
// log.audit({event: "nodes.module.set",error:"invalid_request"},req);
res.status(400).json({code:"invalid_request", message:"Invalid request"});
return;
}
var opts = {
user: req.user,
module: req.params[0],
enabled: body.enabled
}
runtimeAPI.nodes.setModuleState(opts).then(function(result) {
res.send(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
},
getModuleCatalog: function(req,res) {
var opts = {
user: req.user,
module: req.params[0],
lang: req.query.lng
}
runtimeAPI.nodes.getModuleCatalog(opts).then(function(result) {
res.json(result);
}).catch(function(err) {
console.log(err.stack);
apiUtils.rejectHandler(req,res,err);
})
},
getModuleCatalogs: function(req,res) {
var opts = {
user: req.user,
lang: req.query.lng
}
runtimeAPI.nodes.getModuleCatalogs(opts).then(function(result) {
res.json(result);
}).catch(function(err) {
console.log(err.stack);
apiUtils.rejectHandler(req,res,err);
})
},
getIcons: function(req,res) {
var opts = {
user: req.user
}
runtimeAPI.nodes.getIconList(opts).then(function(list) {
res.json(list);
});
}
};

View File

@@ -25,7 +25,7 @@ var permissions = require("./permissions");
var theme = require("../editor/theme");
var settings = null;
var log = null
var log = require("@node-red/util").log; // TODO: separate module
passport.use(strategies.bearerStrategy.BearerStrategy);
@@ -36,13 +36,11 @@ var server = oauth2orize.createServer();
server.exchange(oauth2orize.exchange.password(strategies.passwordTokenExchange));
function init(runtime) {
settings = runtime.settings;
log = runtime.log;
function init(_settings,storage) {
settings = _settings;
if (settings.adminAuth) {
Users.init(settings.adminAuth);
Tokens.init(settings.adminAuth,runtime.storage);
strategies.init(runtime);
Tokens.init(settings.adminAuth,storage);
}
}

View File

@@ -26,7 +26,7 @@ var Users = require("./users");
var Clients = require("./clients");
var permissions = require("./permissions");
var log;
var log = require("@node-red/util").log; // TODO: separate module
var bearerStrategy = function (accessToken, done) {
// is this a valid token?
@@ -124,9 +124,6 @@ AnonymousStrategy.prototype.authenticate = function(req) {
}
module.exports = {
init: function(runtime) {
log = runtime.log;
},
bearerStrategy: bearerStrategy,
clientPasswordStrategy: clientPasswordStrategy,
passwordTokenExchange: passwordTokenExchange,

View File

@@ -0,0 +1,243 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var ws = require("ws");
var log = require("@node-red/util").log; // TODO: separate module
var Tokens;
var Users;
var Permissions;
var server;
var settings;
var runtimeAPI;
var wsServer;
var activeConnections = [];
var anonymousUser;
var retained = {};
var heartbeatTimer;
var lastSentTime;
function init(_server,_settings,_runtimeAPI) {
server = _server;
settings = _settings;
runtimeAPI = _runtimeAPI;
Tokens = require("../auth/tokens");
Users = require("../auth/users");
Permissions = require("../auth/permissions");
}
function generateSession(length) {
var c = "ABCDEFGHIJKLMNOPQRSTUZWXYZabcdefghijklmnopqrstuvwxyz1234567890";
var token = [];
for (var i=0;i<length;i++) {
token.push(c[Math.floor(Math.random()*c.length)]);
}
return token.join("");
}
function CommsConnection(ws) {
this.session = generateSession(32);
this.ws = ws;
this.stack = [];
this.user = null;
this.lastSentTime = 0;
var self = this;
log.audit({event: "comms.open"});
log.trace("comms.open "+self.session);
var pendingAuth = (settings.adminAuth != null);
if (!pendingAuth) {
addActiveConnection(self);
}
ws.on('close',function() {
log.audit({event: "comms.close",user:self.user, session: self.session});
log.trace("comms.close "+self.session);
removeActiveConnection(self);
});
ws.on('message', function(data,flags) {
var msg = null;
try {
msg = JSON.parse(data);
} catch(err) {
log.trace("comms received malformed message : "+err.toString());
return;
}
if (!pendingAuth) {
if (msg.subscribe) {
self.subscribe(msg.subscribe);
// handleRemoteSubscription(ws,msg.subscribe);
}
} else {
var completeConnection = function(userScope,sendAck) {
try {
if (!userScope || !Permissions.hasPermission(userScope,"status.read")) {
ws.send(JSON.stringify({auth:"fail"}));
ws.close();
} else {
pendingAuth = false;
addActiveConnection(self);
if (sendAck) {
ws.send(JSON.stringify({auth:"ok"}));
}
}
} catch(err) {
console.log(err.stack);
// Just in case the socket closes before we attempt
// to send anything.
}
}
if (msg.auth) {
Tokens.get(msg.auth).then(function(client) {
if (client) {
Users.get(client.user).then(function(user) {
if (user) {
self.user = user;
log.audit({event: "comms.auth",user:self.user});
completeConnection(client.scope,true);
} else {
log.audit({event: "comms.auth.fail"});
completeConnection(null,false);
}
});
} else {
log.audit({event: "comms.auth.fail"});
completeConnection(null,false);
}
});
} else {
if (anonymousUser) {
log.audit({event: "comms.auth",user:anonymousUser});
self.user = anonymousUser;
completeConnection(anonymousUser.permissions,false);
//TODO: duplicated code - pull non-auth message handling out
if (msg.subscribe) {
self.subscribe(msg.subscribe);
}
} else {
log.audit({event: "comms.auth.fail"});
completeConnection(null,false);
}
}
}
});
ws.on('error', function(err) {
log.warn(log._("comms.error",{message:err.toString()}));
});
}
CommsConnection.prototype.send = function(topic,data) {
var self = this;
if (topic && data) {
this.stack.push({topic:topic,data:data});
}
if (!this._xmitTimer) {
this._xmitTimer = setTimeout(function() {
try {
self.ws.send(JSON.stringify(self.stack));
self.lastSentTime = Date.now();
} catch(err) {
removeActiveConnection(self);
log.warn(log._("comms.error-send",{message:err.toString()}));
}
delete self._xmitTimer;
self.stack = [];
},50);
}
}
CommsConnection.prototype.subscribe = function(topic) {
runtimeAPI.comms.subscribe({
user: this.user,
client: this,
topic: topic
})
}
function start() {
if (!settings.disableEditor) {
Users.default().then(function(_anonymousUser) {
anonymousUser = _anonymousUser;
var webSocketKeepAliveTime = settings.webSocketKeepAliveTime || 15000;
var path = settings.httpAdminRoot || "/";
path = (path.slice(0,1) != "/" ? "/":"") + path + (path.slice(-1) == "/" ? "":"/") + "comms";
wsServer = new ws.Server({
server:server,
path:path,
// Disable the deflate option due to this issue
// https://github.com/websockets/ws/pull/632
// that is fixed in the 1.x release of the ws module
// that we cannot currently pickup as it drops node 0.10 support
//perMessageDeflate: false
});
wsServer.on('connection',function(ws) {
var commsConnection = new CommsConnection(ws);
});
wsServer.on('error', function(err) {
log.warn(log._("comms.error-server",{message:err.toString()}));
});
lastSentTime = Date.now();
heartbeatTimer = setInterval(function() {
var now = Date.now();
if (now-lastSentTime > webSocketKeepAliveTime) {
activeConnections.forEach(connection => connection.send("hb",lastSentTime));
}
}, webSocketKeepAliveTime);
});
}
}
function stop() {
if (heartbeatTimer) {
clearInterval(heartbeatTimer);
heartbeatTimer = null;
}
if (wsServer) {
wsServer.close();
wsServer = null;
}
}
function addActiveConnection(connection) {
activeConnections.push(connection);
runtimeAPI.comms.addConnection({client: connection});
}
function removeActiveConnection(connection) {
for (var i=0;i<activeConnections.length;i++) {
if (activeConnections[i] === connection) {
activeConnections.splice(i,1);
runtimeAPI.comms.removeConnection({client:connection})
break;
}
}
}
module.exports = {
init:init,
start:start,
stop:stop
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var runtimeAPI;
var apiUtils = require("../util");
module.exports = {
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI
},
get: function (req, res) {
var opts = {
user: req.user,
type: req.params.type,
id: req.params.id
}
runtimeAPI.flows.getNodeCredentials(opts).then(function(result) {
res.json(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
}
}

View File

@@ -24,31 +24,35 @@ var info = require("./settings");
var auth = require("../auth");
var nodes = require("../admin/nodes"); // TODO: move /icons into here
var needsPermission;
var runtime;
var log;
var runtimeAPI;
var log = require("@node-red/util").log; // TODO: separate module
var i18n = require("@node-red/util").i18n; // TODO: separate module
var apiUtil = require("../util");
var ensureRuntimeStarted = function(req,res,next) {
if (!runtime.isStarted()) {
log.error("Node-RED runtime not started");
res.status(503).send("Not started");
} else {
next();
}
runtimeAPI.isStarted().then( started => {
if (!started) {
log.error("Node-RED runtime not started");
res.status(503).send("Not started");
} else {
next()
}
})
}
module.exports = {
init: function(server, _runtime) {
runtime = _runtime;
log = runtime.log;
init: function(server, settings, _runtimeAPI) {
runtimeAPI = _runtimeAPI;
needsPermission = auth.needsPermission;
var settings = runtime.settings;
if (!settings.disableEditor) {
info.init(runtime);
comms.init(server,runtime);
info.init(runtimeAPI);
comms.init(server,settings,runtimeAPI);
var ui = require("./ui");
ui.init(runtime);
ui.init(runtimeAPI);
var editorApp = express();
if (settings.requireHttps === true) {
editorApp.enable('trust proxy');
@@ -67,31 +71,31 @@ module.exports = {
editorApp.get("/icons/:scope/:module/:icon",ui.icon);
var theme = require("./theme");
theme.init(runtime);
theme.init(settings);
editorApp.use("/theme",theme.app());
editorApp.use("/",ui.editorResources);
//Projects
var projects = require("./projects");
projects.init(runtime);
projects.init(runtimeAPI);
editorApp.use("/projects",projects.app());
// Locales
var locales = require("./locales");
locales.init(runtime);
editorApp.get('/locales/nodes',locales.getAllNodes,apiUtil.errorHandler);
locales.init(runtimeAPI);
editorApp.get(/locales\/(.+)\/?$/,locales.get,apiUtil.errorHandler);
// Library
var library = require("./library");
library.init(editorApp,runtime);
editorApp.post(new RegExp("/library/flows\/(.*)"),needsPermission("library.write"),library.post,apiUtil.errorHandler);
library.init(runtimeAPI);
editorApp.get("/library/flows",needsPermission("library.read"),library.getAll,apiUtil.errorHandler);
editorApp.get(new RegExp("/library/flows\/(.*)"),needsPermission("library.read"),library.get,apiUtil.errorHandler);
editorApp.get(/library\/([^\/]+)(?:$|\/(.*))/,needsPermission("library.read"),library.getEntry);
editorApp.post(/library\/([^\/]+)\/(.*)/,needsPermission("library.write"),library.saveEntry);
// Credentials
var credentials = require("./credentials");
credentials.init(runtime);
credentials.init(runtimeAPI);
editorApp.get('/credentials/:type/:id', needsPermission("credentials.read"),credentials.get,apiUtil.errorHandler);
// Settings
@@ -100,18 +104,15 @@ module.exports = {
editorApp.get("/settings/user",needsPermission("settings.read"),info.userSettings,apiUtil.errorHandler);
// User Settings
editorApp.post("/settings/user",needsPermission("settings.write"),info.updateUserSettings,apiUtil.errorHandler);
// SSH keys
var sshkeys = require("./sshkeys");
sshkeys.init(runtime);
editorApp.use("/settings/user/keys",sshkeys.app());
editorApp.use("/settings/user/keys",needsPermission("settings.write"),info.sshkeys());
return editorApp;
}
},
start: function() {
var catalogPath = path.resolve(path.join(__dirname,"locales"));
return runtime.i18n.registerMessageCatalogs([
return i18n.registerMessageCatalogs([
{namespace: "editor", dir: catalogPath, file:"editor.json"},
{namespace: "jsonata", dir: catalogPath, file:"jsonata.json"},
{namespace: "infotips", dir: catalogPath, file:"infotips.json"}
@@ -119,7 +120,5 @@ module.exports = {
comms.start();
});
},
stop: comms.stop,
publish: comms.publish,
registerLibrary: library.register
stop: comms.stop
}

View File

@@ -0,0 +1,83 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var apiUtils = require("../util");
var fs = require('fs');
var fspath = require('path');
var when = require('when');
var runtimeAPI;
module.exports = {
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI;
},
getAll: function(req,res) {
var opts = {
user: req.user,
type: 'flows'
}
runtimeAPI.library.getEntries(opts).then(function(result) {
res.json(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
});
},
getEntry: function(req,res) {
var opts = {
user: req.user,
type: req.params[0],
path: req.params[1]||""
}
runtimeAPI.library.getEntry(opts).then(function(result) {
if (typeof result === "string") {
if (opts.type === 'flows') {
res.writeHead(200, {'Content-Type': 'application/json'});
} else {
res.writeHead(200, {'Content-Type': 'text/plain'});
}
res.write(result);
res.end();
} else {
res.json(result);
}
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
});
},
saveEntry: function(req,res) {
var opts = {
user: req.user,
type: req.params[0],
path: req.params[1]||""
}
// TODO: horrible inconsistencies between flows and all other types
if (opts.type === "flows") {
opts.meta = {};
opts.body = JSON.stringify(req.body);
} else {
opts.meta = req.body;
opts.body = opts.meta.text;
delete opts.meta.text;
}
runtimeAPI.library.saveEntry(opts).then(function(result) {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
});
}
}

View File

@@ -16,13 +16,14 @@
var fs = require('fs');
var path = require('path');
//var apiUtil = require('../util');
var i18n;
var redNodes;
var i18n = require("@node-red/util").i18n; // TODO: separate module
var runtimeAPI;
module.exports = {
init: function(runtime) {
i18n = runtime.i18n;
redNodes = runtime.nodes;
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI;
},
get: function(req,res) {
var namespace = req.params[0];
@@ -36,17 +37,5 @@ module.exports = {
res.json(catalog||{});
});
i18n.i.changeLanguage(prevLang);
},
getAllNodes: function(req,res) {
var lngs = req.query.lng;
var nodeList = redNodes.getNodeList();
var result = {};
nodeList.forEach(function(n) {
if (n.module !== "node-red") {
result[n.id] = i18n.i.getResourceBundle(lngs, n.id)||{};
}
});
res.json(result);
}
}

View File

@@ -97,6 +97,7 @@
"undeployedChanges": "node has undeployed changes",
"nodeActionDisabled": "node actions disabled within subflow",
"missing-types": "<p>Flows stopped due to missing node types.</p>",
"safe-mode":"<p>Flows stopped in safe mode.</p><p>You can modify your flows and deploy the changes to restart.",
"restartRequired": "Node-RED must be restarted to enable upgraded modules",
"credentials_load_failed": "<p>Flows stopped as the credentials could not be decrypted.</p><p>The flow credential file is encrypted, but the project's encryption key is missing or invalid.</p>",
"credentials_load_failed_reset":"<p>Credentials could not be decrypted</p><p>The flow credential file is encrypted, but the project's encryption key is missing or invalid.</p><p>The flow credential file will be reset on the next deployment. Any existing flow credentials will be cleared.</p>",
@@ -172,7 +173,10 @@
"modifiedFlowsDesc": "Only deploys flows that contain changed nodes",
"modifiedNodes": "Modified Nodes",
"modifiedNodesDesc": "Only deploys nodes that have changed",
"restartFlows": "Restart Flows",
"restartFlowsDesc": "Restarts the current deployed flows",
"successfulDeploy": "Successfully deployed",
"successfulRestart": "Successfully restarted flows",
"deployFailed": "Deploy failed: __message__",
"unusedConfigNodes":"You have some unused configuration nodes.",
"unusedConfigNodesLink":"Click here to see them",
@@ -267,6 +271,7 @@
"defaultLabel": "use default label",
"searchIcons": "Search icons",
"useDefault": "use default",
"description": "Description",
"errors": {
"scopeChange": "Changing the scope will make it unavailable to nodes in other flows that use it"
}
@@ -433,6 +438,7 @@
"instances": "Instances",
"properties": "Properties",
"info": "Information",
"desc": "Description",
"blank": "blank",
"null": "null",
"showMore": "show more",
@@ -456,6 +462,7 @@
"flows": "flows",
"filterUnused":"unused",
"filterAll":"all",
"deleteUnused":"Delete unused",
"filtered": "__count__ hidden"
},
"context": {
@@ -587,6 +594,7 @@
"pullUnrelatedHistory": "<p>The remote has an unrelated history of commits.</p><p>Are you sure you want to pull the changes into your local repository?</p>",
"pullChanges": "Pull changes",
"history": "history",
"projectHistory": "Project History",
"daysAgo": "__count__ day ago",
"daysAgo_plural": "__count__ days ago",
"hoursAgo": "__count__ hour ago",

View File

@@ -266,6 +266,7 @@
"defaultLabel": "既定の名前を使用",
"searchIcons": "アイコンを検索",
"useDefault": "デフォルトを使用",
"description": "詳細",
"errors": {
"scopeChange": "スコープの変更は、他のフローで使われているノードを無効にします"
}
@@ -585,6 +586,7 @@
"pullUnrelatedHistory": "<p>リモートに関連のないコミット履歴があります。</p><p>本当に変更をプルしてローカルリポジトリに反映しますか?</p>",
"pullChanges": "プル変更",
"history": "履歴",
"projectHistory": "プロジェクト履歴",
"daysAgo": "__count__ 日前",
"daysAgo_plural": "__count__ 日前",
"hoursAgo": "__count__ 時間前",

View File

@@ -0,0 +1,507 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var express = require("express");
var apiUtils = require("../util");
var runtimeAPI;
var needsPermission = require("../auth").needsPermission;
module.exports = {
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI;
},
app: function() {
var app = express();
app.use(function(req,res,next) {
runtimeAPI.projects.available().then(function(available) {
if (!available) {
res.status(404).end();
} else {
next();
}
})
});
// Projects
// List all projects
app.get("/", needsPermission("projects.read"), function(req,res) {
var opts = {
user: req.user
}
runtimeAPI.projects.listProjects(opts).then(function(result) {
res.json(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
});
});
// Create project
app.post("/", needsPermission("projects.write"), function(req,res) {
var opts = {
user: req.user,
project: req.body
}
runtimeAPI.projects.createProject(opts).then(function(result) {
res.json(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
});
});
// Update a project
app.put("/:id", needsPermission("projects.write"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id,
project: req.body
}
if (req.body.active) {
runtimeAPI.projects.setActiveProject(opts).then(function() {
res.redirect(303,req.baseUrl + '/');
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
} else if (req.body.initialise) {
runtimeAPI.projects.initialiseProject(opts).then(function() {
res.redirect(303,req.baseUrl + '/'+ req.params.id);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
} else if (req.body.hasOwnProperty('credentialSecret') ||
req.body.hasOwnProperty('description') ||
req.body.hasOwnProperty('dependencies')||
req.body.hasOwnProperty('summary') ||
req.body.hasOwnProperty('files') ||
req.body.hasOwnProperty('git')) {
runtimeAPI.projects.updateProject(opts).then(function() {
res.redirect(303,req.baseUrl + '/'+ req.params.id);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
} else {
res.status(400).json({error:"unexpected_error", message:"invalid_request"});
}
});
// Get project metadata
app.get("/:id", needsPermission("projects.read"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id
}
runtimeAPI.projects.getProject(opts).then(function(data) {
if (data) {
res.json(data);
} else {
res.status(404).end();
}
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Delete project
app.delete("/:id", needsPermission("projects.write"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id
}
runtimeAPI.projects.deleteProject(opts).then(function() {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Get project status - files, commit counts, branch info
app.get("/:id/status", needsPermission("projects.read"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id,
remote: req.query.remote
}
runtimeAPI.projects.getStatus(opts).then(function(data){
if (data) {
res.json(data);
} else {
res.status(404).end();
}
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Project file listing
app.get("/:id/files", needsPermission("projects.read"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id
}
runtimeAPI.projects.getFiles(opts).then(function(data) {
res.json(data);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Get file content in a given tree (index/stage)
app.get("/:id/files/:treeish/*", needsPermission("projects.read"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id,
path: req.params[0],
tree: req.params.treeish
}
runtimeAPI.projects.getFile(opts).then(function(data) {
res.json({content:data});
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Revert a file
app.delete("/:id/files/_/*", needsPermission("projects.write"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id,
path: req.params[0]
}
runtimeAPI.projects.revertFile(opts).then(function() {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Stage a file
app.post("/:id/stage/*", needsPermission("projects.write"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id,
path: req.params[0]
}
runtimeAPI.projects.stageFile(opts).then(function() {
res.redirect(303,req.baseUrl+"/"+opts.id+"/status");
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Stage multiple files
app.post("/:id/stage", needsPermission("projects.write"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id,
path: req.body.files
}
runtimeAPI.projects.stageFile(opts).then(function() {
res.redirect(303,req.baseUrl+"/"+opts.id+"/status");
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Commit changes
app.post("/:id/commit", needsPermission("projects.write"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id,
message: req.body.message
}
runtimeAPI.projects.commit(opts).then(function() {
res.redirect(303,req.baseUrl+"/"+opts.id+"/status");
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Unstage a file
app.delete("/:id/stage/*", needsPermission("projects.write"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id,
path: req.params[0]
}
runtimeAPI.projects.unstageFile(opts).then(function() {
res.redirect(303,req.baseUrl+"/"+opts.id+"/status");
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Unstage multiple files
app.delete("/:id/stage", needsPermission("projects.write"), function(req, res) {
var opts = {
user: req.user,
id: req.params.id
}
runtimeAPI.projects.unstageFile(opts).then(function() {
res.redirect(303,req.baseUrl+"/"+opts.id+"/status");
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Get a file diff
app.get("/:id/diff/:type/*", needsPermission("projects.read"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id,
path: req.params[0],
type: req.params.type
}
runtimeAPI.projects.getFileDiff(opts).then(function(data) {
res.json({
diff: data
})
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Get a list of commits
app.get("/:id/commits", needsPermission("projects.read"), function(req, res) {
var opts = {
user: req.user,
id: req.params.id,
limit: req.query.limit || 20,
before: req.query.before
}
runtimeAPI.projects.getCommits(opts).then(function(data) {
res.json(data);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Get an individual commit details
app.get("/:id/commits/:sha", needsPermission("projects.read"), function(req, res) {
var opts = {
user: req.user,
id: req.params.id,
sha: req.params.sha
}
runtimeAPI.projects.getCommit(opts).then(function(data) {
res.json({commit:data});
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Push local commits to remote
app.post("/:id/push/?*", needsPermission("projects.write"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id,
remote: req.params[0],
track: req.query.u
}
runtimeAPI.projects.push(opts).then(function(data) {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Pull remote commits
app.post("/:id/pull/?*", needsPermission("projects.write"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id,
remote: req.params[0],
track: req.query.setUpstream,
allowUnrelatedHistories: req.query.allowUnrelatedHistories
}
runtimeAPI.projects.pull(opts).then(function(data) {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Abort an ongoing merge
app.delete("/:id/merge", needsPermission("projects.write"), function(req, res) {
var opts = {
user: req.user,
id: req.params.id
}
runtimeAPI.projects.abortMerge(opts).then(function() {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Resolve a merge
app.post("/:id/resolve/*", needsPermission("projects.write"), function(req, res) {
var opts = {
user: req.user,
id: req.params.id,
path: req.params[0],
resolution: req.body.resolutions
}
runtimeAPI.projects.resolveMerge(opts).then(function() {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Get a list of local branches
app.get("/:id/branches", needsPermission("projects.read"), function(req, res) {
var opts = {
user: req.user,
id: req.params.id,
remote: false
}
runtimeAPI.projects.getBranches(opts).then(function(data) {
res.json(data);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Delete a local branch - ?force=true
app.delete("/:id/branches/:branchName", needsPermission("projects.write"), function(req, res) {
var opts = {
user: req.user,
id: req.params.id,
branch: req.params.branchName,
force: !!req.query.force
}
runtimeAPI.projects.deleteBranch(opts).then(function(data) {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Get a list of remote branches
app.get("/:id/branches/remote", needsPermission("projects.read"), function(req, res) {
var opts = {
user: req.user,
id: req.params.id,
remote: true
}
runtimeAPI.projects.getBranches(opts).then(function(data) {
res.json(data);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Get branch status - commit counts/ahead/behind
app.get("/:id/branches/remote/*/status", needsPermission("projects.read"), function(req, res) {
var opts = {
user: req.user,
id: req.params.id,
branch: req.params[0]
}
runtimeAPI.projects.getBranchStatus(opts).then(function(data) {
res.json(data);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Set the active local branch
app.post("/:id/branches", needsPermission("projects.write"), function(req, res) {
var opts = {
user: req.user,
id: req.params.id,
branch: req.body.name,
create: req.body.create
}
runtimeAPI.projects.setBranch(opts).then(function(data) {
res.json(data);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Get a list of remotes
app.get("/:id/remotes", needsPermission("projects.read"), function(req, res) {
var opts = {
user: req.user,
id: req.params.id
}
runtimeAPI.projects.getRemotes(opts).then(function(data) {
res.json(data);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Add a remote
app.post("/:id/remotes", needsPermission("projects.write"), function(req,res) {
var opts = {
user: req.user,
id: req.params.id,
remote: req.body
}
if (/^https?:\/\/[^/]+@/i.test(req.body.url)) {
res.status(400).json({error:"unexpected_error", message:"Git http url must not include username/password"});
return;
}
runtimeAPI.projects.addRemote(opts).then(function(data) {
res.redirect(303,req.baseUrl+"/"+opts.id+"/remotes");
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Delete a remote
app.delete("/:id/remotes/:remoteName", needsPermission("projects.write"), function(req, res) {
var opts = {
user: req.user,
id: req.params.id,
remote: req.params.remoteName
}
runtimeAPI.projects.removeRemote(opts).then(function(data) {
res.redirect(303,req.baseUrl+"/"+opts.id+"/remotes");
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
// Update a remote
app.put("/:id/remotes/:remoteName", needsPermission("projects.write"), function(req,res) {
var remote = req.body || {};
remote.name = req.params.remoteName;
var opts = {
user: req.user,
id: req.params.id,
remote: remote
}
runtimeAPI.projects.updateRemote(opts).then(function() {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
});
return app;
}
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var apiUtils = require("../util");
var runtimeAPI;
var sshkeys = require("./sshkeys");
var theme = require("./theme");
module.exports = {
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI;
sshkeys.init(runtimeAPI);
},
runtimeSettings: function(req,res) {
var opts = {
user: req.user
}
runtimeAPI.settings.getRuntimeSettings(opts).then(function(result) {
var themeSettings = theme.settings();
if (themeSettings) {
result.editorTheme = themeSettings;
}
res.json(result);
});
},
userSettings: function(req, res) {
var opts = {
user: req.user
}
runtimeAPI.settings.getUserSettings(opts).then(function(result) {
res.json(result);
});
},
updateUserSettings: function(req,res) {
var opts = {
user: req.user,
settings: req.body
}
runtimeAPI.settings.updateUserSettings(opts).then(function(result) {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
});
},
sshkeys: function() {
return sshkeys.app()
}
}

View File

@@ -0,0 +1,101 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var apiUtils = require("../util");
var express = require("express");
var runtimeAPI;
function getUsername(userObj) {
var username = '__default';
if ( userObj && userObj.name ) {
username = userObj.name;
}
return username;
}
module.exports = {
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI;
},
app: function() {
var app = express();
// List all SSH keys
app.get("/", function(req,res) {
var opts = {
user: req.user
}
runtimeAPI.settings.getUserKeys(opts).then(function(list) {
res.json({
keys: list
});
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
});
});
// Get SSH key detail
app.get("/:id", function(req,res) {
var opts = {
user: req.user,
id: req.params.id
}
runtimeAPI.settings.getUserKey(opts).then(function(data) {
res.json({
publickey: data
});
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
});
});
// Generate a SSH key
app.post("/", function(req,res) {
var opts = {
user: req.user,
id: req.params.id
}
// TODO: validate params
opts.name = req.body.name;
opts.password = req.body.password;
opts.comment = req.body.comment;
opts.size = req.body.size;
runtimeAPI.settings.generateUserKey(opts).then(function(name) {
res.json({
name: name
});
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
});
});
// Delete a SSH key
app.delete("/:id", function(req,res) {
var opts = {
user: req.user,
id: req.params.id
}
runtimeAPI.settings.removeUserKey(opts).then(function(name) {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
});
});
return app;
}
}

View File

@@ -40,7 +40,6 @@ var defaultContext = {
var theme = null;
var themeContext = clone(defaultContext);
var themeSettings = null;
var runtime = null;
var themeApp;
@@ -78,12 +77,8 @@ function serveFilesFromTheme(themeValue, themeApp, directory) {
}
module.exports = {
init: function(runtime) {
var settings = runtime.settings;
init: function(settings) {
themeContext = clone(defaultContext);
if (runtime.version) {
themeContext.version = runtime.version();
}
themeSettings = null;
theme = settings.editorTheme || {};
},

View File

@@ -17,18 +17,22 @@ var express = require('express');
var fs = require("fs");
var path = require("path");
var Mustache = require("mustache");
var mime = require("mime");
var apiUtils = require("../util");
var theme = require("./theme");
var redNodes;
var templateDir = path.resolve(__dirname+"/../../../editor/templates");
var runtimeAPI;
var editorClientDir = path.dirname(require.resolve("@node-red/editor-client"));
var defaultNodeIcon = path.join(editorClientDir,"public","red","images","icons","arrow-in.png");
var editorTemplatePath = path.join(editorClientDir,"templates","index.mst");
var editorTemplate;
module.exports = {
init: function(runtime) {
redNodes = runtime.nodes;
editorTemplate = fs.readFileSync(path.join(templateDir,"index.mst"),"utf8");
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI;
editorTemplate = fs.readFileSync(editorTemplatePath,"utf8");
Mustache.parse(editorTemplate);
},
@@ -46,11 +50,26 @@ module.exports = {
var icon = req.params.icon;
var scope = req.params.scope;
var module = scope ? scope + '/' + req.params.module : req.params.module;
var iconPath = redNodes.getNodeIconPath(module,icon);
res.sendFile(iconPath);
var opts = {
user: req.user,
module: module,
icon: icon
}
runtimeAPI.nodes.getIcon(opts).then(function(data) {
if (data) {
var contentType = mime.lookup(icon);
res.set("Content-Type", contentType);
res.send(data);
} else {
res.sendFile(defaultNodeIcon);
}
}).catch(function(err) {
console.log(err.stack);
apiUtils.rejectHandler(req,res,err);
})
},
editor: function(req,res) {
res.send(Mustache.render(editorTemplate,theme.context()));
},
editorResources: express.static(__dirname + '/../../../public')
editorResources: express.static(path.join(editorClientDir,'public'))
};

View File

@@ -26,17 +26,21 @@ var apiUtil = require("./util");
var adminApp;
var server;
var runtime;
var editor;
function init(_server,_runtime) {
function init(_server,settings,storage,runtimeAPI) {
server = _server;
runtime = _runtime;
var settings = runtime.settings;
if (settings.httpAdminRoot !== false) {
apiUtil.init(runtime);
adminApp = express();
auth.init(runtime);
var cors = require('cors');
var corsHandler = cors({
origin: "*",
methods: "GET,PUT,POST,DELETE"
});
adminApp.use(corsHandler);
auth.init(settings,storage);
var maxApiRequestSize = settings.apiMaxLength || '5mb';
adminApp.use(bodyParser.json({limit:maxApiRequestSize}));
@@ -61,7 +65,7 @@ function init(_server,_runtime) {
// Editor
if (!settings.disableEditor) {
editor = require("./editor");
var editorApp = editor.init(server, runtime);
var editorApp = editor.init(server, settings, runtimeAPI);
adminApp.use(editorApp);
}
@@ -70,7 +74,7 @@ function init(_server,_runtime) {
adminApp.use(corsHandler);
}
var adminApiApp = require("./admin").init(runtime);
var adminApiApp = require("./admin").init(runtimeAPI);
adminApp.use(adminApiApp);
} else {
adminApp = null;
@@ -93,23 +97,9 @@ module.exports = {
init: init,
start: start,
stop: stop,
library: {
register: function(type) {
if (editor) {
editor.registerLibrary(type);
}
}
},
auth: {
needsPermission: auth.needsPermission
},
comms: {
publish: function(topic,data,retain) {
if (editor) {
editor.publish(topic,data,retain);
}
}
},
get adminApp() { return adminApp; },
get server() { return server; }
};

View File

@@ -15,16 +15,12 @@
**/
var i18n;
var log;
var log = require("@node-red/util").log; // TODO: separate module
var i18n = require("@node-red/util").i18n; // TODO: separate module
module.exports = {
init: function(_runtime) {
log = _runtime.log;
i18n = _runtime.i18n;
},
errorHandler: function(err,req,res,next) {
console.error(err.stack);
if (err.message === "request entity too large") {
log.error(err);
} else {
@@ -41,5 +37,11 @@ module.exports = {
lang = acceptedLanguages[0];
}
return lang;
},
rejectHandler: function(req,res,err) {
res.status(err.status||500).json({
code: err.code||"unexpected_error",
message: err.message||err.toString()
});
}
}

View File

@@ -0,0 +1,33 @@
{
"name": "@node-red/editor",
"version": "0.20.0-alpha.0",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
"type": "git",
"url": "https://github.com/node-red/node-red.git"
},
"contributors": [
{ "name": "Nick O'Leary" },
{ "name": "Dave Conway-Jones"}
],
"dependencies": {
"@node-red/util": "*",
"@node-red/editor-client": "*",
"bcryptjs": "2.4.3",
"body-parser": "1.18.3",
"clone": "2.1.2",
"cors": "2.8.4",
"express-session": "1.15.6",
"express": "4.16.3",
"memorystore": "1.6.0",
"mime": "1.4.1",
"mustache": "2.3.2",
"oauth2orize": "1.11.0",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"passport": "0.4.0",
"when": "3.7.8",
"ws": "1.1.5"
}
}

View File

@@ -0,0 +1 @@
src

View File

@@ -0,0 +1,178 @@
Copyright JS Foundation and other contributors, http://js.foundation
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@@ -0,0 +1,10 @@
@node-red/editor-client
====================
Node-RED editor resources module.
This provides all of the client-side resources of the Node-RED editor application.
### Source
The main Node-RED modules are maintained as a monorepo on [GitHub](https://github.com/node-red/node-red).

View File

@@ -13,4 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = {};
module.exports = false

View File

@@ -0,0 +1,14 @@
{
"name": "@node-red/editor-client",
"version": "0.20.0-alpha.0",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/node-red/node-red.git"
},
"contributors": [
{ "name": "Nick O'Leary" },
{ "name": "Dave Conway-Jones"}
],
"main": "./lib/index.js"
}

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 291 B

After

Width:  |  Height:  |  Size: 291 B

View File

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 386 B

View File

Before

Width:  |  Height:  |  Size: 289 B

After

Width:  |  Height:  |  Size: 289 B

View File

Before

Width:  |  Height:  |  Size: 368 B

After

Width:  |  Height:  |  Size: 368 B

View File

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 290 B

View File

Before

Width:  |  Height:  |  Size: 392 B

After

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1015 B

View File

Before

Width:  |  Height:  |  Size: 192 B

After

Width:  |  Height:  |  Size: 192 B

View File

Before

Width:  |  Height:  |  Size: 393 B

After

Width:  |  Height:  |  Size: 393 B

View File

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 386 B

View File

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 386 B

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1019 B

After

Width:  |  Height:  |  Size: 1019 B

View File

Before

Width:  |  Height:  |  Size: 600 B

After

Width:  |  Height:  |  Size: 600 B

View File

Before

Width:  |  Height:  |  Size: 410 B

After

Width:  |  Height:  |  Size: 410 B

View File

Before

Width:  |  Height:  |  Size: 638 B

After

Width:  |  Height:  |  Size: 638 B

View File

Before

Width:  |  Height:  |  Size: 546 B

After

Width:  |  Height:  |  Size: 546 B

View File

Before

Width:  |  Height:  |  Size: 638 B

After

Width:  |  Height:  |  Size: 638 B

View File

Before

Width:  |  Height:  |  Size: 646 B

After

Width:  |  Height:  |  Size: 646 B

View File

Before

Width:  |  Height:  |  Size: 809 B

After

Width:  |  Height:  |  Size: 809 B

View File

Before

Width:  |  Height:  |  Size: 563 B

After

Width:  |  Height:  |  Size: 563 B

View File

Before

Width:  |  Height:  |  Size: 588 B

After

Width:  |  Height:  |  Size: 588 B

View File

Before

Width:  |  Height:  |  Size: 502 B

After

Width:  |  Height:  |  Size: 502 B

View File

@@ -28,14 +28,24 @@ RED.comms = (function() {
function connectWS() {
active = true;
var path = location.hostname;
var port = location.port;
if (port.length !== 0) {
path = path+":"+port;
var wspath;
if (RED.settings.apiRootUrl) {
var m = /^(https?):\/\/(.*)$/.exec(RED.settings.apiRootUrl);
if (m) {
console.log(m);
wspath = "ws"+(m[1]==="https"?"s":"")+"://"+m[2]+"comms";
}
} else {
var path = location.hostname;
var port = location.port;
if (port.length !== 0) {
path = path+":"+port;
}
path = path+document.location.pathname;
path = path+(path.slice(-1) == "/"?"":"/")+"comms";
wspath = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path;
}
path = path+document.location.pathname;
path = path+(path.slice(-1) == "/"?"":"/")+"comms";
path = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path;
var auth_tokens = RED.settings.get("auth-tokens");
pendingAuth = (auth_tokens!=null);
@@ -48,7 +58,7 @@ RED.comms = (function() {
}
}
ws = new WebSocket(path);
ws = new WebSocket(wspath);
ws.onopen = function() {
reconnectAttempts = 0;
if (errornotification) {

View File

@@ -16,10 +16,13 @@
RED.i18n = (function() {
var apiRootUrl;
return {
init: function(done) {
init: function(options, done) {
apiRootUrl = options.apiRootUrl||"";
i18n.init({
resGetPath: 'locales/__ns__?lng=__lng__',
resGetPath: apiRootUrl+'locales/__ns__?lng=__lng__',
dynamicLoad: false,
load:'current',
ns: {
@@ -36,7 +39,7 @@ RED.i18n = (function() {
}
},
loadCatalog: function(namespace,done) {
loadNodeCatalog: function(namespace,done) {
var languageList = i18n.functions.toLanguages(i18n.detectLanguage());
var toLoad = languageList.length;
languageList.forEach(function(lang) {
@@ -45,7 +48,7 @@ RED.i18n = (function() {
"Accept":"application/json"
},
cache: false,
url: 'locales/'+namespace+'?lng='+lang,
url: apiRootUrl+'nodes/'+namespace+'/messages?lng='+lang,
success: function(data) {
i18n.addResourceBundle(lang,namespace,data);
toLoad--;
@@ -68,7 +71,7 @@ RED.i18n = (function() {
"Accept":"application/json"
},
cache: false,
url: 'locales/nodes?lng='+lang,
url: apiRootUrl+'nodes/messages?lng='+lang,
success: function(data) {
var namespaces = Object.keys(data);
namespaces.forEach(function(ns) {

View File

@@ -0,0 +1,24 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
$(function() {
if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) {
document.title = document.title+" : "+window.location.hostname;
}
RED.init({
apiRootUrl: ""
});
});

View File

@@ -354,7 +354,6 @@ RED.nodes = (function() {
subflows[sf.id] = sf;
RED.nodes.registerType("subflow:"+sf.id, {
defaults:{name:{value:""}},
info: sf.info,
icon: function() { return sf.icon||"subflow.png" },
category: sf.category || "subflows",
inputs: sf.in.length,
@@ -510,6 +509,9 @@ RED.nodes = (function() {
}
}
}
if (n.info) {
node.info = n.info;
}
return node;
}
@@ -904,7 +906,14 @@ RED.nodes = (function() {
}
if (!existingConfigNode || existingConfigNode._def.exclusive) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode.z !== n.z) {
configNode = {id:n.id, z:n.z, type:n.type, users:[], _config:{}};
configNode = {
id:n.id,
z:n.z,
type:n.type,
info: n.info,
users:[],
_config:{}
};
for (d in def.defaults) {
if (def.defaults.hasOwnProperty(d)) {
configNode[d] = n[d];
@@ -947,6 +956,7 @@ RED.nodes = (function() {
inputLabels: n.inputLabels,
outputLabels: n.outputLabels,
icon: n.icon,
info: n.info,
changed:false,
_config:{}
};

View File

@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
(function() {
var RED = (function() {
function appendNodeConfig(nodeConfig) {
function appendNodeConfig(nodeConfig,done) {
done = done || function(){};
var m = /<!-- --- \[red-module:(\S+)\] --- -->/.exec(nodeConfig.trim());
var moduleId;
if (m) {
@@ -24,13 +25,31 @@
moduleId = "unknown";
}
try {
$("body").append(nodeConfig);
var hasDeferred = false;
var nodeConfigEls = $("<div>"+nodeConfig+"</div>");
nodeConfigEls.find("script").each(function(i,el) {
var srcUrl = $(el).attr('src');
if (srcUrl && !/^\s*(https?:|\/|\.)/.test(srcUrl)) {
$(el).remove();
var newScript = document.createElement("script");
newScript.onload = function() { $("body").append(nodeConfigEls); done() }
$('body').append(newScript);
newScript.src = RED.settings.apiRootUrl+srcUrl;
hasDeferred = true;
}
})
if (!hasDeferred) {
$("body").append(nodeConfigEls);
done();
}
} catch(err) {
RED.notify(RED._("notification.errors.failedToAppendNode",{module:moduleId, error:err.toString()}),{
type: "error",
timeout: 10000
});
console.log("["+moduleId+"] "+err.toString());
done();
}
}
@@ -75,36 +94,40 @@
url: 'nodes',
success: function(data) {
var configs = data.trim().split(/(?=<!-- --- \[red-module:\S+\] --- -->)/);
configs.forEach(function(data) {
appendNodeConfig(data);
});
$("body").i18n();
$("#palette > .palette-spinner").hide();
$(".palette-scroll").removeClass("hide");
$("#palette-search").removeClass("hide");
loadFlows(function() {
if (RED.settings.theme("projects.enabled",false)) {
RED.projects.refresh(function(activeProject) {
RED.sidebar.info.refresh()
if (!activeProject) {
// Projects enabled but no active project
RED.menu.setDisabled('menu-item-projects-open',true);
RED.menu.setDisabled('menu-item-projects-settings',true);
if (activeProject === false) {
// User previously decline the migration to projects.
} else { // null/undefined
RED.projects.showStartup();
}
var stepConfig = function() {
if (configs.length === 0) {
$("body").i18n();
$("#palette > .palette-spinner").hide();
$(".palette-scroll").removeClass("hide");
$("#palette-search").removeClass("hide");
loadFlows(function() {
if (RED.settings.theme("projects.enabled",false)) {
RED.projects.refresh(function(activeProject) {
RED.sidebar.info.refresh()
if (!activeProject) {
// Projects enabled but no active project
RED.menu.setDisabled('menu-item-projects-open',true);
RED.menu.setDisabled('menu-item-projects-settings',true);
if (activeProject === false) {
// User previously decline the migration to projects.
} else { // null/undefined
RED.projects.showStartup();
}
}
completeLoad();
});
} else {
// Projects disabled by the user
RED.sidebar.info.refresh()
completeLoad();
}
completeLoad();
});
} else {
// Projects disabled by the user
RED.sidebar.info.refresh()
completeLoad();
var config = configs.shift();
appendNodeConfig(config,stepConfig);
}
});
}
stepConfig();
}
});
}
@@ -178,7 +201,16 @@
id: notificationId
}
if (notificationId === "runtime-state") {
if (msg.error === "missing-types") {
if (msg.error === "safe-mode") {
options.buttons = [
{
text: RED._("common.label.close"),
click: function() {
persistentNotifications[notificationId].hideNotification();
}
}
]
} else if (msg.error === "missing-types") {
text+="<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
if (!!RED.projects.getActiveProject()) {
options.buttons = [
@@ -190,7 +222,7 @@
}
}
]
// } else if (RED.settings.theme('palette.editable') !== false) {
// } else if (RED.settings.theme('palette.editable') !== false) {
} else {
options.buttons = [
{
@@ -207,7 +239,7 @@
if (RED.user.hasPermission("projects.write")) {
options.buttons = [
{
text: RED._("notification.label.setup-cred"),
text: "Setup credentials",
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.projects.showCredentialsPrompt();
@@ -218,7 +250,7 @@
} else {
options.buttons = [
{
text: RED._("common.label.close"),
text: "Close",
click: function() {
persistentNotifications[notificationId].hideNotification();
}
@@ -229,7 +261,7 @@
if (RED.user.hasPermission("projects.write")) {
options.buttons = [
{
text: RED._("notification.label.setup-project"),
text: "Setup project files",
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.projects.showFilesPrompt();
@@ -241,7 +273,7 @@
if (RED.user.hasPermission("projects.write")) {
options.buttons = [
{
text: RED._("notification.label.create-default-package"),
text: "Create default package file",
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.projects.createDefaultPackageFile();
@@ -253,13 +285,13 @@
if (RED.user.hasPermission("projects.write")) {
options.buttons = [
{
text: RED._("notification.label.no-thanks"),
text: "No thanks",
click: function() {
persistentNotifications[notificationId].hideNotification();
}
},
{
text: RED._("notification.label.create-default-project"),
text: "Create default project files",
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.projects.createDefaultFileSet();
@@ -273,7 +305,7 @@
if (RED.user.hasPermission("projects.write")) {
options.buttons = [
{
text: RED._("notification.label.show-merge-conflicts"),
text: "Show merge conflicts",
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.sidebar.versionControl.showLocalChanges();
@@ -317,7 +349,7 @@
var id = m.id;
RED.nodes.addNodeSet(m);
addedTypes = addedTypes.concat(m.types);
RED.i18n.loadCatalog(id, function() {
RED.i18n.loadNodeCatalog(id, function() {
$.get('nodes/'+id, function(data) {
appendNodeConfig(data);
});
@@ -371,8 +403,8 @@
function showAbout() {
$.get('red/about', function(data) {
var aboutHeader = '<div style="text-align:center;">'+
'<img width="50px" src="red/images/node-red-icon.svg" />'+
'</div>';
'<img width="50px" src="red/images/node-red-icon.svg" />'+
'</div>';
RED.sidebar.info.set(aboutHeader+marked(data));
RED.sidebar.info.show();
@@ -382,10 +414,10 @@
function loadEditor() {
var menuOptions = [];
if (RED.settings.theme("projects.enabled",false)) {
menuOptions.push({id:"menu-item-projects-menu",label:RED._("menu.label.projects"),options:[
{id:"menu-item-projects-new",label:RED._("menu.label.projects-new"),disabled:false,onselect:"core:new-project"},
{id:"menu-item-projects-open",label:RED._("menu.label.projects-open"),disabled:false,onselect:"core:open-project"},
{id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"}
menuOptions.push({id:"menu-item-projects-menu",label:"Projects",options:[
{id:"menu-item-projects-new",label:"New",disabled:false,onselect:"core:new-project"},
{id:"menu-item-projects-open",label:"Open",disabled:false,onselect:"core:open-project"},
{id:"menu-item-projects-settings",label:"Project Settings",disabled:false,onselect:"core:show-project-settings"}
]});
}
@@ -486,16 +518,25 @@
loadNodeList();
}
$(function() {
var initialised = false;
if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) {
document.title = document.title+" : "+window.location.hostname;
function init(options) {
if (initialised) {
throw new Error("RED already initialised");
}
initialised = true;
ace.require("ace/ext/language_tools");
RED.i18n.init(function() {
RED.settings.init(loadEditor);
options = options || {};
options.apiRootUrl = options.apiRootUrl || "";
if (options.apiRootUrl && !/\/$/.test(options.apiRootUrl)) {
options.apiRootUrl = options.apiRootUrl+"/";
}
RED.i18n.init(options, function() {
RED.settings.init(options, loadEditor);
})
});
}
return {
init: init
}
})();

View File

@@ -89,18 +89,22 @@ RED.settings = (function () {
userSettings = data;
}
var init = function (done) {
var init = function (options, done) {
var accessTokenMatch = /[?&]access_token=(.*?)(?:$|&)/.exec(window.location.search);
if (accessTokenMatch) {
var accessToken = accessTokenMatch[1];
RED.settings.set("auth-tokens",{access_token: accessToken});
window.location.search = "";
}
RED.settings.apiRootUrl = options.apiRootUrl;
$.ajaxSetup({
beforeSend: function(jqXHR,settings) {
// Only attach auth header for requests to relative paths
if (!/^\s*(https?:|\/|\.)/.test(settings.url)) {
if (options.apiRootUrl) {
settings.url = options.apiRootUrl+settings.url;
}
var auth_tokens = RED.settings.get("auth-tokens");
if (auth_tokens) {
jqXHR.setRequestHeader("Authorization","Bearer "+auth_tokens.access_token);

View File

@@ -0,0 +1,102 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.panels = (function() {
function createPanel(options) {
var container = options.container || $("#"+options.id);
var children = container.children();
if (children.length !== 2) {
throw new Error("Container must have exactly two children");
}
var vertical = (!options.dir || options.dir === "vertical");
container.addClass("red-ui-panels");
if (!vertical) {
container.addClass("red-ui-panels-horizontal");
}
var separator = $('<div class="red-ui-panels-separator"></div>').insertAfter(children[0]);
var startPosition;
var panelSizes = [];
var modifiedSizes = false;
var panelRatio;
separator.draggable({
axis: vertical?"y":"x",
containment: container,
scroll: false,
start:function(event,ui) {
startPosition = vertical?ui.position.top:ui.position.left;
panelSizes = [
vertical?$(children[0]).height():$(children[0]).width(),
vertical?$(children[1]).height():$(children[1]).width()
];
},
drag: function(event,ui) {
var size = vertical?container.height():container.width();
var delta = (vertical?ui.position.top:ui.position.left)-startPosition;
var newSizes = [panelSizes[0]+delta,panelSizes[1]-delta];
if (vertical) {
$(children[0]).height(newSizes[0]);
$(children[1]).height(newSizes[1]);
ui.position.top -= delta;
} else {
$(children[0]).width(newSizes[0]);
$(children[1]).width(newSizes[1]);
ui.position.left -= delta;
}
if (options.resize) {
options.resize(newSizes[0],newSizes[1]);
}
panelRatio = newSizes[0]/size;
},
stop:function(event,ui) {
modifiedSizes = true;
}
});
return {
resize: function(size) {
var panelSizes = [$(children[0]).height(),$(children[1]).height()];
if (vertical) {
container.height(size);
} else {
container.width(size);
}
if (modifiedSizes) {
var topPanelSize = panelRatio*size;
var bottomPanelSize = size - topPanelSize - 48;
panelSizes = [topPanelSize,bottomPanelSize];
if (vertical) {
$(children[0]).height(panelSizes[0]);
$(children[1]).height(panelSizes[1]);
} else {
$(children[0]).width(panelSizes[0]);
$(children[1]).width(panelSizes[1]);
}
}
if (options.resize) {
options.resize(panelSizes[0],panelSizes[1]);
}
}
}
}
return {
create: createPanel
}
})();

Some files were not shown because too many files have changed in this diff Show More