From 7ffcdd543c84fe1b0c60ec1bf20c8cdaee570377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sun, 10 May 2015 21:19:35 +0200 Subject: [PATCH] Added new automatic rpc call generation for all available calls. Needs more work because is async and needs to play well with angular. --- luciexpress/htdocs/index.html | 2 +- luciexpress/htdocs/js/app.js | 62 ++++--- luciexpress/htdocs/js/rpc.js | 166 ++++++++++++------ luciexpress/htdocs/js/session.js | 7 +- luciexpress/htdocs/js/uci.js | 1 - .../plugins/core/widgets/luci.footer.js | 4 +- .../htdocs/plugins/core/widgets/luci.login.js | 18 +- .../plugins/core/widgets/luci.top_bar.js | 2 +- luciexpress/server.js | 55 ++++-- 9 files changed, 207 insertions(+), 110 deletions(-) diff --git a/luciexpress/htdocs/index.html b/luciexpress/htdocs/index.html index 42fb9573b..34fe29352 100644 --- a/luciexpress/htdocs/index.html +++ b/luciexpress/htdocs/index.html @@ -1,5 +1,5 @@ - + LuCi Express diff --git a/luciexpress/htdocs/js/app.js b/luciexpress/htdocs/js/app.js index ac5f55f4b..174622aea 100644 --- a/luciexpress/htdocs/js/app.js +++ b/luciexpress/htdocs/js/app.js @@ -54,6 +54,7 @@ require([ angular.bootstrap(document, ['luci']); }); */ + angular.module("luci", [ "ui.bootstrap", "ui.router", @@ -63,9 +64,9 @@ angular.module("luci", [ "ngAnimate", "gettext", "checklist-model" - ]); - -angular.module("luci") +]); + + angular.module("luci") .config(function ($stateProvider, $locationProvider, $compileProvider, $urlRouterProvider, $controllerProvider, $provide) { //$locationProvider.otherwise({ redirectTo: "/" }); $locationProvider.hashPrefix('!'); @@ -83,7 +84,7 @@ angular.module("luci") console.log(JSON.stringify($delegate[0])); return $delegate; });*/ - + $juci.$urlRouterProvider = $urlRouterProvider; $juci.redirect = function(page){ window.location.href = "#!"+page; @@ -121,7 +122,7 @@ angular.module("luci") luci_config: {} }); }) - .run(function($rootScope, $state, $session, gettextCatalog, $rpc, $uci, $config, $location, $navigation){ + .run(function($rootScope, $state, $session, gettextCatalog, $rpc, $config, $location, $navigation){ $rootScope.config = $config; //window.rpc = $rpc; //window.uci = $uci; @@ -130,8 +131,8 @@ angular.module("luci") gettextCatalog.currentLanguage = "en"; gettextCatalog.debug = true; /*$rootScope.$on('$routeChangeSuccess', function (event, current, previous) { - $rootScope.title = current.$$route.title; - });*/ + $rootScope.title = current.$$route.title; + });*/ var path = $location.path().replace(/\//g, "").replace(/\./g, "_"); $config.system = {}; @@ -156,26 +157,35 @@ angular.module("luci") //$state.go("login"); }); }) + + .directive("luciFooter", function(){ return {} }) + .directive("luciLayoutNaked", function(){ return {} }) + .directive("luciLayoutSingleColumn", function(){ return {} }) + .directive("luciLayoutWithSidebar", function(){ return {} }) + .directive("luciNav", function(){ return {} }) + .directive("luciNavbar", function(){ return {} }) + .directive("luciTopBar", function(){ return {} }) + .directive('ngOnload', [function(){ + return { + scope: { + callBack: '&ngOnload' + }, + link: function(scope, element, attrs){ + element.on('load', function(){ + return scope.callBack(); + }) + } + }}]); -.directive("luciFooter", function(){ return {} }) -.directive("luciLayoutNaked", function(){ return {} }) -.directive("luciLayoutSingleColumn", function(){ return {} }) -.directive("luciLayoutWithSidebar", function(){ return {} }) -.directive("luciNav", function(){ return {} }) -.directive("luciNavbar", function(){ return {} }) -.directive("luciTopBar", function(){ return {} }) -.directive('ngOnload', [function(){ - return { - scope: { - callBack: '&ngOnload' - }, - link: function(scope, element, attrs){ - element.on('load', function(){ - return scope.callBack(); - }) - } -}}]) +angular.element(document).ready(function() { + window.rpc.$init().done(function(){ + console.log("RPC INIT DONE!"); + angular.bootstrap(document, ["luci"]); + }); +}); + +/* angular.module("luci") .factory("$hosts", function($rpc, $uci){ var hosts = {}; @@ -266,4 +276,4 @@ angular.module("luci") $(document).ready(function(){ $("#loading-indicator").hide(); -}); +}); */ diff --git a/luciexpress/htdocs/js/rpc.js b/luciexpress/htdocs/js/rpc.js index c408dd5da..d208c8b7f 100644 --- a/luciexpress/htdocs/js/rpc.js +++ b/luciexpress/htdocs/js/rpc.js @@ -1,10 +1,69 @@ //! Author: Martin K. Schröder -// luci rpc module for communicating with the server -angular.module("luci") -.factory('$rpc', function($rootScope, $config, gettext){ +(function(){ + var RPC_HOST = ""; //(($config.rpc.host)?$config.rpc.host:"") + var RPC_SESSION_ID = "00000000000000000000000000000000"; + var gettext = function(text){ return text; } + function rpc_request(type, namespace, method, data){ + var sid = ""; + var deferred = $.Deferred(); + // setup default rpcs + $.jsonRPC.withOptions({ + namespace: "", + endPoint: RPC_HOST+"/ubus" + }, function(){ + //var sid = "00000000000000000000000000000000"; + //if($rootScope.sid) sid = $rootScope.sid; + //data.ubus_rpc_session = sid; + this.request(type, { + params: [ RPC_SESSION_ID, namespace, method, data], + success: function(result){ + //alert("SID: "+sid + " :: "+ JSON.stringify(result)); + if(type == "call" && result && result.result) { + // TODO: modify all rpc UCI services so that they ALWAYS return at least + // an empty json object. Otherwise we have no way to differentiate success + // from failure of a request. This has to be done on the host side. + if(result.result[0] != 0){ // || result.result[1] == undefined) { + console.log("RPC succeeded, but returned error: "+JSON.stringify(result)); + deferred.reject((function(){ + switch(result.result[0]){ + case 0: return gettext("Parse error"); + case 1: return gettext("Invalid request"); + case 2: return gettext("Invalid parameters"); + case 3: return gettext("Internal error"); + case 4: return gettext("Object not found"); + case 5: return gettext("Session not found"); + case 6: return gettext("Access denied"); + case 7: return gettext("Timed out"); + default: return gettext("RPC error #")+result.result[0]+": "+result.result[1]; + } + })()); + } else { + deferred.resolve(result.result[1]); + } + } else if(type == "list" && result && result.result){ + deferred.resolve(result.result); + } else { + deferred.reject(); + } + }, + error: function(result){ + console.error("RPC error ("+namespace+"."+method+"): "+JSON.stringify(result)); + if(result && result.error){ + deferred.reject(result.error); + //$rootScope.$broadcast("error", result.error.message); + } + } + }) + }); + return deferred.promise(); + } var rpc = window.rpc = { - register_method: function(call){ + $sid: function(sid){ + if(sid) RPC_SESSION_ID = sid; + else return RPC_SESSION_ID; + }, + $register: function(call){ //console.log("registering: "+call); var self = this; function _find(path, obj){ @@ -17,53 +76,8 @@ angular.module("luci") (function(namespace, method){ // create the rpc method obj[path[0]] = function(data){ - var func = (function(data){ - if(!data) data = { }; - var deferred = $.Deferred(); - $.jsonRPC.withOptions({ - namespace: "", - endPoint: (($config.rpc.host)?$config.rpc.host:"")+"/ubus" - }, function(){ - var sid = "00000000000000000000000000000000"; - if($rootScope.sid) sid = $rootScope.sid; - //data.ubus_rpc_session = sid; - this.request('call', { - params: [ sid, namespace, method, data], - success: function(result){ - //alert("SID: "+sid + " :: "+ JSON.stringify(result)); - if(result && result.result) { - // TODO: modify all rpc UCI services so that they ALWAYS return at least - // an empty json object. Otherwise we have no way to differentiate success - // from failure of a request. This has to be done on the host side. - if(result.result[0] != 0){ // || result.result[1] == undefined) { - console.log("RPC succeeded, but returned error: "+JSON.stringify(result)); - deferred.reject((function(){ - switch(result.result[0]){ - case 6: return gettext("Access denied!"); - default: return gettext("RPC error #")+result.result[0]+": "+result.result[1]; - } - })()); - } else { - deferred.resolve(result.result[1]); - } - } else { - deferred.reject(); - } - }, - error: function(result){ - console.error("RPC error ("+namespace+"."+method+"): "+JSON.stringify(result)); - if(result && result.error){ - deferred.reject(result.error); - $rootScope.$broadcast("error", result.error.message); - } else { - deferred.reject("RPC error ("+namespace+"."+method+"): "+JSON.stringify(result)); - } - } - }) - }); - return deferred.promise(); - }); - return func(data); + if(!data) data = { }; + return rpc_request("call", namespace, method, data); } })(namespace, path[0]); } else { @@ -73,13 +87,49 @@ angular.module("luci") } } _find(call.split("."), self); + }, + $init: function(){ + var self = this; + var deferred = $.Deferred(); + // request list of all methods and construct rpc object containing all of the methods in javascript. + rpc_request("list", "*", "", {}).done(function(result){ + //console.log("RESULT: "+JSON.stringify(result)); + // TODO: make this less obscure of a method :) + function _processNode(obj, cur_path){ + var is_leaf = true; + var leafs = {}; + Object.keys(obj).map(function(x){ + if((typeof obj[x]) == "object") { + leafs[x] = obj[x]; + is_leaf = false; + } else { + + } + }); + if(is_leaf){ + // add a new rpc call + //console.log("Leaf: "+namespace+", "+method); + self.$register(cur_path); + } else { + //console.log("Processing node: "+cur_path); + Object.keys(leafs).map(function(x){ + var path = ((cur_path)?(cur_path+"."):"")+x; + //var namespace = parent[x] = {}; + _processNode(leafs[x], path); + }); + } + } + _processNode(result, null); + deferred.resolve(); + }); + return deferred.promise(); } }; - // setup default rpcs - if($config.rpc && $config.rpc.exposed_calls){ - $config.rpc.exposed_calls.forEach(function(call){ - rpc.register_method(call); - }); - } - return rpc; +})(); + +// luci rpc module for communicating with the server +angular.module("luci") +.factory('$rpc', function($rootScope, $config, gettext){ + + return window.rpc; }); diff --git a/luciexpress/htdocs/js/session.js b/luciexpress/htdocs/js/session.js index 9f0db2dac..4a41c643a 100644 --- a/luciexpress/htdocs/js/session.js +++ b/luciexpress/htdocs/js/session.js @@ -6,13 +6,13 @@ angular.module("luci") var saved_sid = $localStorage.getItem("sid"); var default_sid = "00000000000000000000000000000000"; if(saved_sid){ - $rootScope.sid = saved_sid; + window.rpc.$sid(saved_sid); } function setupUbusRPC(acls){ Object.keys(acls).map(function(key){ acls[key].map(function(func){ - $rpc.register_method(key+"."+func); + //$rpc.register_method(key+"."+func); }); }); } @@ -53,7 +53,8 @@ angular.module("luci") "username": obj.username, "password": obj.password }).done(function(result){ - $rootScope.sid = self.sid = result.ubus_rpc_session; + self.sid = result.ubus_rpc_session; + window.rpc.$sid(self.sid); self.data = result; $localStorage.setItem("sid", self.sid); if(result && result.acls && result.acls.ubus) setupUbusRPC(result.acls.ubus); diff --git a/luciexpress/htdocs/js/uci.js b/luciexpress/htdocs/js/uci.js index 2e17a8bde..2fd45bcd7 100644 --- a/luciexpress/htdocs/js/uci.js +++ b/luciexpress/htdocs/js/uci.js @@ -226,7 +226,6 @@ angular.module("luci") UCI.Field = UCIField; })(); (function(){ - function UCISection(config){ this[".config"] = config; } diff --git a/luciexpress/htdocs/plugins/core/widgets/luci.footer.js b/luciexpress/htdocs/plugins/core/widgets/luci.footer.js index a4a6ee6e6..960f5d8c3 100644 --- a/luciexpress/htdocs/plugins/core/widgets/luci.footer.js +++ b/luciexpress/htdocs/plugins/core/widgets/luci.footer.js @@ -29,7 +29,7 @@ $juci.module("core") controller: "luciFooterController" }; }) -.controller("luciFooterController", function($scope, $rpc, $config, $languages, gettextCatalog, gettext, $tr, $status){ +.controller("luciFooterController", function($scope, $rpc, $config, $languages, gettextCatalog, gettext, $tr){ // TODO: move this into a higher level controller maybe? $scope.languages = $languages.getLanguages(); $scope.isActiveLanguage = function(lang){ @@ -42,7 +42,7 @@ $juci.module("core") $rpc.network.interface.dump().done(function(result){ if(result && result.interface) { result.interface.map(function(i){ - if(i.interface == "wan" && i["ipv4-address"].length){ + if(i.interface == "wan" && i["ipv4-address"] && i["ipv4-address"].length){ $scope.wanip = i["ipv4-address"][0].address; } }); diff --git a/luciexpress/htdocs/plugins/core/widgets/luci.login.js b/luciexpress/htdocs/plugins/core/widgets/luci.login.js index b3e97984b..4075dbe8d 100644 --- a/luciexpress/htdocs/plugins/core/widgets/luci.login.js +++ b/luciexpress/htdocs/plugins/core/widgets/luci.login.js @@ -44,15 +44,17 @@ $juci.module("core") $scope.loggedIn = $session.isLoggedIn(); $scope.errors = []; $scope.showHost = 0; - $rpc.local.features().done(function(features){ - if(features.list) features.list.map(function(x){ - if(x.indexOf("rpcforward") == 0) { - $scope.showHost = 1; - $scope.form.host = $localStorage.getItem("rpc_host")||""; - } + if($rpc.local){ + $rpc.local.features().done(function(features){ + if(features.list) features.list.map(function(x){ + if(x.indexOf("rpcforward") == 0) { + $scope.showHost = 1; + $scope.form.host = $localStorage.getItem("rpc_host")||""; + } + }); + $scope.$apply(); }); - $scope.$apply(); - }); + } $scope.doLogin = function(){ $scope.errors = []; async.series([ diff --git a/luciexpress/htdocs/plugins/core/widgets/luci.top_bar.js b/luciexpress/htdocs/plugins/core/widgets/luci.top_bar.js index 0eb007bed..0f46fd771 100644 --- a/luciexpress/htdocs/plugins/core/widgets/luci.top_bar.js +++ b/luciexpress/htdocs/plugins/core/widgets/luci.top_bar.js @@ -7,7 +7,7 @@ $juci.module("core") replace: true }; }) -.controller("luciTopBarController", function($scope, $config, $session, $uci, $window, $localStorage, $state, gettext){ +.controller("luciTopBarController", function($scope, $config, $session, $uci, $rpc, $window, $localStorage, $state, gettext){ $uci.sync("system").done(function(){ var system = $uci.system["@system"]; if(system && system.length && system[0].displayname.value){ diff --git a/luciexpress/server.js b/luciexpress/server.js index b41f13a4f..71dcefd10 100644 --- a/luciexpress/server.js +++ b/luciexpress/server.js @@ -82,7 +82,24 @@ app.post('/ubus', function(req, res) { }); } else { console.log("JSON_CALL (-> "+config.ubus_uri+"): "+JSON.stringify(data)); - + + function sendResponse(body){ + var json = JSON.stringify(body); + console.log("JSON_RESP: "+json); + res.write(json); + res.end(); + } + + var timedOut = false; + var timeout = setTimeout(function(){ + var body = { + jsonrpc: "2.0", + result: [1, "ETIMEOUT"] + }; + timedOut = true; + sendResponse(body); + }, 10000); + request({ url: config.ubus_uri, method: "POST", @@ -91,16 +108,16 @@ app.post('/ubus', function(req, res) { }, function (error, response, body) { if(error){ console.log("ERROR: "+error); - body = JSON.stringify({ + body = { jsonrpc: "2.0", result: [1, String(error)] - }); + }; //doLocalRPC(); } - var json = JSON.stringify(body); - console.log("JSON_RESP: "+json); - res.write(json); - res.end(); + clearTimeout(timeout); + if(!timedOut){ + sendResponse(body); + } }); //console.log("Unknown RPC call "+name); //res.end(); @@ -114,11 +131,29 @@ app.post('/ubus', function(req, res) { }); var server = app.listen(3000, function () { - var host = server.address().address; var port = server.address().port; - console.log('Example app listening at http://%s:%s', host, port); - + for(var i = 0; i < process.argv.length; i++){ + switch(process.argv[i]){ + case "-p": { + var paths = process.argv[++i].split(";"); + paths.map(function(k){ + var url, path; + if(k.indexOf(":") >= 0){ + var parts = k.split(":"); + path = parts[1]; + url = parts[0]; + } else { + url = k.split("/").pop(); + path = k; + } + console.log("Adding extra plugin search path: "+path+" at "+url); + app.use(url, express.static(path + '/')); + }); + } break; + } + } + console.log('Local server listening on http://%s:%s', host, port); });