summaryrefslogtreecommitdiffstats
path: root/dgbuilder/red
diff options
context:
space:
mode:
Diffstat (limited to 'dgbuilder/red')
-rw-r--r--dgbuilder/red/cli/lib/config.js53
-rw-r--r--dgbuilder/red/cli/lib/request.js51
-rwxr-xr-xdgbuilder/red/cli/nr-cli.js151
-rw-r--r--dgbuilder/red/comms.js132
-rw-r--r--dgbuilder/red/events.js19
-rw-r--r--dgbuilder/red/library.js117
-rw-r--r--dgbuilder/red/log.js39
-rw-r--r--dgbuilder/red/nodes/Node.js147
-rw-r--r--dgbuilder/red/nodes/credentials.js208
-rw-r--r--dgbuilder/red/nodes/flows.js220
-rw-r--r--dgbuilder/red/nodes/index.js134
-rw-r--r--dgbuilder/red/nodes/registry.js693
-rw-r--r--dgbuilder/red/red.js68
-rw-r--r--dgbuilder/red/server.js1317
-rw-r--r--dgbuilder/red/settings.js84
-rw-r--r--dgbuilder/red/sla.js249
-rw-r--r--dgbuilder/red/storage/index.js107
-rw-r--r--dgbuilder/red/storage/localfilesystem.js309
-rw-r--r--dgbuilder/red/ui.js77
-rw-r--r--dgbuilder/red/util.js43
20 files changed, 4218 insertions, 0 deletions
diff --git a/dgbuilder/red/cli/lib/config.js b/dgbuilder/red/cli/lib/config.js
new file mode 100644
index 00000000..3cd5244d
--- /dev/null
+++ b/dgbuilder/red/cli/lib/config.js
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * 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 path = require("path");
+var fs = require("fs");
+
+var userHome = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
+
+var configDir = path.join(userHome,".nodered");
+var configFile = path.join(configDir,"config.json");
+
+var config;
+
+function load() {
+ if (config == null) {
+ try {
+ config = JSON.parse(fs.readFileSync(configFile));
+ } catch(err) {
+ config = {};
+ }
+ }
+}
+
+function save() {
+ try {
+ fs.mkdirSync(configDir);
+ } catch(err) {
+ if (err.code != "EEXIST") {
+ throw err;
+ }
+ }
+ fs.writeFileSync(configFile,JSON.stringify(config,null,4));
+}
+module.exports = {
+ unload: function() {
+ config = null;
+ }
+};
+module.exports.__defineGetter__('target',function() { load(); return config.target|| "http://localhost:1880" });
+module.exports.__defineSetter__('target',function(v) { load(); config.target = v; save();});
diff --git a/dgbuilder/red/cli/lib/request.js b/dgbuilder/red/cli/lib/request.js
new file mode 100644
index 00000000..fbbe3dc6
--- /dev/null
+++ b/dgbuilder/red/cli/lib/request.js
@@ -0,0 +1,51 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * 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 when = require("when");
+var request = require("request");
+var config = require("./config");
+
+module.exports = function(path, options) {
+ var basePath = config.target;
+ return when.promise(function(resolve,reject) {
+ options.headers = options.headers||{};
+ options.headers['Accept'] = 'application/json';
+ if (options.method == 'PUT' || options.method == "POST") {
+ options.headers['content-type'] = 'application/json';
+ }
+ options.url = basePath+path;
+
+ // Pull out the request function so we can stub it in the tests
+ var requestFunc = request.get;
+
+ if (options.method == 'PUT') {
+ requestFunc = request.put;
+ } else if (options.method == 'POST') {
+ requestFunc = request.post;
+ } else if (options.method == 'DELETE') {
+ requestFunc = request.del;
+ }
+ requestFunc(options, function(error,response,body) {
+ if (!error && response.statusCode == 200) {
+ resolve(JSON.parse(body));
+ } else if (error) {
+ reject(error.toString());
+ } else {
+ reject(response.statusCode+": "+body)
+ }
+ });
+ });
+}
diff --git a/dgbuilder/red/cli/nr-cli.js b/dgbuilder/red/cli/nr-cli.js
new file mode 100755
index 00000000..6d62f05f
--- /dev/null
+++ b/dgbuilder/red/cli/nr-cli.js
@@ -0,0 +1,151 @@
+#!/usr/bin/env node
+;(function() {
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * 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 request = require("request");
+var colors = require('colors');
+var apiRequest = require("./lib/request");
+var config = require("./lib/config");
+
+var commands = {
+ "target": function() {
+ var target = process.argv[3];
+ if (target) {
+ if (!/^https?:\/\/.+/.test(target)) {
+ console.warn("Invalid target url");
+ return;
+ }
+ if (target.slice(-1) == "/") {
+ target = target.slice(0,target.length-1);
+ }
+ var oldTarget = config.target;
+ config.target = target;
+ } else {
+ console.log("Target: ".yellow+config.target);
+ }
+
+ },
+ "nodes": function() {
+ apiRequest('/nodes',{}).then(logNodeList).otherwise(logFailure);
+ },
+ "node": function() {
+ apiRequest('/nodes/'+process.argv[3],{}).then(logNodeList).otherwise(logFailure);
+ },
+ "enable-node": function() {
+ apiRequest('/nodes/'+process.argv[3],{
+ method: "PUT",
+ body: JSON.stringify({enabled:true})
+ }).then(logNodeList).otherwise(logFailure);
+ },
+ "disable-node": function() {
+ apiRequest('/nodes/'+process.argv[3],{
+ method: "PUT",
+ body: JSON.stringify({enabled:false})
+ }).then(logNodeList).otherwise(logFailure);
+ },
+ "install": function() {
+ apiRequest('/nodes',{
+ method: "POST",
+ body: JSON.stringify({module:process.argv[3]})
+ }).then(logNodeList).otherwise(logFailure);
+ },
+ "remove": function() {
+ apiRequest('/nodes/'+process.argv[3],{
+ method: "DELETE"
+ }).then(logNodeList).otherwise(logFailure);
+ },
+ "search": function() {
+ var options = {
+ method: "GET",
+ url: 'https://registry.npmjs.org/-/_view/byKeyword?startkey=["node-red"]&endkey=["node-red",{}]&group_level=3' ,
+ headers: {
+ 'Accept': 'application/json',
+ }
+ };
+ request(options, function (error, response, body) {
+ if (!error && response.statusCode == 200) {
+ var info = (JSON.parse(body)).rows;
+ var filter = null;
+ if (process.argv[3]) {
+ filter = new RegExp(process.argv[3]);
+ }
+ for (var i=0;i<info.length;i++) {
+ var n = info[i];
+ if (!filter || filter.test(n.key[1]) || filter.test(n.key[2])) {
+ console.log(n.key[1] + (" - "+ n.key[2]).grey);
+ }
+ }
+ } else if (error) {
+ console.log(error.toString().red);
+ } else {
+ console.log((response.statusCode+": "+body).red);
+ }
+ });
+ }
+}
+
+function logNodeList(nodes) {
+ if (!util.isArray(nodes)) {
+ nodes = [nodes];
+ }
+ for (var i=0;i<nodes.length;i++) {
+ var n = nodes[i];
+ console.log(formatNodeInfo(n))
+ }
+}
+
+function logFailure(msg) {
+ console.log(msg.red);
+}
+
+function formatBoolean(v,c) {
+ if (v) {
+ return ("["+c+"]");
+ } else {
+ return ("[ ]");
+ }
+}
+
+function formatNodeInfo(n) {
+ var inError = n.hasOwnProperty("err");
+
+ var str = formatBoolean(n.enabled,"X")+formatBoolean(n.loaded,"L")+" ";
+ str += n.id;
+ if (n.enabled && n.loaded) {
+ str = str.green;
+ } else if (n.enabled && n.err) {
+ str = str.red;
+ } else {
+ str = str.yellow;
+ }
+ if (n.module) {
+ str += " ["+n.module+"]";
+ }
+ str += " "+n.types.join(", ");
+ if (n.err) {
+ str+=" "+n.err.red;
+ }
+ return str;
+}
+
+if (commands[process.argv[2]]) {
+ commands[process.argv[2]].call();
+}
+
+
+})();
diff --git a/dgbuilder/red/comms.js b/dgbuilder/red/comms.js
new file mode 100644
index 00000000..5828c996
--- /dev/null
+++ b/dgbuilder/red/comms.js
@@ -0,0 +1,132 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * 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 util = require("util");
+
+var server;
+var settings;
+
+var wsServer;
+var activeConnections = [];
+
+var retained = {};
+
+var heartbeatTimer;
+var lastSentTime;
+
+
+function init(_server,_settings) {
+ server = _server;
+ settings = _settings;
+}
+
+function start() {
+
+ if (!settings.disableEditor) {
+ var webSocketKeepAliveTime = settings.webSocketKeepAliveTime || 15000;
+ var path = settings.httpAdminRoot || "/";
+ path = path + (path.slice(-1) == "/" ? "":"/") + "comms";
+ wsServer = new ws.Server({server:server,path:path});
+
+ wsServer.on('connection',function(ws) {
+ activeConnections.push(ws);
+ ws.on('close',function() {
+ for (var i=0;i<activeConnections.length;i++) {
+ if (activeConnections[i] === ws) {
+ activeConnections.splice(i,1);
+ break;
+ }
+ }
+ });
+ ws.on('message', function(data,flags) {
+ var msg = null;
+ try {
+ msg = JSON.parse(data);
+ } catch(err) {
+ util.log("[red:comms] received malformed message : "+err.toString());
+ return;
+ }
+ if (msg.subscribe) {
+ handleRemoteSubscription(ws,msg.subscribe);
+ }
+ });
+ ws.on('error', function(err) {
+ util.log("[red:comms] error : "+err.toString());
+ });
+ });
+
+ wsServer.on('error', function(err) {
+ util.log("[red:comms] server error : "+err.toString());
+ });
+
+ lastSentTime = Date.now();
+
+ heartbeatTimer = setInterval(function() {
+ var now = Date.now();
+ if (now-lastSentTime > webSocketKeepAliveTime) {
+ publish("hb",lastSentTime);
+ }
+ }, webSocketKeepAliveTime);
+ }
+}
+
+function stop() {
+ if (heartbeatTimer) {
+ clearInterval(heartbeatTimer);
+ }
+ if (wsServer) {
+ wsServer.close();
+ }
+}
+
+function publish(topic,data,retain) {
+ if (retain) {
+ retained[topic] = data;
+ } else {
+ delete retained[topic];
+ }
+ lastSentTime = Date.now();
+ activeConnections.forEach(function(conn) {
+ publishTo(conn,topic,data);
+ });
+}
+
+function publishTo(ws,topic,data) {
+ var msg = JSON.stringify({topic:topic,data:data});
+ try {
+ ws.send(msg);
+ } catch(err) {
+ util.log("[red:comms] send error : "+err.toString());
+ }
+}
+
+function handleRemoteSubscription(ws,topic) {
+ var re = new RegExp("^"+topic.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
+ for (var t in retained) {
+ if (re.test(t)) {
+ publishTo(ws,t,retained[t]);
+ }
+ }
+}
+
+
+module.exports = {
+ init:init,
+ start:start,
+ stop:stop,
+ publish:publish,
+}
diff --git a/dgbuilder/red/events.js b/dgbuilder/red/events.js
new file mode 100644
index 00000000..89590494
--- /dev/null
+++ b/dgbuilder/red/events.js
@@ -0,0 +1,19 @@
+/**
+ * Copyright 2013 IBM Corp.
+ *
+ * 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 events = require("events");
+
+module.exports = new events.EventEmitter();
diff --git a/dgbuilder/red/library.js b/dgbuilder/red/library.js
new file mode 100644
index 00000000..cc4199f2
--- /dev/null
+++ b/dgbuilder/red/library.js
@@ -0,0 +1,117 @@
+/**
+ * Copyright 2013 IBM Corp.
+ *
+ * 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 redApp = null;
+var storage = null;
+
+function init() {
+ redApp = require("./server").app;
+ storage = require("./storage");
+
+ // -------- Flow Library --------
+ redApp.post(new RegExp("/library/flows\/(.*)"), function(req,res) {
+ var fullBody = '';
+ req.on('data', function(chunk) {
+ fullBody += chunk.toString();
+ });
+ req.on('end', function() {
+ storage.saveFlow(req.params[0],fullBody).then(function() {
+ res.send(204);
+ }).otherwise(function(err) {
+ util.log("[red] Error loading flow '"+req.params[0]+"' : "+err);
+ if (err.message.indexOf('forbidden') === 0) {
+ res.send(403);
+ return;
+ }
+ res.send(500);
+ });
+ });
+ });
+
+ redApp.get("/library/flows",function(req,res) {
+ storage.getAllFlows().then(function(flows) {
+ res.json(flows);
+ });
+ });
+
+ redApp.get(new RegExp("/library/flows\/(.*)"), function(req,res) {
+ storage.getFlow(req.params[0]).then(function(data) {
+ res.set('Content-Type', 'application/json');
+ res.send(data);
+ }).otherwise(function(err) {
+ if (err) {
+ util.log("[red] Error loading flow '"+req.params[0]+"' : "+err);
+ if (err.message.indexOf('forbidden') === 0) {
+ res.send(403);
+ return;
+ }
+ }
+ res.send(404);
+ });
+ });
+
+ // ------------------------------
+}
+
+function createLibrary(type) {
+
+ redApp.get(new RegExp("/library/"+type+"($|\/(.*))"),function(req,res) {
+ var path = req.params[1]||"";
+ storage.getLibraryEntry(type,path).then(function(result) {
+ if (typeof result === "string") {
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.write(result);
+ res.end();
+ } else {
+ res.json(result);
+ }
+ }).otherwise(function(err) {
+ if (err) {
+ util.log("[red] Error loading library entry '"+path+"' : "+err);
+ if (err.message.indexOf('forbidden') === 0) {
+ res.send(403);
+ return;
+ }
+ }
+ res.send(404);
+ });
+ });
+
+ redApp.post(new RegExp("/library/"+type+"\/(.*)"),function(req,res) {
+ var path = req.params[0];
+ var fullBody = '';
+ req.on('data', function(chunk) {
+ fullBody += chunk.toString();
+ });
+ req.on('end', function() {
+ storage.saveLibraryEntry(type,path,req.query,fullBody).then(function() {
+ res.send(204);
+ }).otherwise(function(err) {
+ util.log("[red] Error saving library entry '"+path+"' : "+err);
+ if (err.message.indexOf('forbidden') === 0) {
+ res.send(403);
+ return;
+ }
+ res.send(500);
+ });
+ });
+ });
+}
+
+module.exports.init = init;
+module.exports.register = createLibrary;
diff --git a/dgbuilder/red/log.js b/dgbuilder/red/log.js
new file mode 100644
index 00000000..236e6df0
--- /dev/null
+++ b/dgbuilder/red/log.js
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * 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 EventEmitter = require("events").EventEmitter;
+
+var logHandlers = [];
+
+var ConsoleLogHandler = new EventEmitter();
+ConsoleLogHandler.on("log",function(msg) {
+ util.log("["+msg.level+"] ["+msg.type+":"+(msg.name||msg.id)+"] "+msg.msg);
+});
+
+var log = module.exports = {
+ addHandler: function(func) {
+ logHandlers.push(func);
+ },
+
+ log: function(msg) {
+ logHandlers.forEach(function(handler) {
+ handler.emit("log",msg);
+ });
+ }
+}
+
+log.addHandler(ConsoleLogHandler);
diff --git a/dgbuilder/red/nodes/Node.js b/dgbuilder/red/nodes/Node.js
new file mode 100644
index 00000000..0e6fc525
--- /dev/null
+++ b/dgbuilder/red/nodes/Node.js
@@ -0,0 +1,147 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * 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 EventEmitter = require("events").EventEmitter;
+var clone = require("clone");
+var when = require("when");
+
+var flows = require("./flows");
+var comms = require("../comms");
+
+function Node(n) {
+ this.id = n.id;
+ flows.add(this);
+ this.type = n.type;
+ if (n.name) {
+ this.name = n.name;
+ }
+ this.wires = n.wires||[];
+}
+
+util.inherits(Node,EventEmitter);
+
+Node.prototype._on = Node.prototype.on;
+
+Node.prototype.on = function(event,callback) {
+ var node = this;
+ if (event == "close") {
+ if (callback.length == 1) {
+ this.close = function() {
+ return when.promise(function(resolve) {
+ callback.call(node,function() {
+ resolve();
+ });
+ });
+ }
+ } else {
+ this.close = callback;
+ }
+ } else {
+ this._on(event,callback);
+ }
+}
+
+Node.prototype.close = function() {
+}
+
+Node.prototype.send = function(msg) {
+ // instanceof doesn't work for some reason here
+ if (msg == null) {
+ return;
+ } else if (!util.isArray(msg)) {
+ msg = [msg];
+ }
+ for (var i=0;i<this.wires.length;i++) {
+ var wires = this.wires[i];
+ if (i < msg.length) {
+ if (msg[i] != null) {
+ var msgs = msg[i];
+ if (!util.isArray(msg[i])) {
+ msgs = [msg[i]];
+ }
+ //if (wires.length == 1) {
+ // // Single recipient, don't need to clone the message
+ // var node = flows.get(wires[0]);
+ // if (node) {
+ // for (var k in msgs) {
+ // var mm = msgs[k];
+ // node.receive(mm);
+ // }
+ // }
+ //} else {
+ // Multiple recipients, must send message copies
+ for (var j=0;j<wires.length;j++) {
+ var node = flows.get(wires[j]);
+ if (node) {
+ for (var k=0;k<msgs.length;k++) {
+ var mm = msgs[k];
+ // Temporary fix for #97
+ // TODO: remove this http-node-specific fix somehow
+ var req = mm.req;
+ var res = mm.res;
+ delete mm.req;
+ delete mm.res;
+ var m = clone(mm);
+ if (req) {
+ m.req = req;
+ mm.req = req;
+ }
+ if (res) {
+ m.res = res;
+ mm.res = res;
+ }
+ node.receive(m);
+ }
+ }
+ }
+ //}
+ }
+ }
+ }
+}
+
+Node.prototype.receive = function(msg) {
+ this.emit("input",msg);
+}
+
+function log_helper(self, level, msg) {
+ var o = {level:level, id:self.id, type:self.type, msg:msg};
+ if (self.name) {
+ o.name = self.name;
+ }
+ self.emit("log",o);
+}
+
+Node.prototype.log = function(msg) {
+ log_helper(this, 'log', msg);
+}
+
+Node.prototype.warn = function(msg) {
+ log_helper(this, 'warn', msg);
+}
+
+Node.prototype.error = function(msg) {
+ log_helper(this, 'error', msg);
+}
+
+/**
+ * status: { fill:"red|green", shape:"dot|ring", text:"blah" }
+ */
+Node.prototype.status = function(status) {
+ comms.publish("status/"+this.id,status,true);
+}
+module.exports = Node;
diff --git a/dgbuilder/red/nodes/credentials.js b/dgbuilder/red/nodes/credentials.js
new file mode 100644
index 00000000..22e78d81
--- /dev/null
+++ b/dgbuilder/red/nodes/credentials.js
@@ -0,0 +1,208 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * 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 when = require("when");
+
+var credentialCache = {};
+var storage = null;
+var credentialsDef = {};
+var redApp = null;
+
+/**
+ * Adds an HTTP endpoint to allow look up of credentials for a given node id.
+ */
+function registerEndpoint(type) {
+ redApp.get('/credentials/' + type + '/:id', function (req, res) {
+ // TODO: This could be a generic endpoint with the type value
+ // parameterised.
+ //
+ // TODO: It should verify the given node id is of the type specified -
+ // but that would add a dependency from this module to the
+ // registry module that knows about node types.
+ var nodeType = type;
+ var nodeID = req.params.id;
+
+ var credentials = credentialCache[nodeID];
+ if (credentials === undefined) {
+ res.json({});
+ return;
+ }
+ var definition = credentialsDef[nodeType];
+
+ var sendCredentials = {};
+ for (var cred in definition) {
+ if (definition.hasOwnProperty(cred)) {
+ if (definition[cred].type == "password") {
+ var key = 'has_' + cred;
+ sendCredentials[key] = credentials[cred] != null && credentials[cred] !== '';
+ continue;
+ }
+ sendCredentials[cred] = credentials[cred] || '';
+ }
+ }
+ res.json(sendCredentials);
+
+ });
+}
+
+
+module.exports = {
+ init: function (_storage) {
+ storage = _storage;
+ // TODO: this should get passed in init function call rather than
+ // required directly.
+ redApp = require("../server").app;
+ },
+
+ /**
+ * Loads the credentials from storage.
+ */
+ load: function () {
+ return storage.getCredentials().then(function (creds) {
+ credentialCache = creds;
+ }).otherwise(function (err) {
+ util.log("[red] Error loading credentials : " + err);
+ });
+ },
+
+ /**
+ * Adds a set of credentials for the given node id.
+ * @param id the node id for the credentials
+ * @param creds an object of credential key/value pairs
+ * @return a promise for the saving of credentials to storage
+ */
+ add: function (id, creds) {
+ credentialCache[id] = creds;
+ return storage.saveCredentials(credentialCache);
+ },
+
+ /**
+ * Gets the credentials for the given node id.
+ * @param id the node id for the credentials
+ * @return the credentials
+ */
+ get: function (id) {
+ return credentialCache[id];
+ },
+
+ /**
+ * Deletes the credentials for the given node id.
+ * @param id the node id for the credentials
+ * @return a promise for the saving of credentials to storage
+ */
+ delete: function (id) {
+ delete credentialCache[id];
+ storage.saveCredentials(credentialCache);
+ },
+
+ /**
+ * Deletes any credentials for nodes that no longer exist
+ * @param getNode a function that can return a node for a given id
+ * @return a promise for the saving of credentials to storage
+ */
+ clean: function (getNode) {
+ var deletedCredentials = false;
+ for (var c in credentialCache) {
+ if (credentialCache.hasOwnProperty(c)) {
+ var n = getNode(c);
+ if (!n) {
+ deletedCredentials = true;
+ delete credentialCache[c];
+ }
+ }
+ }
+ if (deletedCredentials) {
+ return storage.saveCredentials(credentialCache);
+ } else {
+ return when.resolve();
+ }
+ },
+
+ /**
+ * Registers a node credential definition.
+ * @param type the node type
+ * @param definition the credential definition
+ */
+ register: function (type, definition) {
+ var dashedType = type.replace(/\s+/g, '-');
+ credentialsDef[dashedType] = definition;
+ registerEndpoint(dashedType);
+ },
+
+ /**
+ * Extracts and stores any credential updates in the provided node.
+ * The provided node may have a .credentials property that contains
+ * new credentials for the node.
+ * This function loops through the credentials in the definition for
+ * the node-type and applies any of the updates provided in the node.
+ *
+ * This function does not save the credentials to disk as it is expected
+ * to be called multiple times when a new flow is deployed.
+ *
+ * @param node the node to extract credentials from
+ */
+ extract: function(node) {
+ var nodeID = node.id;
+ var nodeType = node.type;
+ var newCreds = node.credentials;
+ if (newCreds) {
+ var savedCredentials = credentialCache[nodeID] || {};
+
+ var dashedType = nodeType.replace(/\s+/g, '-');
+ var definition = credentialsDef[dashedType];
+
+ if (!definition) {
+ util.log('Credential Type ' + nodeType + ' is not registered.');
+ return;
+ }
+
+ for (var cred in definition) {
+ if (definition.hasOwnProperty(cred)) {
+ if (newCreds[cred] === undefined) {
+ continue;
+ }
+ if (definition[cred].type == "password" && newCreds[cred] == '__PWRD__') {
+ continue;
+ }
+ if (0 === newCreds[cred].length || /^\s*$/.test(newCreds[cred])) {
+ delete savedCredentials[cred];
+ continue;
+ }
+ savedCredentials[cred] = newCreds[cred];
+ }
+ }
+ credentialCache[nodeID] = savedCredentials;
+ }
+ },
+
+ /**
+ * Saves the credentials to storage
+ * @return a promise for the saving of credentials to storage
+ */
+ save: function () {
+ return storage.saveCredentials(credentialCache);
+ },
+
+ /**
+ * Gets the credential definition for the given node type
+ * @param type the node type
+ * @return the credential definition
+ */
+ getDefinition: function (type) {
+ return credentialsDef[type];
+ }
+}
diff --git a/dgbuilder/red/nodes/flows.js b/dgbuilder/red/nodes/flows.js
new file mode 100644
index 00000000..b0b5d514
--- /dev/null
+++ b/dgbuilder/red/nodes/flows.js
@@ -0,0 +1,220 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * 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 when = require("when");
+
+var typeRegistry = require("./registry");
+var credentials = require("./credentials");
+var log = require("../log");
+var events = require("../events");
+
+var storage = null;
+
+var nodes = {};
+var activeConfig = [];
+var missingTypes = [];
+
+events.on('type-registered',function(type) {
+ if (missingTypes.length > 0) {
+ var i = missingTypes.indexOf(type);
+ if (i != -1) {
+ missingTypes.splice(i,1);
+ util.log("[red] Missing type registered: "+type);
+ if (missingTypes.length === 0) {
+ parseConfig();
+ }
+ }
+ }
+});
+
+/**
+ * Parses the current activeConfig and creates the required node instances
+ */
+function parseConfig() {
+ var i;
+ var nt;
+ missingTypes = [];
+
+ // Scan the configuration for any unknown node types
+ for (i=0;i<activeConfig.length;i++) {
+ var type = activeConfig[i].type;
+ // TODO: remove workspace in next release+1
+ if (type != "workspace" && type != "tab") {
+ nt = typeRegistry.get(type);
+ if (!nt && missingTypes.indexOf(type) == -1) {
+ missingTypes.push(type);
+ }
+ }
+ }
+ // Abort if there are any missing types
+ if (missingTypes.length > 0) {
+ util.log("[red] Waiting for missing types to be registered:");
+ for (i=0;i<missingTypes.length;i++) {
+ util.log("[red] - "+missingTypes[i]);
+ }
+ return;
+ }
+
+ util.log("[red] Starting flows");
+ events.emit("nodes-starting");
+
+ // Instantiate each node in the flow
+ for (i=0;i<activeConfig.length;i++) {
+ var nn = null;
+ // TODO: remove workspace in next release+1
+ if (activeConfig[i].type != "workspace" && activeConfig[i].type != "tab") {
+ nt = typeRegistry.get(activeConfig[i].type);
+ if (nt) {
+ try {
+ nn = new nt(activeConfig[i]);
+ }
+ catch (err) {
+ util.log("[red] "+activeConfig[i].type+" : "+err);
+ }
+ }
+ // console.log(nn);
+ if (nn === null) {
+ util.log("[red] unknown type: "+activeConfig[i].type);
+ }
+ }
+ }
+ // Clean up any orphaned credentials
+ credentials.clean(flowNodes.get);
+ events.emit("nodes-started");
+}
+
+/**
+ * Stops the current activeConfig
+ */
+function stopFlows() {
+ if (activeConfig&&activeConfig.length > 0) {
+ util.log("[red] Stopping flows");
+ }
+ return flowNodes.clear();
+}
+
+var flowNodes = module.exports = {
+ init: function(_storage) {
+ storage = _storage;
+ },
+
+ /**
+ * Load the current activeConfig from storage and start it running
+ * @return a promise for the loading of the config
+ */
+ load: function() {
+ return storage.getFlows().then(function(flows) {
+ return credentials.load().then(function() {
+ activeConfig = flows;
+ if (activeConfig && activeConfig.length > 0) {
+ parseConfig();
+ }
+ });
+ }).otherwise(function(err) {
+ util.log("[red] Error loading flows : "+err);
+ });
+ },
+
+ /**
+ * Add a node to the current active set
+ * @param n the node to add
+ */
+ add: function(n) {
+ nodes[n.id] = n;
+ n.on("log",log.log);
+ },
+
+ /**
+ * Get a node
+ * @param i the node id
+ * @return the node
+ */
+ get: function(i) {
+ return nodes[i];
+ },
+
+ /**
+ * Stops all active nodes and clears the active set
+ * @return a promise for the stopping of all active nodes
+ */
+ clear: function() {
+ return when.promise(function(resolve) {
+ events.emit("nodes-stopping");
+ var promises = [];
+ for (var n in nodes) {
+ if (nodes.hasOwnProperty(n)) {
+ try {
+ var p = nodes[n].close();
+ if (p) {
+ promises.push(p);
+ }
+ } catch(err) {
+ nodes[n].error(err);
+ }
+ }
+ }
+ when.settle(promises).then(function() {
+ events.emit("nodes-stopped");
+ nodes = {};
+ resolve();
+ });
+ });
+ },
+
+ /**
+ * Provides an iterator over the active set of nodes
+ * @param cb a function to be called for each node in the active set
+ */
+ each: function(cb) {
+ for (var n in nodes) {
+ if (nodes.hasOwnProperty(n)) {
+ cb(nodes[n]);
+ }
+ }
+ },
+
+ /**
+ * @return the active configuration
+ */
+ getFlows: function() {
+ return activeConfig;
+ },
+
+ /**
+ * Sets the current active config.
+ * @param config the configuration to enable
+ * @return a promise for the starting of the new flow
+ */
+ setFlows: function (config) {
+ // Extract any credential updates
+ for (var i=0; i<config.length; i++) {
+ var node = config[i];
+ if (node.credentials) {
+ credentials.extract(node);
+ delete node.credentials;
+ }
+ }
+ return credentials.save()
+ .then(function() { return storage.saveFlows(config);})
+ .then(function() { return stopFlows();})
+ .then(function () {
+ activeConfig = config;
+ parseConfig();
+ });
+ },
+ stopFlows: stopFlows
+};
diff --git a/dgbuilder/red/nodes/index.js b/dgbuilder/red/nodes/index.js
new file mode 100644
index 00000000..3d5ad719
--- /dev/null
+++ b/dgbuilder/red/nodes/index.js
@@ -0,0 +1,134 @@
+/**
+ * Copyright 2013, 2014 IBM Corp.
+ *
+ * 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 registry = require("./registry");
+var credentials = require("./credentials");
+var flows = require("./flows");
+var Node = require("./Node");
+
+/**
+ * Registers a node constructor
+ * @param type - the string type name
+ * @param constructor - the constructor function for this node type
+ * @param opts - optional additional options for the node
+ */
+function registerType(type,constructor,opts) {
+ if (opts && opts.credentials) {
+ credentials.register(type,opts.credentials);
+ }
+ registry.registerType(type,constructor);
+}
+
+/**
+ * Called from a Node's constructor function, invokes the super-class
+ * constructor and attaches any credentials to the node.
+ * @param node the node object being created
+ * @param def the instance definition for the node
+ */
+function createNode(node,def) {
+ Node.call(node,def);
+ var creds = credentials.get(node.id);
+ if (creds) {
+ node.credentials = creds;
+ }
+}
+
+function init(_settings,storage) {
+ credentials.init(storage);
+ flows.init(storage);
+ registry.init(_settings);
+}
+
+function checkTypeInUse(id) {
+ var nodeInfo = registry.getNodeInfo(id);
+ if (!nodeInfo) {
+ throw new Error("Unrecognised id: "+info);
+ }
+ var inUse = {};
+ flows.each(function(n) {
+ inUse[n.type] = (inUse[n.type]||0)+1;
+ });
+ var nodesInUse = [];
+ nodeInfo.types.forEach(function(t) {
+ if (inUse[t]) {
+ nodesInUse.push(t);
+ }
+ });
+ if (nodesInUse.length > 0) {
+ var msg = nodesInUse.join(", ");
+ throw new Error("Type in use: "+msg);
+ }
+}
+
+function removeNode(id) {
+ checkTypeInUse(id);
+ return registry.removeNode(id);
+}
+
+function removeModule(module) {
+ var info = registry.getNodeModuleInfo(module);
+ for (var i=0;i<info.nodes.length;i++) {
+ checkTypeInUse(info.nodes[i]);
+ }
+ return registry.removeModule(module);
+}
+
+
+function disableNode(id) {
+ checkTypeInUse(id);
+ return registry.disableNode(id);
+}
+
+module.exports = {
+ // Lifecycle
+ init: init,
+ load: registry.load,
+
+ // Node registry
+ createNode: createNode,
+ getNode: flows.get,
+
+ addNode: registry.addNode,
+ removeNode: removeNode,
+
+ addModule: registry.addModule,
+ removeModule: removeModule,
+
+ enableNode: registry.enableNode,
+ disableNode: disableNode,
+
+ // Node type registry
+ registerType: registerType,
+ getType: registry.get,
+ getNodeInfo: registry.getNodeInfo,
+ getNodeModuleInfo: registry.getNodeModuleInfo,
+ getNodeList: registry.getNodeList,
+ getNodeConfigs: registry.getNodeConfigs,
+ getNodeConfig: registry.getNodeConfig,
+ clearRegistry: registry.clear,
+ cleanNodeList: registry.cleanNodeList,
+
+ // Flow handling
+ loadFlows: flows.load,
+ stopFlows: flows.stopFlows,
+ setFlows: flows.setFlows,
+ getFlows: flows.getFlows,
+
+ // Credentials
+ addCredentials: credentials.add,
+ getCredentials: credentials.get,
+ deleteCredentials: credentials.delete
+}
+
diff --git a/dgbuilder/red/nodes/registry.js b/dgbuilder/red/nodes/registry.js
new file mode 100644
index 00000000..f2073aff
--- /dev/null
+++ b/dgbuilder/red/nodes/registry.js
@@ -0,0 +1,693 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * 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 when = require("when");
+var whenNode = require('when/node');
+var fs = require("fs");
+var path = require("path");
+var crypto = require("crypto");
+var UglifyJS = require("uglify-js");
+
+var events = require("../events");
+
+var Node;
+var settings;
+
+function filterNodeInfo(n) {
+ var r = {
+ id: n.id,
+ name: n.name,
+ types: n.types,
+ enabled: n.enabled
+ }
+ if (n.hasOwnProperty("loaded")) {
+ r.loaded = n.loaded;
+ }
+ if (n.hasOwnProperty("module")) {
+ r.module = n.module;
+ }
+ if (n.hasOwnProperty("err")) {
+ r.err = n.err.toString();
+ }
+ return r;
+}
+
+var registry = (function() {
+ var nodeConfigCache = null;
+ var nodeConfigs = {};
+ var nodeList = [];
+ var nodeConstructors = {};
+ var nodeTypeToId = {};
+ var nodeModules = {};
+
+ function saveNodeList() {
+ var nodeList = {};
+
+ for (var i in nodeConfigs) {
+ if (nodeConfigs.hasOwnProperty(i)) {
+ var nodeConfig = nodeConfigs[i];
+ var n = filterNodeInfo(nodeConfig);
+ n.file = nodeConfig.file;
+ delete n.loaded;
+ delete n.err;
+ delete n.file;
+ delete n.id;
+ nodeList[i] = n;
+ }
+ }
+ if (settings.available()) {
+ return settings.set("nodes",nodeList);
+ } else {
+ return when.reject("Settings unavailable");
+ }
+ }
+
+ return {
+ init: function() {
+ if (settings.available()) {
+ nodeConfigs = settings.get("nodes")||{};
+ // Restore the node id property to individual entries
+ for (var id in nodeConfigs) {
+ if (nodeConfigs.hasOwnProperty(id)) {
+ nodeConfigs[id].id = id;
+ }
+ }
+ } else {
+ nodeConfigs = {};
+ }
+ nodeModules = {};
+ nodeTypeToId = {};
+ nodeConstructors = {};
+ nodeList = [];
+ nodeConfigCache = null;
+ },
+
+ addNodeSet: function(id,set) {
+ if (!set.err) {
+ set.types.forEach(function(t) {
+ nodeTypeToId[t] = id;
+ });
+ }
+
+ if (set.module) {
+ nodeModules[set.module] = nodeModules[set.module]||{nodes:[]};
+ nodeModules[set.module].nodes.push(id);
+ }
+
+ nodeConfigs[id] = set;
+ nodeList.push(id);
+ nodeConfigCache = null;
+ },
+ removeNode: function(id) {
+ var config = nodeConfigs[id];
+ if (!config) {
+ throw new Error("Unrecognised id: "+id);
+ }
+ delete nodeConfigs[id];
+ var i = nodeList.indexOf(id);
+ if (i > -1) {
+ nodeList.splice(i,1);
+ }
+ config.types.forEach(function(t) {
+ delete nodeConstructors[t];
+ delete nodeTypeToId[t];
+ });
+ config.enabled = false;
+ config.loaded = false;
+ nodeConfigCache = null;
+ return filterNodeInfo(config);
+ },
+ removeModule: function(module) {
+ if (!settings.available()) {
+ throw new Error("Settings unavailable");
+ }
+ var nodes = nodeModules[module];
+ if (!nodes) {
+ throw new Error("Unrecognised module: "+module);
+ }
+ var infoList = [];
+ for (var i=0;i<nodes.nodes.length;i++) {
+ infoList.push(registry.removeNode(nodes.nodes[i]));
+ }
+ delete nodeModules[module];
+ saveNodeList();
+ return infoList;
+ },
+ getNodeInfo: function(typeOrId) {
+ if (nodeTypeToId[typeOrId]) {
+ return filterNodeInfo(nodeConfigs[nodeTypeToId[typeOrId]]);
+ } else if (nodeConfigs[typeOrId]) {
+ return filterNodeInfo(nodeConfigs[typeOrId]);
+ }
+ return null;
+ },
+ getNodeList: function() {
+ var list = [];
+ for (var id in nodeConfigs) {
+ if (nodeConfigs.hasOwnProperty(id)) {
+ list.push(filterNodeInfo(nodeConfigs[id]))
+ }
+ }
+ return list;
+ },
+ registerNodeConstructor: function(type,constructor) {
+ if (nodeConstructors[type]) {
+ throw new Error(type+" already registered");
+ }
+ //TODO: Ensure type is known - but doing so will break some tests
+ // that don't have a way to register a node template ahead
+ // of registering the constructor
+ util.inherits(constructor,Node);
+ nodeConstructors[type] = constructor;
+ events.emit("type-registered",type);
+ },
+
+
+ /**
+ * Gets all of the node template configs
+ * @return all of the node templates in a single string
+ */
+ getAllNodeConfigs: function() {
+ if (!nodeConfigCache) {
+ var result = "";
+ var script = "";
+ for (var i=0;i<nodeList.length;i++) {
+ var config = nodeConfigs[nodeList[i]];
+ if (config.enabled && !config.err) {
+ result += config.config;
+ script += config.script;
+ }
+ }
+ if (script.length > 0) {
+ result += '<script type="text/javascript">';
+ result += UglifyJS.minify(script, {fromString: true}).code;
+ result += '</script>';
+ }
+ nodeConfigCache = result;
+ }
+ return nodeConfigCache;
+ },
+
+ getNodeConfig: function(id) {
+ var config = nodeConfigs[id];
+ if (config) {
+ var result = config.config;
+ if (config.script) {
+ result += '<script type="text/javascript">'+config.script+'</script>';
+ }
+ return result;
+ } else {
+ return null;
+ }
+ },
+
+ getNodeConstructor: function(type) {
+ var config = nodeConfigs[nodeTypeToId[type]];
+ if (!config || (config.enabled && !config.err)) {
+ return nodeConstructors[type];
+ }
+ return null;
+ },
+
+ clear: function() {
+ nodeConfigCache = null;
+ nodeConfigs = {};
+ nodeList = [];
+ nodeConstructors = {};
+ nodeTypeToId = {};
+ },
+
+ getTypeId: function(type) {
+ return nodeTypeToId[type];
+ },
+
+ getModuleInfo: function(type) {
+ return nodeModules[type];
+ },
+
+ enableNodeSet: function(id) {
+ if (!settings.available()) {
+ throw new Error("Settings unavailable");
+ }
+ var config = nodeConfigs[id];
+ if (config) {
+ delete config.err;
+ config.enabled = true;
+ if (!config.loaded) {
+ // TODO: honour the promise this returns
+ loadNodeModule(config);
+ }
+ nodeConfigCache = null;
+ saveNodeList();
+ } else {
+ throw new Error("Unrecognised id: "+id);
+ }
+ return filterNodeInfo(config);
+ },
+
+ disableNodeSet: function(id) {
+ if (!settings.available()) {
+ throw new Error("Settings unavailable");
+ }
+ var config = nodeConfigs[id];
+ if (config) {
+ // TODO: persist setting
+ config.enabled = false;
+ nodeConfigCache = null;
+ saveNodeList();
+ } else {
+ throw new Error("Unrecognised id: "+id);
+ }
+ return filterNodeInfo(config);
+ },
+
+ saveNodeList: saveNodeList,
+
+ cleanNodeList: function() {
+ var removed = false;
+ for (var id in nodeConfigs) {
+ if (nodeConfigs.hasOwnProperty(id)) {
+ if (nodeConfigs[id].module && !nodeModules[nodeConfigs[id].module]) {
+ registry.removeNode(id);
+ removed = true;
+ }
+ }
+ }
+ if (removed) {
+ saveNodeList();
+ }
+ }
+ }
+})();
+
+
+
+function init(_settings) {
+ Node = require("./Node");
+ settings = _settings;
+ registry.init();
+}
+
+/**
+ * Synchronously walks the directory looking for node files.
+ * Emits 'node-icon-dir' events for an icon dirs found
+ * @param dir the directory to search
+ * @return an array of fully-qualified paths to .js files
+ */
+function getNodeFiles(dir) {
+ var result = [];
+ var files = [];
+ try {
+ files = fs.readdirSync(dir);
+ } catch(err) {
+ return result;
+ }
+ files.sort();
+ files.forEach(function(fn) {
+ var stats = fs.statSync(path.join(dir,fn));
+ if (stats.isFile()) {
+ if (/\.js$/.test(fn)) {
+ var valid = true;
+ if (settings.nodesExcludes) {
+ for (var i=0;i<settings.nodesExcludes.length;i++) {
+ if (settings.nodesExcludes[i] == fn) {
+ valid = false;
+ break;
+ }
+ }
+ }
+ valid = valid && fs.existsSync(path.join(dir,fn.replace(/\.js$/,".html")))
+
+ if (valid) {
+ result.push(path.join(dir,fn));
+ }
+ }
+ } else if (stats.isDirectory()) {
+ // Ignore /.dirs/, /lib/ /node_modules/
+ if (!/^(\..*|lib|icons|node_modules|test)$/.test(fn)) {
+ result = result.concat(getNodeFiles(path.join(dir,fn)));
+ } else if (fn === "icons") {
+ events.emit("node-icon-dir",path.join(dir,fn));
+ }
+ }
+ });
+ return result;
+}
+
+/**
+ * Scans the node_modules path for nodes
+ * @param moduleName the name of the module to be found
+ * @return a list of node modules: {dir,package}
+ */
+function scanTreeForNodesModules(moduleName) {
+ var dir = __dirname+"/../../nodes";
+ var results = [];
+ var up = path.resolve(path.join(dir,".."));
+ while (up !== dir) {
+ var pm = path.join(dir,"node_modules");
+ try {
+ var files = fs.readdirSync(pm);
+ for (var i=0;i<files.length;i++) {
+ var fn = files[i];
+ if (!registry.getModuleInfo(fn)) {
+ if (!moduleName || fn == moduleName) {
+ var pkgfn = path.join(pm,fn,"package.json");
+ try {
+ var pkg = require(pkgfn);
+ if (pkg['node-red']) {
+ var moduleDir = path.join(pm,fn);
+ results.push({dir:moduleDir,package:pkg});
+ }
+ } catch(err) {
+ if (err.code != "MODULE_NOT_FOUND") {
+ // TODO: handle unexpected error
+ }
+ }
+ if (fn == moduleName) {
+ break;
+ }
+ }
+ }
+ }
+ } catch(err) {
+ }
+
+ dir = up;
+ up = path.resolve(path.join(dir,".."));
+ }
+ return results;
+}
+
+/**
+ * Loads the nodes provided in an npm package.
+ * @param moduleDir the root directory of the package
+ * @param pkg the module's package.json object
+ */
+function loadNodesFromModule(moduleDir,pkg) {
+ var nodes = pkg['node-red'].nodes||{};
+ var results = [];
+ var iconDirs = [];
+ for (var n in nodes) {
+ if (nodes.hasOwnProperty(n)) {
+ var file = path.join(moduleDir,nodes[n]);
+ try {
+ results.push(loadNodeConfig(file,pkg.name,n));
+ } catch(err) {
+ }
+ var iconDir = path.join(moduleDir,path.dirname(nodes[n]),"icons");
+ if (iconDirs.indexOf(iconDir) == -1) {
+ if (fs.existsSync(iconDir)) {
+ events.emit("node-icon-dir",iconDir);
+ iconDirs.push(iconDir);
+ }
+ }
+ }
+ }
+ return results;
+}
+
+
+/**
+ * Loads a node's configuration
+ * @param file the fully qualified path of the node's .js file
+ * @param name the name of the node
+ * @return the node object
+ * {
+ * id: a unqiue id for the node file
+ * name: the name of the node file, or label from the npm module
+ * file: the fully qualified path to the node's .js file
+ * template: the fully qualified path to the node's .html file
+ * config: the non-script parts of the node's .html file
+ * script: the script part of the node's .html file
+ * types: an array of node type names in this file
+ * }
+ */
+function loadNodeConfig(file,module,name) {
+ var id = crypto.createHash('sha1').update(file).digest("hex");
+ if (module && name) {
+ var newid = crypto.createHash('sha1').update(module+":"+name).digest("hex");
+ var existingInfo = registry.getNodeInfo(id);
+ if (existingInfo) {
+ // For a brief period, id for modules were calculated incorrectly.
+ // To prevent false-duplicates, this removes the old id entry
+ registry.removeNode(id);
+ registry.saveNodeList();
+ }
+ id = newid;
+
+ }
+ var info = registry.getNodeInfo(id);
+
+ var isEnabled = true;
+
+ if (info) {
+ if (info.hasOwnProperty("loaded")) {
+ throw new Error(file+" already loaded");
+ }
+ isEnabled = info.enabled;
+ }
+
+ var node = {
+ id: id,
+ file: file,
+ template: file.replace(/\.js$/,".html"),
+ enabled: isEnabled,
+ loaded:false
+ }
+
+ if (module) {
+ node.name = module+":"+name;
+ node.module = module;
+ } else {
+ node.name = path.basename(file)
+ }
+ try {
+ var content = fs.readFileSync(node.template,'utf8');
+
+ var types = [];
+
+ var regExp = /<script ([^>]*)data-template-name=['"]([^'"]*)['"]/gi;
+ var match = null;
+
+ while((match = regExp.exec(content)) !== null) {
+ types.push(match[2]);
+ }
+ node.types = types;
+ node.config = content;
+
+ // TODO: parse out the javascript portion of the template
+ node.script = "";
+
+ for (var i=0;i<node.types.length;i++) {
+ if (registry.getTypeId(node.types[i])) {
+ node.err = node.types[i]+" already registered";
+ break;
+ }
+ }
+ } catch(err) {
+ node.types = [];
+ if (err.code === 'ENOENT') {
+ node.err = "Error: "+file+" does not exist";
+ } else {
+ node.err = err.toString();
+ }
+ }
+ registry.addNodeSet(id,node);
+ return node;
+}
+
+/**
+ * Loads all palette nodes
+ * @param defaultNodesDir optional parameter, when set, it overrides the default
+ * location of nodeFiles - used by the tests
+ * @return a promise that resolves on completion of loading
+ */
+function load(defaultNodesDir,disableNodePathScan) {
+ return when.promise(function(resolve,reject) {
+ // Find all of the nodes to load
+ var nodeFiles;
+ if(defaultNodesDir) {
+ nodeFiles = getNodeFiles(path.resolve(defaultNodesDir));
+ } else {
+ nodeFiles = getNodeFiles(__dirname+"/../../nodes");
+ }
+
+ if (settings.nodesDir) {
+ var dir = settings.nodesDir;
+ if (typeof settings.nodesDir == "string") {
+ dir = [dir];
+ }
+ for (var i=0;i<dir.length;i++) {
+ nodeFiles = nodeFiles.concat(getNodeFiles(dir[i]));
+ }
+ }
+ var nodes = [];
+ nodeFiles.forEach(function(file) {
+ try {
+ nodes.push(loadNodeConfig(file));
+ } catch(err) {
+ //
+ }
+ });
+
+ // TODO: disabling npm module loading if defaultNodesDir set
+ // This indicates a test is being run - don't want to pick up
+ // unexpected nodes.
+ // Urgh.
+ if (!disableNodePathScan) {
+ // Find all of the modules containing nodes
+ var moduleFiles = scanTreeForNodesModules();
+ moduleFiles.forEach(function(moduleFile) {
+ nodes = nodes.concat(loadNodesFromModule(moduleFile.dir,moduleFile.package));
+ });
+ }
+ var promises = [];
+ nodes.forEach(function(node) {
+ if (!node.err) {
+ promises.push(loadNodeModule(node));
+ }
+ });
+
+ //resolve([]);
+ when.settle(promises).then(function(results) {
+ // Trigger a load of the configs to get it precached
+ registry.getAllNodeConfigs();
+
+ if (settings.available()) {
+ resolve(registry.saveNodeList());
+ } else {
+ resolve();
+ }
+ });
+ });
+}
+
+/**
+ * Loads the specified node into the runtime
+ * @param node a node info object - see loadNodeConfig
+ * @return a promise that resolves to an update node info object. The object
+ * has the following properties added:
+ * err: any error encountered whilst loading the node
+ *
+ */
+function loadNodeModule(node) {
+ var nodeDir = path.dirname(node.file);
+ var nodeFn = path.basename(node.file);
+ if (!node.enabled) {
+ return when.resolve(node);
+ }
+ try {
+ var loadPromise = null;
+ var r = require(node.file);
+ if (typeof r === "function") {
+ var promise = r(require('../red'));
+ if (promise != null && typeof promise.then === "function") {
+ loadPromise = promise.then(function() {
+ node.enabled = true;
+ node.loaded = true;
+ return node;
+ }).otherwise(function(err) {
+ node.err = err;
+ return node;
+ });
+ }
+ }
+ if (loadPromise == null) {
+ node.enabled = true;
+ node.loaded = true;
+ loadPromise = when.resolve(node);
+ }
+ return loadPromise;
+ } catch(err) {
+ node.err = err;
+ return when.resolve(node);
+ }
+}
+
+function loadNodeList(nodes) {
+ var promises = [];
+ nodes.forEach(function(node) {
+ if (!node.err) {
+ promises.push(loadNodeModule(node));
+ } else {
+ promises.push(node);
+ }
+ });
+
+ return when.settle(promises).then(function(results) {
+ return registry.saveNodeList().then(function() {
+ var list = results.map(function(r) {
+ return filterNodeInfo(r.value);
+ });
+ return list;
+ });
+ });
+}
+
+function addNode(file) {
+ if (!settings.available()) {
+ throw new Error("Settings unavailable");
+ }
+ var nodes = [];
+ try {
+ nodes.push(loadNodeConfig(file));
+ } catch(err) {
+ return when.reject(err);
+ }
+ return loadNodeList(nodes);
+}
+
+function addModule(module) {
+ if (!settings.available()) {
+ throw new Error("Settings unavailable");
+ }
+ var nodes = [];
+ if (registry.getModuleInfo(module)) {
+ return when.reject(new Error("Module already loaded"));
+ }
+ var moduleFiles = scanTreeForNodesModules(module);
+ if (moduleFiles.length === 0) {
+ var err = new Error("Cannot find module '" + module + "'");
+ err.code = 'MODULE_NOT_FOUND';
+ return when.reject(err);
+ }
+ moduleFiles.forEach(function(moduleFile) {
+ nodes = nodes.concat(loadNodesFromModule(moduleFile.dir,moduleFile.package));
+ });
+ return loadNodeList(nodes);
+}
+
+module.exports = {
+ init:init,
+ load:load,
+ clear: registry.clear,
+ registerType: registry.registerNodeConstructor,
+ get: registry.getNodeConstructor,
+ getNodeInfo: registry.getNodeInfo,
+ getNodeModuleInfo: registry.getModuleInfo,
+ getNodeList: registry.getNodeList,
+ getNodeConfigs: registry.getAllNodeConfigs,
+ getNodeConfig: registry.getNodeConfig,
+ addNode: addNode,
+ removeNode: registry.removeNode,
+ enableNode: registry.enableNodeSet,
+ disableNode: registry.disableNodeSet,
+
+ addModule: addModule,
+ removeModule: registry.removeModule,
+ cleanNodeList: registry.cleanNodeList
+}
diff --git a/dgbuilder/red/red.js b/dgbuilder/red/red.js
new file mode 100644
index 00000000..2735e0da
--- /dev/null
+++ b/dgbuilder/red/red.js
@@ -0,0 +1,68 @@
+/**
+ * Copyright 2013 IBM Corp.
+ *
+ * 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 server = require("./server");
+var nodes = require("./nodes");
+var library = require("./library");
+var comms = require("./comms");
+var log = require("./log");
+var util = require("./util");
+var fs = require("fs");
+var settings = require("./settings");
+var credentials = require("./nodes/credentials");
+
+var path = require('path');
+
+process.env.NODE_RED_HOME = process.env.NODE_RED_HOME || path.resolve(__dirname+"/..");
+
+var events = require("events");
+
+var RED = {
+
+ init: function(httpServer,userSettings) {
+ userSettings.version = this.version();
+ settings.init(userSettings);
+ server.init(httpServer,settings);
+ library.init();
+ return server.app;
+ },
+
+ start: server.start,
+ stop: server.stop,
+ nodes: nodes,
+ library: library,
+ credentials: credentials,
+ events: events,
+ log: log,
+ comms: comms,
+ settings:settings,
+ util: util,
+ version: function () {
+ var p = require(path.join(process.env.NODE_RED_HOME,"package.json"));
+ if (fs.existsSync(path.join(process.env.NODE_RED_HOME,".git"))) {
+ return p.version+".git";
+ } else {
+ return p.version;
+ }
+ }
+};
+
+RED.__defineGetter__("app", function() { console.log("Deprecated use of RED.app - use RED.httpAdmin instead"); return server.app });
+RED.__defineGetter__("httpAdmin", function() { return server.app });
+RED.__defineGetter__("httpNode", function() { return server.nodeApp });
+RED.__defineGetter__("server", function() { return server.server });
+
+module.exports = RED;
diff --git a/dgbuilder/red/server.js b/dgbuilder/red/server.js
new file mode 100644
index 00000000..01a769e7
--- /dev/null
+++ b/dgbuilder/red/server.js
@@ -0,0 +1,1317 @@
+/**
+ * Copyright 2013 IBM Corp.
+ *
+ * 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 util = require('util');
+var multer = require('multer');
+var when = require('when');
+var exec = require('child_process').exec;
+
+var createUI = require("./ui");
+var redNodes = require("./nodes");
+var comms = require("./comms");
+var storage = require("./storage");
+var fs=require('fs');
+var path = require("path");
+var app = null;
+var nodeApp = null;
+var server = null;
+var settings = null;
+
+var flowShareUsers = require("../flowShareUsers");
+
+//console.dir(flowShareUsers);
+
+function createServer(_server,_settings) {
+ server = _server;
+ settings = _settings;
+
+ comms.init(_server,_settings);
+
+ nodeApp = express();
+ app = express();
+
+ if (settings.httpAdminRoot !== false) {
+
+
+ if (!settings.disableEditor) {
+ createUI(settings,app);
+ }
+
+ var slaActions = require("./sla");
+
+ app.get("/flows",function(req,res) {
+ res.json(redNodes.getFlows());
+ });
+
+ app.get("/loadJSFiles",function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var generatedJSDir=appDir + "/generatedJS";
+ var glob = require("glob")
+ glob(generatedJSDir + "/**/*.js", null, function (er, files) {
+ // files is an array of filenames.
+ // If the `nonull` option is set, and nothing
+ // was found, then files is ["**/*.js"]
+ // er is an error object or null.
+ //console.dir(files);
+ var sliValuesObj =[];
+ for(var i=0;files!= null && i<files.length;i++){
+ var f = files[i].replace( new RegExp(generatedJSDir + "/", "g" ), "" );
+ console.log("loading file " + f);
+ try{
+ sliValuesObj.push(require(files[i]));
+ //console.dir(sliValuesObj);
+ }catch(err){
+ console.log("Error:Could not load file " + files[i]);
+ }
+ }
+ res.json({"sliValuesObj" : sliValuesObj});
+ });
+ });
+
+ app.get("/loadSelectedModules",function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ var generatedJSDir=appDir + "/generatedJS";
+ //console.dir(req);
+ var selectedModulesStr = req.query.selectedModules;
+ var selectedModules = [];
+ if(selectedModulesStr != undefined && selectedModulesStr != null){
+ selectedModules = selectedModulesStr.split(",");
+ }
+ console.log(selectedModules);
+ var loaded_modules = {"selected_modules" :selectedModules};
+ var file = userDir + "/selected_modules";
+ var content = "module.exports=\n" + JSON.stringify(loaded_modules);
+ try{
+ fs.writeFileSync(file, content, 'utf8');
+ }catch(err){
+ console.log("could not write to file " + file);
+ }
+ var sliValuesObj =[];
+ for(var i=0;selectedModules!= null && i<selectedModules.length;i++){
+ var f = generatedJSDir + "/" + selectedModules[i] + "_inputs.js";
+ try{
+ delete require.cache[require.resolve(f)]
+ require.resolve();
+ }catch(err){
+ console.log("error deleting loaded module " + f + " from cache");
+ }
+ //console.log("loading file " + f);
+ try{
+ sliValuesObj.push(require(f));
+ }catch(err){
+ console.log("Error:Could not load file " + f);
+ }
+ }
+ //console.dir(sliValuesObj);
+ res.json({"sliValuesObj" : sliValuesObj});
+ });
+
+ app.get("/initialLoadSelectedModules",function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ var generatedJSDir=appDir + "/generatedJS";
+ var file = userDir + "/selected_modules";
+ var sliValuesObj =[];
+ var selected_modules = [];
+ var selectedModules;
+ try{
+ selectedModules = require(file);
+ selected_modules=selectedModules["selected_modules"];
+ //console.log("selected_modules are ");
+ //console.dir(selected_modules);
+ }catch(err){
+ console.log("Could not load the file " + file);
+ }
+ for(var i=0;selected_modules!= null && i<selected_modules.length;i++){
+ var f = generatedJSDir + "/" + selected_modules[i] + "_inputs.js";
+ console.log("loading file " + f);
+ try{
+ sliValuesObj.push(require(f));
+ }catch(err){
+ console.log("Error:Could not load file " + f);
+ }
+ }
+ res.json({"sliValuesObj" : sliValuesObj});
+ });
+
+ app.get("/listAvailableModules",function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ var generatedJSDir=appDir + "/generatedJS";
+ var glob = require("glob")
+ var file = userDir + "/selected_modules";
+ var selected_modules = [];
+ var selectedModules;
+ try{
+ delete require.cache[require.resolve(file)]
+ require.resolve();
+ }catch(err){
+ console.log("error deleting loaded module " + file + " from cache");
+ }
+ try{
+ selectedModules = require(file);
+ selected_modules=selectedModules["selected_modules"];
+ console.log("selected_modules are ");
+ //console.dir(selected_modules);
+ }catch(err){
+ console.log("Could not load the file " + file);
+ }
+ glob(generatedJSDir + "/**/*.js", null, function (er, files) {
+ var filesList =[];
+ for(var i=0;files!= null && i<files.length;i++){
+ var f = files[i].replace( new RegExp(generatedJSDir + "/", "g" ), "" );
+ f = f.replace("_inputs.js","");
+ if(selected_modules != undefined && selected_modules != null && selected_modules.indexOf(f) != -1){
+ filesList.push(f + ":checked");
+ }else{
+ filesList.push(f + ":unchecked");
+ }
+ }
+ res.json({"files" : filesList});
+ });
+ });
+
+ app.get("/listSLA",function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ var settingsFile = userDir + "/customSettings.js";
+ var jsonObj = require(settingsFile);
+ slaActions.listSLA(jsonObj,req,res);
+ });
+
+ app.get("/listCurrentDGs",function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ var settingsFile = userDir + "/customSettings.js";
+ var jsonObj = require(settingsFile);
+ slaActions.listCurrentDGs(jsonObj,req,res);
+ });
+
+ app.get("/activateDG",function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ var settingsFile = userDir + "/customSettings.js";
+ var jsonObj = require(settingsFile);
+ slaActions.activateDG(jsonObj,req,res);
+ });
+
+ app.get("/deActivateDG",function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ var settingsFile = userDir + "/customSettings.js";
+ var jsonObj = require(settingsFile);
+ slaActions.deActivateDG(jsonObj,req,res);
+ });
+
+ app.get("/deleteDG",function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ var settingsFile = userDir + "/customSettings.js";
+ var jsonObj = require(settingsFile);
+ slaActions.deleteDG(jsonObj,req,res);
+ });
+
+ app.get("/getCurrentSettings",function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ //console.log("userDir:" + userDir);
+ var settingsFile = userDir + "/customSettings.js";
+ var jsonObj = require(settingsFile);
+ res.json(jsonObj);
+ });
+
+ app.get("/getCommitsInfo", function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ //console.dir(req);
+ var filePath = req.query.filePath;
+ var fullFilePath = userDir + "/codecloud/" + filePath ;
+ //console.log("fullFilePath:" + fullFilePath);
+ var exec = require('child_process').exec;
+ var commandToExec = appDir + "/git_scripts/gitlog " + fullFilePath ;
+ console.log("commandToExec:" + commandToExec);
+ var child = exec(commandToExec ,function (error,stdout,stderr){
+ if(error){
+ console.log("Error occured:" + error);
+ if(stderr){
+ //console.log("stderr:" + stderr);
+ res.send(500,{'error':error,'stderr':stderr});
+ }else{
+ res.send(500,{'error':error});
+ }
+ //console.log("stdout :" + stdout);
+ }else{
+ if(stderr){
+ console.log("stderr:" + stderr);
+ }
+ if(stdout){
+ //console.log("output:" + stdout);
+ res.send(200,{'stdout':stdout,'stderr':stderr});
+ }
+ }
+ });
+ });
+
+ app.get("/importCodeCloudFlow",
+ function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ //console.dir(req);
+ var commitId = req.query.commitId;
+ var filePath = req.query.filePath;
+ var fullFilePath = userDir + "/codecloud/" + filePath ;
+ //console.log("fullFilePath:" + fullFilePath);
+ var exec = require('child_process').exec;
+ var commandToExec = appDir + "/git_scripts/gitckout " + commitId + " " + fullFilePath ;
+ console.log("commandToExec:" + commandToExec);
+ var child = exec(commandToExec ,{maxBuffer: 1024 * 1024 * 16}, function (error,stdout,stderr){
+ if(error){
+ console.log("Error occured:" + error);
+ if(stderr){
+ //console.log("stderr:" + stderr);
+ res.send(500,{'error':error,'stderr':stderr});
+ }else{
+ res.send(500,{'error':error});
+ }
+ }else{
+ if(stderr){
+ console.log("stderr:" + stderr);
+ }
+ if(stdout){
+ //console.log("output:" + stdout);
+ res.send(200,{'stdout':stdout,'stderr':stderr});
+ }
+ }
+ });
+ });
+
+ app.get("/importGitLocalFlow",
+ function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var gitLocalRepository = settings.gitLocalRepository;
+ //console.dir(req);
+ var filePath = req.query.filePath;
+ var fullFilePath = gitLocalRepository +"/" + filePath ;
+ //console.log("fullFilePath:" + fullFilePath);
+ var exec = require('child_process').exec;
+ var commandToExec = "cat " + fullFilePath ;
+ console.log("commandToExec:" + commandToExec);
+ var child = exec(commandToExec ,{maxBuffer: 1024 * 1024 * 16}, function (error,stdout,stderr){
+ if(error){
+ console.log("Error occured:" + error);
+ if(stderr){
+ //console.log("stderr:" + stderr);
+ res.send(500,{'error':error,'stderr':stderr});
+ }else{
+ res.send(500,{'error':error});
+ }
+ }else{
+ if(stderr){
+ console.log("stderr:" + stderr);
+ }
+ if(stdout){
+ //console.log("output:" + stdout);
+ res.send(200,{'stdout':stdout,'stderr':stderr});
+ }
+ }
+ });
+ });
+
+
+ app.get("/gitcheckout", function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var gitLocalRepository = settings.gitLocalRepository;
+ //console.dir(req);
+ var branch = req.query.branch;
+ //console.log("fullFilePath:" + fullFilePath);
+ var exec = require('child_process').exec;
+ var commandToExec = appDir + "/git_scripts/gitcheckout " + gitLocalRepository + " " + branch ;
+ console.log("commandToExec:" + commandToExec);
+ var child = exec(commandToExec ,function (error,stdout,stderr){
+ if(error){
+ console.log("Error occured:" + error);
+ if(stderr){
+ console.log("stderr:" + stderr);
+ res.json({"output":stderr});
+ }else{
+ res.json({"output":error});
+ }
+ }else{
+ if(stderr){
+ console.log("stderr:" + stderr);
+ }
+ if(stdout){
+ res.json({"output": stderr + " " + stdout});
+ }
+ }
+ });
+ });
+
+ app.get("/gitpull", function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var gitLocalRepository = settings.gitLocalRepository;
+ //console.dir(req);
+ var branch = req.query.branch;
+ //console.log("fullFilePath:" + fullFilePath);
+ var exec = require('child_process').exec;
+ var commandToExec = appDir + "/git_scripts/gitpull " + gitLocalRepository ;
+ console.log("commandToExec:" + commandToExec);
+ var child = exec(commandToExec ,function (error,stdout,stderr){
+ if(error){
+ console.log("Error occured:" + error);
+ if(stderr){
+ console.log("stderr:" + stderr);
+ res.json({"output":stderr});
+ }else{
+ res.json({"output":error});
+ }
+ }else{
+ if(stderr){
+ console.log("stderr:" + stderr);
+ }
+ if(stdout){
+ res.json({"output": stderr + " " + stdout});
+ }
+ }
+ });
+ });
+
+ app.get("/gitstatus", function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var gitLocalRepository = settings.gitLocalRepository;
+ //console.dir(req);
+ var branch = req.query.branch;
+ //console.log("fullFilePath:" + fullFilePath);
+ var exec = require('child_process').exec;
+ var commandToExec = appDir + "/git_scripts/gitstatus " + gitLocalRepository ;
+ console.log("commandToExec:" + commandToExec);
+ var child = exec(commandToExec ,function (error,stdout,stderr){
+ if(error){
+ console.log("Error occured:" + error);
+ if(stderr){
+ console.log("stderr:" + stderr);
+ res.json({"output":stderr});
+ }else{
+ res.json({"output":error});
+ }
+ }else{
+ if(stderr){
+ console.log("stderr:" + stderr);
+ }
+ if(stdout){
+ res.json({"output": stderr + " " + stdout});
+ }
+ }
+ });
+ });
+
+ app.post("/getSharedFlow",
+ express.json(),
+ function(req,res) {
+ var qs = require('querystring');
+ var body = '';
+ req.on('data', function (data) {
+ body += data;
+ });
+ req.on('end', function () {
+ var post = qs.parse(body);
+ //console.log("body:" + body);
+ fs.readFile(post.filePath, 'utf8', function (err,data) {
+ if (err) {
+ return console.log(err);
+ }
+ res.json(data);
+ //console.log(data);
+ });
+ //res.sendFile(body.filePath);
+ });
+ });
+
+ app.post("/downloadYang",
+ express.json(),
+ function(req,res) {
+ var qs = require('querystring');
+ var body = '';
+ req.on('data', function (data) {
+ body += data;
+ });
+ req.on('end', function () {
+ var post = qs.parse(body);
+ var fileName = post.fileName;
+ var appDir = path.dirname(require.main.filename);
+ var yangDir = appDir + "/yangFiles" ;
+ var fullPathToFile = yangDir + "/" + fileName;
+ res.setHeader('Content-disposition', 'attachment; filename=' + fileName);
+ res.setHeader('Content-type', 'application/yang');
+ res.download(fullPathToFile);
+ });
+ });
+
+ function writeToFile(fullPathToFileName,str){
+ try{
+ fs.writeFileSync(fullPathToFileName,str);
+ }catch(e){
+ console.log("Error:" + e);
+ }
+ }
+ function getCurrentDate(){
+ var d = new Date();
+ var mm = d.getMonth() + 1;
+ var dd = d.getDate();
+ var yyyy = d.getYear() + 1900;
+ var hr = d.getHours();
+ var min = d.getMinutes();
+ var sec = d.getSeconds();
+ if(mm<10) mm = "0" + mm;
+ if(dd<10) dd = "0" + dd;
+ if(hr<10) hr = "0" + hr;
+ if(min<10) min = "0" + min;
+ if(sec<10) sec = "0" + sec;
+ var formatedValue = mm + "-" + dd + "-" + yyyy + "_" + hr + "" + min + "" + sec;
+ return formatedValue;
+ }
+
+
+ app.post("/downloadXml",
+ express.json({'limit':'16mb'}),
+ function(req,res) {
+ //console.log("Received request and processing:" + new Date());
+ var qs = require('querystring');
+ var body = '';
+ //var msecs1= Date.now();
+ req.on('data', function (data) {
+ body += data;
+ });
+ req.on('end', function () {
+ var appDir = path.dirname(require.main.filename);
+ var xmlDir = appDir + "/" + settings.xmlPath;
+ //var msecs2= Date.now();
+ //console.log("Time taken to get request body:" + (msecs2 - msecs1));
+ var msecs3= Date.now();
+ var post = qs.parse(body);
+ var msecs4= Date.now();
+ //console.log("Time taken to parse body:" + (msecs4 - msecs3));
+ var xml = post['flowXml'];
+ //var pd = require('pretty-data').pd;
+ //var formatted_xml = pd.xml(xml);
+ var moduleName = post['moduleName'];
+ var methodName = post['methodName'];
+ if(moduleName == "" || methodName == ""){
+ res.send({"ERROR":"ServiceLogic Module Name and method name are required."});
+ }else{
+ //var formatted_date = getCurrentDate();
+ //var fileNameForServer=moduleName + "_" +methodName+ "_" + formatted_date + ".xml";
+ //var fileName=moduleName + "_method_" +methodName+ ".xml";
+ var fileName=moduleName + "_" +methodName+ ".xml";
+ var file = xmlDir + "/" + fileName;
+
+ //var msecs2= Date.now();
+ writeToFile(file,xml);
+ //var msecs3= Date.now();
+ //console.log("Time taken to write File:" + (msecs3 - msecs2));
+ res.setHeader('Content-disposition', 'attachment; filename=' + fileName);
+ res.setHeader('Content-type', 'text/xml');
+ res.end(xml);
+ //console.log("Response sent:" + new Date());
+ }
+ });
+ });
+
+ app.post("/downloadJson",
+ express.json({'limit':'16mb'}),
+ function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var sharedDir = appDir + "/" + settings.sharedDir;
+ var qs = require('querystring');
+ var body = '';
+ req.on('data', function (data) {
+ body += data;
+ });
+ req.on('end', function () {
+ var post = qs.parse(body);
+ var jsonStr = post['flowJson'];
+ var moduleName = post['moduleName'];
+ var methodName = post['methodName'];
+ //console.log("jsonStr:" + jsonStr);
+ if(moduleName == "" || methodName == ""){
+ res.send({"ERROR":"ServiceLogic Module Name and method name are required."});
+ }else{
+ var formatted_date = getCurrentDate();
+ //console.log("moduleName:" + moduleName);
+ //console.log("methodName:" + methodName);
+
+ //var fileName=moduleName + "_method_" +methodName + ".json";
+ //var renameOldfileTo=moduleName + "_method_" +methodName+ "_" + formatted_date + ".json";
+ var fileName=moduleName + "_" +methodName + ".json";
+ var renameOldfileTo=moduleName + "_" +methodName+ "_" + formatted_date + ".json";
+ var file = sharedDir + "/" + fileName;
+ //console.log("fileName:" + fileName);
+ var renameFilePath = sharedDir + "/backups/" + renameOldfileTo;
+ //console.log("localfile:" + localfile);
+ fs.rename(file,renameFilePath, function (err) {
+ if(err){
+ console.log('Error :' + err);
+ }
+ //write the newer version
+ writeToFile(file,jsonStr);
+ res.setHeader('Content-disposition', 'attachment; filename=' + fileName);
+ res.setHeader('Content-type', 'application/json');
+ //res.download(file);
+ res.end(jsonStr);
+ });
+ }
+ });
+ });
+
+ app.post("/flows",
+ express.json({'limit':'16mb'}),
+ function(req,res) {
+ //console.log("Processing Request");
+ var flows = req.body;
+ redNodes.setFlows(flows).then(function() {
+ res.send(204);
+ }).otherwise(function(err) {
+ util.log("[red] Error saving flows : "+err);
+ res.send(500,err.message);
+ });
+ },
+ function(error,req,res,next) {
+ res.send(400,"Invalid Flow. Error " + error);
+ }
+ );
+
+ app.get("/nodes",function(req,res) {
+ if (req.get("accept") == "application/json") {
+ res.json(redNodes.getNodeList());
+ } else {
+ res.send(redNodes.getNodeConfigs());
+ }
+ });
+
+ app.post("/nodes",
+ express.json(),
+ function(req,res) {
+ if (!settings.available()) {
+ res.send(400,new Error("Settings unavailable").toString());
+ return;
+ }
+ var node = req.body;
+ var promise;
+ if (node.file) {
+ promise = redNodes.addNode(node.file).then(reportAddedModules);
+ } else if (node.module) {
+ var module = redNodes.getNodeModuleInfo(node.module);
+ if (module) {
+ res.send(400,"Module already loaded");
+ return;
+ }
+ promise = installModule(node.module);
+ } else {
+ res.send(400,"Invalid request");
+ return;
+ }
+ promise.then(function(info) {
+ res.json(info);
+ }).otherwise(function(err) {
+ if (err.code === 404) {
+ res.send(404);
+ } else {
+ res.send(400,err.toString());
+ }
+ });
+ },
+ function(err,req,res,next) {
+ console.log(err.toString());
+ res.send(400,err);
+ }
+ );
+
+ app.delete("/nodes/:id",
+ function(req,res) {
+ if (!settings.available()) {
+ res.send(400,new Error("Settings unavailable").toString());
+ return;
+ }
+ var id = req.params.id;
+ var removedNodes = [];
+ try {
+ var node = redNodes.getNodeInfo(id);
+ var promise = null;
+ if (!node) {
+ var module = redNodes.getNodeModuleInfo(id);
+ if (!module) {
+ res.send(404);
+ return;
+ } else {
+ promise = uninstallModule(id);
+ }
+ } else {
+ promise = when.resolve([redNodes.removeNode(id)]).then(reportRemovedModules);
+ }
+
+ promise.then(function(removedNodes) {
+ res.json(removedNodes);
+ }).otherwise(function(err) {
+ console.log(err.stack);
+ res.send(400,err.toString());
+ });
+ } catch(err) {
+ res.send(400,err.toString());
+ }
+ },
+ function(err,req,res,next) {
+ res.send(400,err);
+ }
+ );
+
+ app.get("/nodes/:id", function(req,res) {
+ var id = req.params.id;
+ var result = null;
+ if (req.get("accept") == "application/json") {
+ result = redNodes.getNodeInfo(id);
+ } else {
+ result = redNodes.getNodeConfig(id);
+ }
+ if (result) {
+ res.send(result);
+ } else {
+ res.send(404);
+ }
+ });
+
+ app.put("/nodes/:id",
+ express.json(),
+ function(req,res) {
+ if (!settings.available()) {
+ res.send(400,new Error("Settings unavailable").toString());
+ return;
+ }
+ var body = req.body;
+ if (!body.hasOwnProperty("enabled")) {
+ res.send(400,"Invalid request");
+ return;
+ }
+ try {
+ var info;
+ var id = req.params.id;
+ var node = redNodes.getNodeInfo(id);
+ if (!node) {
+ res.send(404);
+ } else if (!node.err && node.enabled === body.enabled) {
+ res.json(node);
+ } else {
+ if (body.enabled) {
+ info = redNodes.enableNode(id);
+ } else {
+ info = redNodes.disableNode(id);
+ }
+ if (info.enabled == body.enabled && !info.err) {
+ comms.publish("node/"+(body.enabled?"enabled":"disabled"),info,false);
+ util.log("[red] "+(body.enabled?"Enabled":"Disabled")+" node types:");
+ for (var i=0;i<info.types.length;i++) {
+ util.log("[red] - "+info.types[i]);
+ }
+ } else if (body.enabled && info.err) {
+ util.log("[red] Failed to enable node:");
+ util.log("[red] - "+info.name+" : "+info.err);
+ }
+ res.json(info);
+ }
+ } catch(err) {
+ res.send(400,err.toString());
+ }
+ }
+ );
+ app.get("/getCodeCloudFlows",function(req,res) {
+ var userDir=settings.userDir;
+ var codeCloudDir=userDir + "/codecloud";
+ var glob = require("glob")
+ glob(codeCloudDir + "/**/*.json", null, function (er, files) {
+ // files is an array of filenames.
+ // If the `nonull` option is set, and nothing
+ // was found, then files is ["**/*.js"]
+ // er is an error object or null.
+ //console.dir(files);
+ var filesList =[];
+ for(var i=0;files!= null && i<files.length;i++){
+ var f = files[i].replace( new RegExp(codeCloudDir + "/", "g" ), "" );
+ filesList.push(f);
+
+ }
+ res.json({"files" : filesList});
+ });
+ });
+
+ app.get("/getCurrentGitBranch",function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var userDir=settings.userDir;
+ var settingsFile = appDir + "/" + userDir + "/customSettings.js";
+ //console.log("settingsFile:" + settingsFile);
+ var jsonObj = require(settingsFile);
+ var gitLocalRepository=jsonObj.gitLocalRepository;
+ if(gitLocalRepository == undefined || gitLocalRepository == null || gitLocalRepository == ''){
+ res.json({"output" : "GIT_LOCAL_REPOSITORY_NOT_SET"});
+ return;
+ }
+ var exec = require('child_process').exec;
+ var commandToExec = appDir + "/git_scripts/gitcurbranch " + gitLocalRepository ;
+ console.log("commandToExec:" + commandToExec);
+ var child = exec(commandToExec ,function (error,stdout,stderr){
+ if(error){
+ console.log("Error occured:" + error);
+ if(stderr){
+ console.log("stderr:" + stderr);
+ res.json({"output":stderr});
+ }else{
+ res.json({"output":error});
+ }
+ }else{
+ if(stderr){
+ console.log("stderr:" + stderr);
+ }
+ if(stdout){
+ res.json({"output":stdout});
+ }
+ }
+ });
+
+ });
+
+ app.get("/getGitLocalFlows",function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var userDir=settings.userDir;
+ var settingsFile = appDir + "/" + userDir + "/customSettings.js";
+ //console.log("settingsFile:" + settingsFile);
+ var jsonObj = require(settingsFile);
+ var performGitPull = jsonObj.performGitPull;
+ if(performGitPull == undefined || performGitPull == null) {
+ performGitPull="N";
+ }
+ var gitLocalRepository=jsonObj.gitLocalRepository;
+ if(gitLocalRepository == undefined || gitLocalRepository == null || gitLocalRepository == ''){
+ res.json({"files" : ["GIT_LOCAL_REPOSITORY_NOT_SET"]});
+ return;
+
+ }
+
+ if(performGitPull == "Y"){
+ var exec = require('child_process').exec;
+ var commandToExec = appDir + "/git_scripts/gitpull " + gitLocalRepository ;
+ console.log("commandToExec:" + commandToExec);
+ var child = exec(commandToExec ,function (error,stdout,stderr){
+ if(error){
+ console.log("Error occured:" + error);
+ if(stderr){
+ console.log("stderr:" + stderr);
+ res.json({"files":[]});
+ }else{
+ res.json({"files":[]});
+ }
+ }else{
+ if(stderr){
+ console.log("stderr:" + stderr);
+ }
+ if(stdout){
+ var glob = require("glob")
+ glob(gitLocalRepository + "/**/*.json", null, function (er, files) {
+ // files is an array of filenames.
+ // If the `nonull` option is set, and nothing
+ // was found, then files is ["**/*.js"]
+ // er is an error object or null.
+ //console.dir(files);
+ var filesList =[];
+ for(var i=0;files!= null && i<files.length;i++){
+ var f = files[i].replace( new RegExp(gitLocalRepository + "/", "g" ), "" );
+ filesList.push(f);
+
+ }
+ res.json({"files" : filesList});
+ });
+ }
+ }
+ });
+ }else{//git pull not requested
+ var glob = require("glob")
+ glob(gitLocalRepository + "/**/*.json", null, function (er, files) {
+ // files is an array of filenames.
+ // If the `nonull` option is set, and nothing
+ // was found, then files is ["**/*.js"]
+ // er is an error object or null.
+ //console.dir(files);
+ var filesList =[];
+ for(var i=0;files!= null && i<files.length;i++){
+ var f = files[i].replace( new RegExp(gitLocalRepository + "/", "g" ), "" );
+ filesList.push(f);
+
+ }
+ res.json({"files" : filesList});
+ });
+ }
+
+ });
+
+ app.get("/flowShareUsers",function(req,res) {
+ res.json(flowShareUsers);
+ });
+ app.get("/getRelease",function(req,res) {
+ var userDir = settings.userDir;
+ //var release = userDir.replace(/releases/g,"release");
+ res.json({"release" : userDir});
+ });
+ app.post("/getFiles/:id",function(req,res) {
+ var id = req.params.id;
+ //console.log("id:" + id);
+ var userDir=settings.userDir;
+ var flowDir= userDir + "/../" + id + "/flows/shared";
+ //console.log("flowDir:" + flowDir);
+ fs.readdir(flowDir,function(err, files){
+ if(err){
+ res.json({"files": []});
+ }else{
+ var onlyFilesArr =[];
+ if(files != null && files.length>0){
+ files.sort(function(a,b){
+ //console.log("file1:" + a);
+ //console.log("file2:" + b);
+ var fileStat1=fs.statSync(flowDir+ "/" + a);
+ var fileStat2=fs.statSync(flowDir+ "/" + b);
+ if(fileStat1.mtime > fileStat2.mtime){
+ return 1;
+ }else if(fileStat1.mtime < fileStat2.mtime){
+ return -1;
+ }else{
+ return 0;
+ }
+ });
+ for(var i=0;i<files.length;i++){
+ var fileStat=fs.statSync(flowDir+ "/" + files[i]);
+ if(fileStat.isFile()){
+ onlyFilesArr.push({"filePath":flowDir+ "/" + files[i],"name":files[i]});
+ }
+ }
+ res.json(onlyFilesArr);
+ }else{
+ res.json({"files": []});
+ }
+ }
+ });
+ });
+
+ app.post("/updateConfiguration",
+ express.json(),
+ function(req,res) {
+ var qs = require('querystring');
+ //console.log("Received the request:");
+ var body ="";
+ req.on('data', function (data) {
+ body += data;
+ });
+ req.on('end',function(){
+ var post = qs.parse(body);
+ var dbHost = post["dbHost"];
+ var dbPort = post["dbPort"];
+ var dbName = post["dbName"];
+ var dbUser = post["dbUser"];
+ var dbPassword = post["dbPassword"];
+ var gitLocalRepository = post["gitLocalRepository"];
+ var performGitPull = post["performGitPull"];
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ console.log("userDir:" + userDir);
+ try{
+ var settingsFile = userDir + "/customSettings.js";
+ var jsonObj = require(settingsFile);
+ jsonObj.flowFile = jsonObj.flowFile.replace(appDir + "/",'');
+ jsonObj.dbHost = dbHost;
+ jsonObj.dbPort = dbPort;
+ jsonObj.dbName = dbName;
+ jsonObj.dbUser = dbUser;
+ jsonObj.dbPassword = dbPassword;
+ jsonObj.gitLocalRepository = gitLocalRepository;
+ jsonObj.performGitPull = performGitPull;
+ var updatedSettings = jsonObj;
+
+ var settingsStr= "module.exports=" + JSON.stringify(updatedSettings,null,4);
+ //console.log("settingsStr:" + settingsStr);
+ fs.writeFileSync(settingsFile,settingsStr);
+ var svcLogicPropStr = "" ;
+ svcLogicPropStr += "org.openecomp.sdnc.sli.dbtype=jdbc" + "\n";
+ svcLogicPropStr += "org.openecomp.sdnc.sli.jdbc.url=jdbc:mysql://" + dbHost + ":" + dbPort + "/" + dbName + "\n";
+ svcLogicPropStr += "org.openecomp.sdnc.sli.jdbc.database=" + dbName + "\n";
+ svcLogicPropStr += "org.openecomp.sdnc.sli.jdbc.user=" + dbUser + "\n";
+ svcLogicPropStr += "org.openecomp.sdnc.sli.jdbc.password=" + dbPassword;
+
+ //create svclogic.properties file in the conf dir
+ var svcPropFile = userDir + "/conf/svclogic.properties";
+ fs.writeFileSync(svcPropFile,svcLogicPropStr);
+
+ res.send({"status": "success"});
+ }catch(e){
+ console.log("Error:" + e);
+ res.send({"status": "error"});
+ }
+ });
+ }
+ );
+
+ app.post("/deleteYangFile",
+ express.json(),
+ function(req,res) {
+ var qs = require('querystring');
+ //console.log("Received the request:");
+ var body ="";
+ req.on('data', function (data) {
+ body += data;
+ });
+ req.on('end',function(){
+ var post = qs.parse(body);
+ //console.dir(body);
+ var fileName = post["fileName"];
+ var appDir = path.dirname(require.main.filename);
+ var yangFilePath = appDir + "/yangFiles/" + fileName;
+ try{
+ fs.unlinkSync(yangFilePath);
+ res.send({"status" :"SUCCESS"});
+ }catch(err){
+ console.log("error" + err);
+ res.send({"status" :"ERROR"});
+ }
+ //console.log("prevPassword:" + settings.httpAuth.pass );
+ });
+ }
+ );
+
+ app.post("/updatePassword",
+ express.json(),
+ function(req,res) {
+ var qs = require('querystring');
+ //console.log("Received the request:");
+ var body ="";
+ req.on('data', function (data) {
+ body += data;
+ });
+ req.on('end',function(){
+ var post = qs.parse(body);
+ //console.dir(body);
+ var password = post["password"];
+ //console.log("prevPassword:" + settings.httpAuth.pass );
+ //console.log("New password:" + password);
+ var crypto = require("crypto");
+ var cryptPasswd = crypto.createHash('md5').update(password,'utf8').digest('hex')
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ //console.log("userDir:" + userDir);
+ /*var newSettings = settings;
+ newSettings.httpAuth.pass = cryptPasswd;
+ var updatedSettings = JSON.stringify(settings,null,4);
+ var settingsStr = "module.exports=" + updatedSettings;
+ console.log(updatedSettings);
+ */
+ try{
+ var settingsFile = userDir + "/customSettings.js";
+ //console.log("settingsFile:" + settingsFile);
+ //var buf = fs.readFileSync(settingsFile, "utf8");
+ var jsonObj = require(settingsFile);
+ //console.log("jsonObj:" + JSON.stringify(jsonObj));
+ jsonObj.httpAuth.pass = cryptPasswd;
+ jsonObj.httpAdminAuth.pass = cryptPasswd;
+ jsonObj.httpNodeAuth.pass = cryptPasswd;
+ jsonObj.flowFile = jsonObj.flowFile.replace(appDir + "/",'');
+ var updatedSettings = jsonObj;
+ /*
+ delete updatedSettings.httpRoot;
+ delete updatedSettings.disableEditor;
+ delete updatedSettings.httpAdminRoot;
+ delete updatedSettings.httpAdminAuth;
+ delete updatedSettings.httpNodeRoot;
+ delete updatedSettings.httpNodeAuth;
+ delete updatedSettings.uiHost;
+ delete updatedSettings.version;
+ */
+ var settingsStr= "module.exports=" + JSON.stringify(updatedSettings,null,4);
+ //console.log("settingsStr:" + settingsStr);
+ fs.writeFileSync(settingsFile,settingsStr);
+ settings.httpAuth.pass = cryptPasswd;
+ res.send({"status": "success"});
+ }catch(e){
+ console.log("Error:" + e);
+ res.send({"status": "error"});
+ }
+ });
+ }
+ );
+
+ var appDir = path.dirname(require.main.filename);
+ var yangDir = appDir + "/yangFiles" ;
+ var diskStorage = multer.diskStorage({
+ destination: function (req, file, callback) {
+ callback(null, yangDir);
+ },
+ filename: function (req, file, callback) {
+ //callback(null, file.fieldname + '-' + Date.now());
+ callback(null, file.originalname);
+ }
+ });
+ var upload = multer({ storage : diskStorage}).single('yangFile');
+
+ app.post('/api/uploadyang',function(req,res){
+ upload(req,res,function(err) {
+ if(err) {
+ console.log(err);
+ return res.end("Error uploading file." + err);
+ }
+ //console.dir(req);
+ var fileName = req.file.originalname;
+ var yangFileFullPath = appDir + "/yangFiles/" + fileName;
+ console.log("yangFileFullPath:" + yangFileFullPath);
+ var commandToExec ="";
+ if(fileName != null){
+ var matchedArr = fileName.match(/.zip$/);
+ if(matchedArr != null && matchedArr.length >0){
+ console.log("uploaded zip file" + fileName);
+ commandToExec = appDir + "/tools/generate_props_from_yangs_zip.sh " + yangFileFullPath ;
+ }else{
+ commandToExec = appDir + "/tools/generate_props_from_yang.sh " + yangFileFullPath ;
+ console.log("uploaded file" + fileName);
+ }
+ }
+ var exec = require('child_process').exec;
+ console.log("commandToExec:" + commandToExec);
+ var child = exec(commandToExec ,function (error,stdout,stderr){
+ if(error){
+ console.log("Error occured:" + error);
+ var msg = "File " + fileName + " could not be processed successfully.";
+ if(stderr){
+ console.log("stderr:" + stderr);
+ res.json({"sliValuesObj" : [],"message":msg});
+ }else{
+ res.json({"sliValuesObj" : [],"message":msg});
+ }
+ }else{
+ if(stderr){
+ console.log("stderr:" + stderr);
+ }
+ if(stdout){
+ console.log("stdout:" + stdout);
+ }
+ var msg = "File " + fileName + " processed successfully.";
+ var generatedJSDir=appDir + "/generatedJS";
+ var sliValuesObj =[];
+ //var glob = require("glob");
+ //glob(generatedJSDir + "/**/*.js", null, function (er, files) {
+ /*
+ var sliValuesObj =[];
+ for(var i=0;files!= null && i<files.length;i++){
+ var f = files[i].replace( new RegExp(generatedJSDir + "/", "g" ), "" );
+ console.log("loading file " + f);
+ try{
+ sliValuesObj.push(require(files[i]));
+ //console.dir(sliValuesObj);
+ }catch(err){
+ console.log("Error:Could not load file " + files[i]);
+ }
+ }
+ res.json({"sliValuesObj" : sliValuesObj,"message":msg});
+ });
+ */
+ res.json({"sliValuesObj" : sliValuesObj,"message":msg});
+ }
+ });
+ });
+ });
+ app.get("/getYangFiles",function(req,res) {
+ var appDir = path.dirname(require.main.filename);
+ var yangFilesDir=appDir + "/yangFiles";
+ var glob = require("glob")
+ glob(yangFilesDir + "/**/*.yang", null, function (er, files) {
+ var filesList =[];
+ for(var i=0;files!= null && i<files.length;i++){
+ var f = files[i].replace( new RegExp(yangFilesDir + "/", "g" ), "" );
+ filesList.push(f);
+
+ }
+ res.json({"files" : filesList});
+ });
+ });
+ }
+}
+
+function reportAddedModules(info) {
+ comms.publish("node/added",info,false);
+ if (info.length > 0) {
+ util.log("[red] Added node types:");
+ for (var i=0;i<info.length;i++) {
+ for (var j=0;j<info[i].types.length;j++) {
+ util.log("[red] - "+
+ (info[i].module?info[i].module+":":"")+
+ info[i].types[j]+
+ (info[i].err?" : "+info[i].err:"")
+ );
+ }
+ }
+ }
+ return info;
+}
+
+function reportRemovedModules(removedNodes) {
+ comms.publish("node/removed",removedNodes,false);
+ util.log("[red] Removed node types:");
+ for (var j=0;j<removedNodes.length;j++) {
+ for (var i=0;i<removedNodes[j].types.length;i++) {
+ util.log("[red] - "+(removedNodes[i].module?removedNodes[i].module+":":"")+removedNodes[j].types[i]);
+ }
+ }
+ return removedNodes;
+}
+
+function installModule(module) {
+ //TODO: ensure module is 'safe'
+ return when.promise(function(resolve,reject) {
+ if (/[\s;]/.test(module)) {
+ reject(new Error("Invalid module name"));
+ return;
+ }
+ util.log("[red] Installing module: "+module);
+ var child = exec('npm install --production '+module, function(err, stdin, stdout) {
+ if (err) {
+ var lookFor404 = new RegExp(" 404 .*"+module+"$","m");
+ if (lookFor404.test(stdout)) {
+ util.log("[red] Installation of module "+module+" failed: module not found");
+ var e = new Error();
+ e.code = 404;
+ reject(e);
+ } else {
+ util.log("[red] Installation of module "+module+" failed:");
+ util.log("------------------------------------------");
+ console.log(err.toString());
+ util.log("------------------------------------------");
+ reject(new Error("Install failed"));
+ }
+ } else {
+ util.log("[red] Installed module: "+module);
+ resolve(redNodes.addModule(module).then(reportAddedModules));
+ }
+ });
+ });
+}
+
+function uninstallModule(module) {
+ var list = redNodes.removeModule(module);
+ return when.promise(function(resolve,reject) {
+ if (/[\s;]/.test(module)) {
+ reject(new Error("Invalid module name"));
+ return;
+ }
+ util.log("[red] Removing module: "+module);
+ var child = exec('npm remove '+module, function(err, stdin, stdout) {
+ if (err) {
+ util.log("[red] Removal of module "+module+" failed:");
+ util.log("------------------------------------------");
+ console.log(err.toString());
+ util.log("------------------------------------------");
+ reject(new Error("Removal failed"));
+ } else {
+ util.log("[red] Removed module: "+module);
+ reportRemovedModules(list);
+ resolve(list);
+ }
+ });
+ });
+}
+
+function start() {
+ var defer = when.defer();
+
+ storage.init(settings).then(function() {
+ settings.load(storage).then(function() {
+ console.log("\nWelcome to Node-RED\n===================\n");
+ if (settings.version) {
+ util.log("[red] Version: "+settings.version);
+ }
+ util.log("[red] Loading palette nodes");
+ redNodes.init(settings,storage);
+ redNodes.load().then(function() {
+ var i;
+ var nodes = redNodes.getNodeList();
+ var nodeErrors = nodes.filter(function(n) { return n.err!=null;});
+ var nodeMissing = nodes.filter(function(n) { return n.module && n.enabled && !n.loaded && !n.err;});
+ if (nodeErrors.length > 0) {
+ util.log("------------------------------------------");
+ if (settings.verbose) {
+ for (i=0;i<nodeErrors.length;i+=1) {
+ util.log("["+nodeErrors[i].name+"] "+nodeErrors[i].err);
+ }
+ } else {
+ util.log("[red] Failed to register "+nodeErrors.length+" node type"+(nodeErrors.length==1?"":"s"));
+ util.log("[red] Run with -v for details");
+ }
+ util.log("------------------------------------------");
+ }
+ if (nodeMissing.length > 0) {
+ util.log("[red] Missing node modules:");
+ var missingModules = {};
+ for (i=0;i<nodeMissing.length;i++) {
+ var missing = nodeMissing[i];
+ missingModules[missing.module] = (missingModules[missing.module]||[]).concat(missing.types);
+ }
+ var promises = [];
+ for (i in missingModules) {
+ if (missingModules.hasOwnProperty(i)) {
+ util.log("[red] - "+i+": "+missingModules[i].join(", "));
+ if (settings.autoInstallModules) {
+ installModule(i).otherwise(function(err) {
+ // Error already reported. Need the otherwise handler
+ // to stop the error propagating any further
+ });
+ }
+ }
+ }
+ if (!settings.autoInstallModules) {
+ util.log("[red] Removing modules from config");
+ redNodes.cleanNodeList();
+ }
+ }
+ defer.resolve();
+
+ redNodes.loadFlows();
+ }).otherwise(function(err) {
+ console.log(err);
+ });
+ comms.start();
+ });
+ }).otherwise(function(err) {
+ defer.reject(err);
+ });
+
+ return defer.promise;
+}
+
+function stop() {
+ redNodes.stopFlows();
+ comms.stop();
+}
+
+module.exports = {
+ init: createServer,
+ start: start,
+ stop: stop
+}
+
+module.exports.__defineGetter__("app", function() { return app });
+module.exports.__defineGetter__("nodeApp", function() { return nodeApp });
+module.exports.__defineGetter__("server", function() { return server });
diff --git a/dgbuilder/red/settings.js b/dgbuilder/red/settings.js
new file mode 100644
index 00000000..4994953f
--- /dev/null
+++ b/dgbuilder/red/settings.js
@@ -0,0 +1,84 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * 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 when = require("when");
+
+var assert = require("assert");
+
+var userSettings = null;
+var globalSettings = null;
+var storage = null;
+
+var persistentSettings = {
+ init: function(settings) {
+ userSettings = settings;
+
+ for (var i in settings) {
+ if (settings.hasOwnProperty(i)) {
+ (function() {
+ var j = i;
+ persistentSettings.__defineGetter__(j,function() { return userSettings[j]; });
+ persistentSettings.__defineSetter__(j,function() { throw new Error("Property '"+i+"' is read-only"); });
+ })();
+ }
+ }
+ globalSettings = null;
+ },
+ load: function(_storage) {
+ storage = _storage;
+ return storage.getSettings().then(function(_settings) {
+ globalSettings = _settings;
+ });
+ },
+ get: function(prop) {
+ if (userSettings.hasOwnProperty(prop)) {
+ return userSettings[prop];
+ }
+ if (globalSettings === null) {
+ throw new Error("Settings not available");
+ }
+ return globalSettings[prop];
+ },
+
+ set: function(prop,value) {
+ if (userSettings.hasOwnProperty(prop)) {
+ throw new Error("Property '"+prop+"' is read-only");
+ }
+ if (globalSettings === null) {
+ throw new Error("Settings not available");
+ }
+ var current = globalSettings[prop];
+ globalSettings[prop] = value;
+ try {
+ assert.deepEqual(current,value);
+ return when.resolve();
+ } catch(err) {
+ return storage.saveSettings(globalSettings);
+ }
+ },
+
+ available: function() {
+ return (globalSettings !== null);
+ },
+
+ reset: function() {
+ userSettings = null;
+ globalSettings = null;
+ storage = null;
+ }
+}
+
+module.exports = persistentSettings;
diff --git a/dgbuilder/red/sla.js b/dgbuilder/red/sla.js
new file mode 100644
index 00000000..1729ecbb
--- /dev/null
+++ b/dgbuilder/red/sla.js
@@ -0,0 +1,249 @@
+exports.listSLA = function(settings,req,res) {
+try{
+ var mysql = require('mysql');
+
+ //console.dir(settings);
+
+ var connection = mysql.createConnection(
+ {
+ host : settings.dbHost,
+ port : settings.dbPort,
+ user : settings.dbUser,
+ password : settings.dbPassword,
+ database : settings.dbName
+ });
+
+ var rows=[];
+
+ var sqlQuery = "SELECT module,rpc,version,mode,active FROM SVC_LOGIC";
+ //console.log("sqlQuery:" + sqlQuery);
+ connection.query(sqlQuery, function(err, rows) {
+ if(!err) {
+ if ( rows.length > 0 )
+ {
+ res.send({ 'rows': rows,'dbHost':settings.dbHost } );
+ }else{
+ res.send({'rows': [],'dbHost':settings.dbHost});
+ }
+ } else {
+ console.log("error:" + err);
+ res.send({error: "Connection to DB failed.",'dbHost':settings.dbHost});
+ }
+ //console.dir(rows);
+ connection.end();
+ }); //end query
+}catch(error){
+ console.log(error);
+ res.send({'error': "Connection to DB failed.",'dbHost':settings.dbHost});
+}
+}
+
+exports.listCurrentDGs = function(settings,req,res) {
+ var _module = req.query.module;
+ var rpc = req.query.rpc;
+ console.log("_module:" + _module);
+ console.log("rpc:" + rpc);
+try{
+ var mysql = require('mysql');
+
+ //console.dir(settings);
+
+ var connection = mysql.createConnection(
+ {
+ host : settings.dbHost,
+ port : settings.dbPort,
+ user : settings.dbUser,
+ password : settings.dbPassword,
+ database : settings.dbName
+ });
+
+ var rows=[];
+
+ var sqlQuery = "SELECT module,rpc,version,mode,active FROM SVC_LOGIC where module ='" + _module + "' and rpc ='" + rpc + "'";
+ console.log("sqlQuery:" + sqlQuery);
+ connection.query(sqlQuery, function(err, rows) {
+ if(!err) {
+ if ( rows.length > 0 )
+ {
+ res.send({ 'rows': rows,'dbHost':settings.dbHost } );
+ }else{
+ res.send({'rows': [],'dbHost':settings.dbHost});
+ }
+ } else {
+ console.log("error:" + err);
+ res.send({error: "Connection to DB failed.",'dbHost':settings.dbHost});
+ }
+ //console.dir(rows);
+ connection.end();
+ }); //end query
+}catch(error){
+ console.log(error);
+ res.send({'error': "Connection to DB failed.",'dbHost':settings.dbHost});
+}
+}
+
+exports.activateDG = function(settings,req,res){
+ var _module = req.query.module;
+ var rpc = req.query.rpc;
+ var version = req.query.version;
+ var mode = req.query.mode;
+ var displayOnlyCurrent = req.query.displayOnlyCurrent;
+
+try{
+ var mysql = require('mysql');
+
+ var connection = mysql.createConnection(
+ {
+ host : settings.dbHost,
+ port : settings.dbPort,
+ user : settings.dbUser,
+ password : settings.dbPassword,
+ database : settings.dbName
+ });
+
+ var rows=[];
+
+ var updateStmt = "UPDATE SVC_LOGIC SET active=\'Y\' WHERE module=\'"
+ + _module + "' AND rpc=\'"
+ + rpc + "' AND version=\'"
+ + version + "' AND mode=\'"
+ + mode + "'";
+
+ connection.query(updateStmt, function(err, result) {
+ var nextUpdateStmt = "UPDATE SVC_LOGIC SET active=\'N\' WHERE module=\'"
+ + _module + "' AND rpc=\'"
+ + rpc + "' AND version !=\'"
+ + version + "'";
+ connection.query(nextUpdateStmt, function(err, result) {
+ var query = "SELECT module,rpc,version,mode,active FROM SVC_LOGIC";
+ if(displayOnlyCurrent == 'true'){
+ query = "SELECT module,rpc,version,mode,active FROM SVC_LOGIC WHERE module=\'"
+ + _module + "' and rpc=\'" + rpc + "'";
+ }
+ connection.query(query, function(err, rows) {
+ if(!err) {
+ if ( rows.length > 0 )
+ {
+ res.send({ 'rows': rows,'dbHost':settings.dbHost } );
+ }else{
+ res.send({'rows': [],'dbHost':settings.dbHost});
+ }
+ } else {
+ res.send({'error': "Connection to DB failed.",'dbHost':settings.dbHost});
+ }
+ connection.end();
+ }); //end query
+ }); //end query
+ }); //end query
+}catch(error){
+ res.send({'error': "Connection to DB failed.",'dbHost':settings.dbHost});
+}
+}
+
+
+exports.deActivateDG = function(settings,req,res){
+
+ var _module = req.query.module;
+ var rpc = req.query.rpc;
+ var version = req.query.version;
+ var mode = req.query.mode;
+ var displayOnlyCurrent = req.query.displayOnlyCurrent;
+
+try{
+ var mysql = require('mysql');
+
+ var connection = mysql.createConnection(
+ {
+ host : settings.dbHost,
+ port : settings.dbPort,
+ user : settings.dbUser,
+ password : settings.dbPassword,
+ database : settings.dbName
+ });
+
+ var rows=[];
+
+ var updateStmt = "UPDATE SVC_LOGIC SET active=\'N\' WHERE module=\'"
+ + _module + "' AND rpc=\'"
+ + rpc + "' AND version=\'"
+ + version + "' AND mode=\'"
+ + mode + "'";
+
+ connection.query(updateStmt, function(err, result) {
+ var query = "SELECT module,rpc,version,mode,active FROM SVC_LOGIC";
+ if(displayOnlyCurrent == 'true'){
+ query = "SELECT module,rpc,version,mode,active FROM SVC_LOGIC WHERE module=\'"
+ + _module + "' and rpc=\'" + rpc + "'";
+ }
+ connection.query(query, function(err, rows) {
+ if(!err) {
+ if ( rows.length > 0 )
+ {
+ res.send({ 'rows': rows,'dbHost':settings.dbHost } );
+ }else{
+ res.send({'rows': [],'dbHost':settings.dbHost});
+ }
+ } else {
+ res.send({'error': "Connection to DB failed.",'dbHost':settings.dbHost});
+ }
+ connection.end();
+ }); //end query
+ }); //end query
+}catch(error){
+ res.send({'error': "Connection to DB failed.",'dbHost':settings.dbHost});
+}
+}
+
+exports.deleteDG = function(settings,req,res){
+
+ var _module = req.query.module;
+ var rpc = req.query.rpc;
+ var version = req.query.version;
+ var mode = req.query.mode;
+ var displayOnlyCurrent = req.query.displayOnlyCurrent;
+
+try{
+ var mysql = require('mysql');
+
+ var connection = mysql.createConnection(
+ {
+ host : settings.dbHost,
+ port : settings.dbPort,
+ user : settings.dbUser,
+ password : settings.dbPassword,
+ database : settings.dbName
+ });
+
+ var rows=[];
+
+ var deleteStmt = "DELETE FROM SVC_LOGIC WHERE module=\'"
+ + _module + "' AND rpc=\'"
+ + rpc + "' AND version=\'"
+ + version + "' AND mode=\'"
+ + mode + "'";
+ console.log(deleteStmt);
+
+ connection.query(deleteStmt, function(err, result) {
+ var query = "SELECT module,rpc,version,mode,active FROM SVC_LOGIC";
+ if(displayOnlyCurrent == 'true'){
+ query = "SELECT module,rpc,version,mode,active FROM SVC_LOGIC WHERE module=\'"
+ + _module + "' and rpc=\'" + rpc + "'";
+ }
+ connection.query(query, function(err, rows) {
+ if(!err) {
+ if ( rows.length > 0 )
+ {
+ res.send({ 'rows': rows,'dbHost':settings.dbHost } );
+ }else{
+ res.send({'rows': [],'dbHost':settings.dbHost});
+ }
+ } else {
+ res.send({'error': "Connection to DB failed.",'dbHost':settings.dbHost});
+ }
+ connection.end();
+ }); //end query
+ }); //end query
+}catch(error){
+ res.send({'error': "Connection to DB failed.",'dbHost':settings.dbHost});
+}
+}
diff --git a/dgbuilder/red/storage/index.js b/dgbuilder/red/storage/index.js
new file mode 100644
index 00000000..ba939627
--- /dev/null
+++ b/dgbuilder/red/storage/index.js
@@ -0,0 +1,107 @@
+/**
+ * Copyright 2013, 2014 IBM Corp.
+ *
+ * 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 when = require('when');
+
+var storageModule;
+var settingsAvailable;
+
+function moduleSelector(aSettings) {
+ var toReturn;
+ if (aSettings.storageModule) {
+ if (typeof aSettings.storageModule === "string") {
+ // TODO: allow storage modules to be specified by absolute path
+ toReturn = require("./"+aSettings.storageModule);
+ } else {
+ toReturn = aSettings.storageModule;
+ }
+ } else {
+ toReturn = require("./localfilesystem");
+ }
+ return toReturn;
+}
+
+function is_malicious(path) {
+ return path.indexOf('../') != -1 || path.indexOf('..\\') != -1;
+}
+
+var storageModuleInterface = {
+ init: function(settings) {
+ try {
+ storageModule = moduleSelector(settings);
+ settingsAvailable = storageModule.hasOwnProperty("getSettings") && storageModule.hasOwnProperty("saveSettings");
+ } catch (e) {
+ return when.reject(e);
+ }
+ return storageModule.init(settings);
+ },
+ getFlows: function() {
+ return storageModule.getFlows();
+ },
+ saveFlows: function(flows) {
+ return storageModule.saveFlows(flows);
+ },
+ getCredentials: function() {
+ return storageModule.getCredentials();
+ },
+ saveCredentials: function(credentials) {
+ return storageModule.saveCredentials(credentials);
+ },
+ getSettings: function() {
+ if (settingsAvailable) {
+ return storageModule.getSettings();
+ } else {
+ return when.resolve(null);
+ }
+ },
+ saveSettings: function(settings) {
+ if (settingsAvailable) {
+ return storageModule.saveSettings(settings);
+ } else {
+ return when.resolve();
+ }
+ },
+ /* Library Functions */
+ getAllFlows: function() {
+ return storageModule.getAllFlows();
+ },
+ getFlow: function(fn) {
+ if (is_malicious(fn)) {
+ return when.reject(new Error('forbidden flow name'));
+ }
+ return storageModule.getFlow(fn);
+ },
+ saveFlow: function(fn, data) {
+ if (is_malicious(fn)) {
+ return when.reject(new Error('forbidden flow name'));
+ }
+ return storageModule.saveFlow(fn, data);
+ },
+ getLibraryEntry: function(type, path) {
+ if (is_malicious(path)) {
+ return when.reject(new Error('forbidden flow name'));
+ }
+ return storageModule.getLibraryEntry(type, path);
+ },
+ saveLibraryEntry: function(type, path, meta, body) {
+ if (is_malicious(path)) {
+ return when.reject(new Error('forbidden flow name'));
+ }
+ return storageModule.saveLibraryEntry(type, path, meta, body);
+ }
+}
+
+module.exports = storageModuleInterface;
diff --git a/dgbuilder/red/storage/localfilesystem.js b/dgbuilder/red/storage/localfilesystem.js
new file mode 100644
index 00000000..48255332
--- /dev/null
+++ b/dgbuilder/red/storage/localfilesystem.js
@@ -0,0 +1,309 @@
+/**
+ * Copyright 2013, 2014 IBM Corp.
+ *
+ * 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 fs = require('fs');
+var when = require('when');
+var nodeFn = require('when/node/function');
+var keys = require('when/keys');
+var util = require('util');
+var fspath = require("path");
+var mkdirp = require("mkdirp");
+
+var promiseDir = nodeFn.lift(mkdirp);
+
+var settings;
+var flowsFile;
+var flowsFullPath;
+var flowsPrev;
+var credentialsFile;
+var oldCredentialsFile;
+var userDir;
+var libDir;
+var libFlowsDir;
+var globalSettingsFile;
+
+function listFiles(dir) {
+ var dirs = {};
+ var files = [];
+ var dirCount = 0;
+ return nodeFn.call(fs.readdir, dir).then(function (contents) {
+ contents.sort().forEach(function(fn) {
+ var stats = fs.lstatSync(dir+"/"+fn);
+ if (stats.isDirectory()) {
+ dirCount += 1;
+ dirs[fn] = listFiles(dir+"/"+fn)
+ } else {
+ files.push(fn.split(".")[0]);
+ }
+ })
+ var result = {};
+ if (dirCount > 0) { result.d = keys.all(dirs); }
+ if (files.length > 0) { result.f = when.resolve(files); }
+ return keys.all(result);
+ })
+}
+
+function getFileMeta(root,path) {
+ var fn = fspath.join(root,path);
+ var fd = fs.openSync(fn,"r");
+ var size = fs.fstatSync(fd).size;
+ var meta = {};
+ var read = 0;
+ var length = 10;
+ var remaining = "";
+ var buffer = Buffer(length);
+ while(read < size) {
+ read+=fs.readSync(fd,buffer,0,length);
+ var data = remaining+buffer.toString();
+ var parts = data.split("\n");
+ remaining = parts.splice(-1);
+ for (var i=0;i<parts.length;i+=1) {
+ var match = /^\/\/ (\w+): (.*)/.exec(parts[i]);
+ if (match) {
+ meta[match[1]] = match[2];
+ } else {
+ read = size;
+ break;
+ }
+ }
+ }
+ fs.closeSync(fd);
+ return meta;
+}
+
+function getFileBody(root,path) {
+ var body = "";
+ var fn = fspath.join(root,path);
+ var fd = fs.openSync(fn,"r");
+ var size = fs.fstatSync(fd).size;
+ var scanning = true;
+ var read = 0;
+ var length = 50;
+ var remaining = "";
+ var buffer = Buffer(length);
+ while(read < size) {
+ var thisRead = fs.readSync(fd,buffer,0,length);
+ read += thisRead;
+ if (scanning) {
+ var data = remaining+buffer.slice(0,thisRead).toString();
+ var parts = data.split("\n");
+ remaining = parts.splice(-1)[0];
+ for (var i=0;i<parts.length;i+=1) {
+ if (! /^\/\/ \w+: /.test(parts[i])) {
+ scanning = false;
+ body += parts[i]+"\n";
+ }
+ }
+ if (! /^\/\/ \w+: /.test(remaining)) {
+ scanning = false;
+ }
+ if (!scanning) {
+ body += remaining;
+ }
+ } else {
+ body += buffer.slice(0,thisRead).toString();
+ }
+ }
+ fs.closeSync(fd);
+ return body;
+}
+
+var localfilesystem = {
+ init: function(_settings) {
+ settings = _settings;
+ userDir = settings.userDir || process.env.NODE_RED_HOME;
+
+ if (settings.flowFile) {
+ flowsFile = settings.flowFile;
+ flowsFullPath = flowsFile;
+ } else {
+ flowsFile = 'flows_'+require('os').hostname()+'.json';
+ flowsFullPath = fspath.join(userDir,flowsFile);
+ }
+ var fsext = fspath.extname(flowsFile);
+ credentialsFile = fspath.join(userDir,fspath.basename(flowsFile,fsext)+"_cred"+fsext);
+ oldCredentialsFile = fspath.join(userDir,"credentials.json");
+ flowsPrev = fspath.join(userDir,"flows.backup");
+
+ libDir = fspath.join(userDir,"lib");
+ libFlowsDir = fspath.join(libDir,"flows");
+
+
+ globalSettingsFile = fspath.join(userDir,".config.json");
+
+ return promiseDir(libFlowsDir);
+ },
+
+ getFlows: function() {
+ var defer = when.defer();
+ fs.exists(flowsFullPath, function(exists) {
+ if (exists) {
+ util.log("[red] Loading flows : "+flowsFile);
+ defer.resolve(nodeFn.call(fs.readFile,flowsFullPath,'utf8').then(function(data) {
+ return JSON.parse(data);
+ }));
+ } else {
+ util.log("[red] Flows file not found : "+flowsFile );
+ defer.resolve([]);
+ }
+ });
+ return defer.promise;
+ },
+
+ saveFlows: function(flows) {
+ if (fs.existsSync(flowsFullPath)) {
+ fs.renameSync(flowsFullPath,flowsPrev);
+ }
+
+ var flowData;
+
+ if (settings.flowFilePretty) {
+ flowData = JSON.stringify(flows,null,4);
+ } else {
+ flowData = JSON.stringify(flows);
+ }
+ console.log("Writing to file:" + flowsFullPath);
+ return nodeFn.call(fs.writeFile, flowsFullPath, flowData);
+ },
+
+ getCredentials: function() {
+ var defer = when.defer();
+ fs.exists(credentialsFile, function(exists) {
+ if (exists) {
+ defer.resolve(nodeFn.call(fs.readFile, credentialsFile, 'utf8').then(function(data) {
+ return JSON.parse(data)
+ }));
+ } else {
+ fs.exists(oldCredentialsFile, function(exists) {
+ if (exists) {
+ defer.resolve(nodeFn.call(fs.readFile, oldCredentialsFile, 'utf8').then(function(data) {
+ return JSON.parse(data)
+ }));
+ } else {
+ defer.resolve({});
+ }
+ });
+ }
+ });
+ return defer.promise;
+ },
+
+ saveCredentials: function(credentials) {
+ var credentialData;
+ if (settings.flowFilePretty) {
+ credentialData = JSON.stringify(credentials,null,4);
+ } else {
+ credentialData = JSON.stringify(credentials);
+ }
+
+ return nodeFn.call(fs.writeFile, credentialsFile, credentialData)
+ },
+
+ getSettings: function() {
+ if (fs.existsSync(globalSettingsFile)) {
+ return nodeFn.call(fs.readFile,globalSettingsFile,'utf8').then(function(data) {
+ if (data) {
+ try {
+ return JSON.parse(data);
+ } catch(err) {
+ util.log("[red] Corrupted config detected - resetting");
+ return {};
+ }
+ } else {
+ return {};
+ }
+ });
+ }
+ return when.resolve({});
+ },
+ saveSettings: function(settings) {
+ return nodeFn.call(fs.writeFile,globalSettingsFile,JSON.stringify(settings,null,1),'utf8');
+ },
+
+
+ getAllFlows: function() {
+ return listFiles(libFlowsDir);
+ },
+
+ getFlow: function(fn) {
+ var defer = when.defer();
+ var file = fspath.join(libFlowsDir,fn+".json");
+ fs.exists(file, function(exists) {
+ if (exists) {
+ defer.resolve(nodeFn.call(fs.readFile,file,'utf8'));
+ } else {
+ defer.reject();
+ }
+ });
+ return defer.promise;
+ },
+
+ saveFlow: function(fn,data) {
+ var file = fspath.join(libFlowsDir,fn+".json");
+ return promiseDir(fspath.dirname(file)).then(function () {
+ return nodeFn.call(fs.writeFile, file, data);
+ });
+ },
+
+ getLibraryEntry: function(type,path) {
+ var root = fspath.join(libDir,type);
+ var rootPath = fspath.join(libDir,type,path);
+ return promiseDir(root).then(function () {
+ return nodeFn.call(fs.lstat, rootPath).then(function(stats) {
+ if (stats.isFile()) {
+ return getFileBody(root,path);
+ }
+ if (path.substr(-1) == '/') {
+ path = path.substr(0,path.length-1);
+ }
+ return nodeFn.call(fs.readdir, rootPath).then(function(fns) {
+ var dirs = [];
+ var files = [];
+ fns.sort().filter(function(fn) {
+ var fullPath = fspath.join(path,fn);
+ var absoluteFullPath = fspath.join(root,fullPath);
+ if (fn[0] != ".") {
+ var stats = fs.lstatSync(absoluteFullPath);
+ if (stats.isDirectory()) {
+ dirs.push(fn);
+ } else {
+ var meta = getFileMeta(root,fullPath);
+ meta.fn = fn;
+ files.push(meta);
+ }
+ }
+ });
+ return dirs.concat(files);
+ });
+ });
+ });
+ },
+
+ saveLibraryEntry: function(type,path,meta,body) {
+ var fn = fspath.join(libDir, type, path);
+ var headers = "";
+ for (var i in meta) {
+ if (meta.hasOwnProperty(i)) {
+ headers += "// "+i+": "+meta[i]+"\n";
+ }
+ }
+ return promiseDir(fspath.dirname(fn)).then(function () {
+ nodeFn.call(fs.writeFile, fn, headers+body);
+ });
+ }
+};
+
+module.exports = localfilesystem;
diff --git a/dgbuilder/red/ui.js b/dgbuilder/red/ui.js
new file mode 100644
index 00000000..16580bf1
--- /dev/null
+++ b/dgbuilder/red/ui.js
@@ -0,0 +1,77 @@
+/**
+ * Copyright 2013, 2014 IBM Corp.
+ *
+ * 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 fs = require("fs");
+var events = require("./events");
+var path = require("path");
+
+var icon_paths = [path.resolve(__dirname + '/../public/icons')];
+
+var settings; // settings has to be global, otherwise variable not in scope for express
+
+events.on("node-icon-dir",function(dir) {
+ icon_paths.push(path.resolve(dir));
+});
+
+
+function setupUI(_settings,app) {
+
+ settings = _settings;
+
+ // Need to ensure the url ends with a '/' so the static serving works
+ // with relative paths
+ app.get("/",function(req,res) {
+ if (req.originalUrl.slice(-1) != "/") {
+ res.redirect(req.originalUrl+"/");
+ } else {
+ req.next();
+ }
+ });
+
+ var iconCache = {};
+ //TODO: create a default icon
+ var defaultIcon = path.resolve(__dirname + '/../public/icons/arrow-in.png');
+
+ app.get("/icons/:icon",function(req,res) {
+ if (iconCache[req.params.icon]) {
+ res.sendfile(iconCache[req.params.icon]); // if not found, express prints this to the console and serves 404
+ } else {
+ for (var p=0;p<icon_paths.length;p++) {
+ var iconPath = path.join(icon_paths[p],req.params.icon);
+ if (fs.existsSync(iconPath)) {
+ res.sendfile(iconPath);
+ iconCache[req.params.icon] = iconPath;
+ return;
+ }
+ }
+ res.sendfile(defaultIcon);
+ }
+ });
+
+ app.get("/settings", function(req,res) {
+ var safeSettings = {
+ httpNodeRoot: settings.httpNodeRoot,
+ version: settings.version
+ };
+ res.json(safeSettings);
+ });
+
+ app.use("/",express.static(__dirname + '/../public'));
+
+ return app;
+}
+
+module.exports = setupUI;
diff --git a/dgbuilder/red/util.js b/dgbuilder/red/util.js
new file mode 100644
index 00000000..7ca72b0e
--- /dev/null
+++ b/dgbuilder/red/util.js
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2014 IBM Corp.
+ *
+ * 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 ensureString(o) {
+ if (Buffer.isBuffer(o)) {
+ return o.toString();
+ } else if (typeof o === "object") {
+ return JSON.stringify(o);
+ } else if (typeof o === "string") {
+ return o;
+ }
+ return ""+o;
+}
+
+function ensureBuffer(o) {
+ if (Buffer.isBuffer(o)) {
+ return o;
+ } else if (typeof o === "object") {
+ o = JSON.stringify(o);
+ } else if (typeof o !== "string") {
+ o = ""+o;
+ }
+ return new Buffer(o);
+}
+
+module.exports = {
+ ensureString: ensureString,
+ ensureBuffer: ensureBuffer,
+};
+