Added new automatic rpc call generation for all available calls. Needs more work because is async and needs to play well with angular.

This commit is contained in:
Martin Schröder 2015-05-10 21:19:35 +02:00 committed by Martin Schröder
parent d18a807cd7
commit 7ffcdd543c
9 changed files with 207 additions and 110 deletions

View file

@ -1,5 +1,5 @@
<!doctype html>
<html lang="en" ng-app="luci">
<html lang="en" >
<head>
<meta charset="utf-8">
<title >LuCi Express</title><!--ng-bind="title"-->

View file

@ -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();
});
}); */

View file

@ -1,10 +1,69 @@
//! Author: Martin K. Schröder <mkschreder.uk@gmail.com>
// 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;
});

View file

@ -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);

View file

@ -226,7 +226,6 @@ angular.module("luci")
UCI.Field = UCIField;
})();
(function(){
function UCISection(config){
this[".config"] = config;
}

View file

@ -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;
}
});

View file

@ -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([

View file

@ -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){

View file

@ -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);
});