diff --git a/luciexpress/htdocs/css/app.css b/luciexpress/htdocs/css/app.css
index e8cf3de92..a67a1bb94 100644
--- a/luciexpress/htdocs/css/app.css
+++ b/luciexpress/htdocs/css/app.css
@@ -1 +1,6 @@
.nav, .pagination, .carousel, .panel-title a { cursor: pointer; }
+
+/* needed for modals */
+.modal-backdrop {
+ z-index: 0;
+}
diff --git a/luciexpress/htdocs/index.html b/luciexpress/htdocs/index.html
index 1389c2a0f..ff4991075 100644
--- a/luciexpress/htdocs/index.html
+++ b/luciexpress/htdocs/index.html
@@ -36,6 +36,7 @@
+
diff --git a/luciexpress/htdocs/js/app.js b/luciexpress/htdocs/js/app.js
index cefec0933..000886ff1 100644
--- a/luciexpress/htdocs/js/app.js
+++ b/luciexpress/htdocs/js/app.js
@@ -78,6 +78,7 @@ angular.module("luci", [
"ui.bootstrap",
"ui.router",
'ui.select',
+ 'angularModalService',
"uiSwitch",
"ngAnimate",
"gettext"
diff --git a/luciexpress/htdocs/js/config.js b/luciexpress/htdocs/js/config.js
index 10dea2ea1..517c686a8 100644
--- a/luciexpress/htdocs/js/config.js
+++ b/luciexpress/htdocs/js/config.js
@@ -61,8 +61,11 @@ angular.module("luci")
"luci2.ui.menu",
"uci.state",
"uci.set",
+ "uci.add",
"uci.delete",
"uci.commit",
+ "uci.rollback",
+ "uci.revert",
"uci.configs"
// the rest is automatically retreived from session now!
/*"session.destroy",
diff --git a/luciexpress/htdocs/js/rpc.js b/luciexpress/htdocs/js/rpc.js
index 76ce0dbd2..39b28c223 100644
--- a/luciexpress/htdocs/js/rpc.js
+++ b/luciexpress/htdocs/js/rpc.js
@@ -38,7 +38,7 @@ angular.module("luci")
// create the rpc method
obj[path[0]] = function(data){
var func = (function(data){
- if(!data) data = {};
+ if(!data) data = { };
var deferred = $.Deferred();
$.jsonRPC.withOptions({
namespace: "",
@@ -46,7 +46,7 @@ angular.module("luci")
}, 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){
diff --git a/luciexpress/htdocs/js/uci.js b/luciexpress/htdocs/js/uci.js
index 49a08cefc..90b0bf859 100644
--- a/luciexpress/htdocs/js/uci.js
+++ b/luciexpress/htdocs/js/uci.js
@@ -22,7 +22,7 @@
// uci module for interacting with uci tables
angular.module("luci")
-.factory('$uci', function($rpc){
+.factory('$uci', function($rpc, $rootScope){
function initReq(path){
var parts = path.split(".");
var req = {};
@@ -50,11 +50,64 @@ angular.module("luci")
var req = initReq(path);
req.values = values;
$rpc.uci.set(req).done(function(state){
- $rpc.uci.commit(req).done(function(){
- deferred.resolve();
- }).fail(function(){
- deferred.reject();
- });
+ deferred.resolve();
+ }).fail(function(){
+ deferred.reject();
+ });
+ return deferred.promise();
+ },
+ // add a new rule to the uci config
+ add: function(config, type, values){
+ var deferred = $.Deferred();
+ $rpc.uci.add({
+ "config": config,
+ "type": type,
+ "values": values
+ }).done(function(state){
+ values[".name"] = state.section;
+ deferred.resolve(state.section);
+ }).fail(function(){
+ deferred.reject();
+ });
+ return deferred.promise();
+ },
+ // commit config changes
+ commit: function(path, values){
+ var deferred = $.Deferred();
+ var req = initReq(path);
+ $rpc.uci.commit(req).done(function(state){
+ deferred.resolve();
+ }).fail(function(){
+ deferred.reject();
+ });
+ return deferred.promise();
+ },
+ // revert uncommitted changes
+ revert: function(path, values){
+ var deferred = $.Deferred();
+ var req = initReq(path);
+ $rpc.uci.revert(req).done(function(state){
+ deferred.resolve();
+ }).fail(function(){
+ deferred.reject();
+ });
+ return deferred.promise();
+ },
+ // rollback
+ rollback: function(){
+ var deferred = $.Deferred();
+ $rpc.uci.rollback().done(function(state){
+ deferred.resolve();
+ }).fail(function(){
+ deferred.reject();
+ });
+ return deferred.promise();
+ },
+ delete: function(path){
+ var deferred = $.Deferred();
+ var req = initReq(path);
+ $rpc.uci.delete(req).done(function(state){
+ deferred.resolve();
}).fail(function(){
deferred.reject();
});
diff --git a/luciexpress/htdocs/lib/js/angular-modal-service.min.js b/luciexpress/htdocs/lib/js/angular-modal-service.min.js
new file mode 100644
index 000000000..4b2d4b0e6
--- /dev/null
+++ b/luciexpress/htdocs/lib/js/angular-modal-service.min.js
@@ -0,0 +1,3 @@
+/*angular-modal-service v0.6.6 - https://github.com/dwmkerr/angular-modal-service */
+!function(){"use strict";var e=angular.module("angularModalService",[]);e.factory("ModalService",["$document","$compile","$controller","$http","$rootScope","$q","$templateCache",function(e,n,l,t,o,r,a){function c(){var e=this,c=function(e,n){var l=r.defer();if(e)l.resolve(e);else if(n){var o=a.get(n);void 0!==o?l.resolve(o):t({method:"GET",url:n,cache:!0}).then(function(e){a.put(n,e.data),l.resolve(e.data)},function(e){l.reject(e)})}else l.reject("No template or templateUrl has been specified.");return l.promise};e.showModal=function(e){var t=r.defer(),a=e.controller;return a?(e.controllerAs&&(a=a+" as "+e.controllerAs),c(e.template,e.templateUrl).then(function(c){var u=o.$new(),s=r.defer(),p={$scope:u,close:function(e,n){(void 0===n||null===n)&&(n=0),window.setTimeout(function(){s.resolve(e),u.$destroy(),m.remove(),p.close=null,t=null,s=null,$=null,p=null,m=null,u=null},n)}};if(e.inputs)for(var d in e.inputs)p[d]=e.inputs[d];var f=angular.element(c),v=n(f),m=v(u);p.$element=m;var h=l(a,p);e.appendElement?e.appendElement.append(m):i.append(m);var $={controller:h,scope:u,element:m,close:s.promise};t.resolve($)}).then(null,function(e){t.reject(e)}),t.promise):(t.reject("No controller has been specified."),t.promise)}}var i=e.find("body");return new c}])}();
+//# sourceMappingURL=angular-modal-service.min.js.map
\ No newline at end of file
diff --git a/luciexpress/htdocs/plugins/core/plugin.json b/luciexpress/htdocs/plugins/core/plugin.json
index 798994ac7..ccc313411 100644
--- a/luciexpress/htdocs/plugins/core/plugin.json
+++ b/luciexpress/htdocs/plugins/core/plugin.json
@@ -11,7 +11,9 @@
"widgets/luci.progress",
"widgets/luci.table",
"widgets/luci.top_bar",
- "widgets/uci.wireless.interface"
+ "widgets/uci.wireless.interface",
+ "widgets/uci.firewall.nat.rule.edit",
+ "widgets/core.modal"
],
"pages": {
"overview": {
diff --git a/luciexpress/htdocs/plugins/core/widgets/core.modal.html b/luciexpress/htdocs/plugins/core/widgets/core.modal.html
new file mode 100644
index 000000000..787926d9d
--- /dev/null
+++ b/luciexpress/htdocs/plugins/core/widgets/core.modal.html
@@ -0,0 +1,15 @@
+
diff --git a/luciexpress/htdocs/plugins/core/widgets/core.modal.js b/luciexpress/htdocs/plugins/core/widgets/core.modal.js
new file mode 100644
index 000000000..52d8db093
--- /dev/null
+++ b/luciexpress/htdocs/plugins/core/widgets/core.modal.js
@@ -0,0 +1,59 @@
+$juci.module("core")
+.directive('modal', function () {
+ var plugin_root = $juci.module("core").plugin_root;
+ return {
+ templateUrl: plugin_root + "/widgets/core.modal.html",
+ restrict: 'E',
+ transclude: true,
+ replace:true,
+ scope: {
+ acceptLabel: "@acceptLabel",
+ dismissLabel: "@dismissLabel",
+ onAccept: "&onAccept",
+ onDismiss: "&onDismiss",
+ ngShow: "=ngShow",
+ title: "=title"
+ },
+ controller: "ModalController",
+ link: function postLink(scope, element, attrs) {
+ //scope.title = attrs.title;
+ //scope.acceptLabel = attrs.acceptLabel;
+ //scope.dismissLabel = attrs.dismissLabel;
+ scope.element = element;
+ scope.$watch("ngShow", function(value){
+ if(value == true)
+ $(element).modal('show');
+ else
+ $(element).modal('hide');
+ });
+
+ $(element).on('shown.bs.modal', function(){
+ scope.$apply(function(){
+ scope.$parent[attrs.ngShow] = true;
+ });
+ });
+
+ $(element).on('hidden.bs.modal', function(){
+ scope.$apply(function(){
+ scope.$parent[attrs.ngShow] = false;
+ });
+ });
+ }
+ };
+})
+.controller('ModalController', function($scope) {
+ /*$scope.onDismiss = function(){
+ console.log("Modal Dismissed");
+ }
+ $scope.onAccept = function(){
+ console.log("Modal accepted!");
+ }*/
+ /*console.log("controller: "+$scope.ngShow);
+ $scope.$watch("ngShow", function(value){
+ console.log("visible: "+$scope.ngShow);
+ if(value == true)
+ $($scope.element).modal('show');
+ else
+ $($scope.element).modal('hide');
+ });*/
+});
diff --git a/luciexpress/htdocs/plugins/core/widgets/uci.firewall.nat.rule.edit.html b/luciexpress/htdocs/plugins/core/widgets/uci.firewall.nat.rule.edit.html
new file mode 100644
index 000000000..86f429551
--- /dev/null
+++ b/luciexpress/htdocs/plugins/core/widgets/uci.firewall.nat.rule.edit.html
@@ -0,0 +1,63 @@
+
diff --git a/luciexpress/htdocs/plugins/core/widgets/uci.firewall.nat.rule.edit.js b/luciexpress/htdocs/plugins/core/widgets/uci.firewall.nat.rule.edit.js
new file mode 100644
index 000000000..af67c84eb
--- /dev/null
+++ b/luciexpress/htdocs/plugins/core/widgets/uci.firewall.nat.rule.edit.js
@@ -0,0 +1,33 @@
+$juci.module("core")
+.directive("uciFirewallNatRuleEdit", function($compile, $parse){
+ var plugin_root = $juci.module("core").plugin_root;
+ return {
+ templateUrl: plugin_root+"/widgets/uci.firewall.nat.rule.edit.html",
+ scope: {
+ rule: "=ngModel"
+ },
+ controller: "uciFirewallNatRuleEdit",
+ replace: true,
+ require: "^ngModel"
+ };
+}).controller("uciFirewallNatRuleEdit", function($scope, $uci, $rpc){
+ $scope.portIsRange = 1;
+ $scope.protocols = ["udp", "tcp"];
+ $scope.patterns = {
+ ipaddress: /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/,
+ port: /^\d{1,5}$/
+ }
+ $rpc.router.clients().done(function(clients){
+ //alert(JSON.stringify(Object.keys(clients).map(function(x) { return clients[x]; })));
+ $scope.clients = [];
+ $scope.devices = {};
+ Object.keys(clients).map(function(x) {
+ var c = clients[x];
+ if(c.connected){
+ $scope.devices[c.ipaddr] = c;
+ $scope.clients.push(c.ipaddr);
+ }
+ });
+ $scope.$apply();
+ });
+});
diff --git a/luciexpress/htdocs/plugins/router/pages/internet.exposed_host.js b/luciexpress/htdocs/plugins/router/pages/internet.exposed_host.js
index fabb84903..deb04dcb4 100644
--- a/luciexpress/htdocs/plugins/router/pages/internet.exposed_host.js
+++ b/luciexpress/htdocs/plugins/router/pages/internet.exposed_host.js
@@ -27,8 +27,10 @@ $juci.module("router")
};
$scope.onSave = function(){
$uci.set("firewall.dmz", $scope.dmz).done(function(){
- $scope.info_message = $tr("STR_SETTINGSSAVED");
- $scope.$apply();
+ $uci.commit("firewall.dmz").done(function(){
+ $scope.info_message = $tr("STR_SETTINGSSAVED");
+ $scope.$apply();
+ });
}).fail(function(){
$scope.error_message = $tr("STR_SETTINGSSAVEFAILED");
});
diff --git a/luciexpress/htdocs/plugins/router/pages/internet.firewall.html b/luciexpress/htdocs/plugins/router/pages/internet.firewall.html
index 192fb87c6..0ad5fefd7 100644
--- a/luciexpress/htdocs/plugins/router/pages/internet.firewall.html
+++ b/luciexpress/htdocs/plugins/router/pages/internet.firewall.html
@@ -6,13 +6,13 @@
|
Firewall
-
+
|
|
Allow Ping to WAN interface
-
+
|
diff --git a/luciexpress/htdocs/plugins/router/pages/internet.firewall.js b/luciexpress/htdocs/plugins/router/pages/internet.firewall.js
index 08a7a7eec..ca5ea4c7a 100644
--- a/luciexpress/htdocs/plugins/router/pages/internet.firewall.js
+++ b/luciexpress/htdocs/plugins/router/pages/internet.firewall.js
@@ -1,5 +1,16 @@
$juci.module("router")
-.controller("InternetFirewallPageCtrl", function($scope){
- $scope.firewallEnabled = 1;
- $scope.allowWANPing = 1;
+.controller("InternetFirewallPageCtrl", function($scope, $uci){
+
+ $uci.show("firewall.settings").done(function(settings){
+ $scope.settings = settings;
+ settings.firewall = Number(settings.firewall);
+ settings.ping_wan = Number(settings.ping_wan);
+
+ $scope.onSave = function(){
+ $uci.set("firewall.settings", $scope.settings).done(function(){
+
+ });
+ }
+ $scope.$apply();
+ });
});
diff --git a/luciexpress/htdocs/plugins/router/pages/internet.port_mapping.html b/luciexpress/htdocs/plugins/router/pages/internet.port_mapping.html
index 24438ecf8..79f2571ef 100644
--- a/luciexpress/htdocs/plugins/router/pages/internet.port_mapping.html
+++ b/luciexpress/htdocs/plugins/router/pages/internet.port_mapping.html
@@ -1,5 +1,34 @@
- Port mapping settings
+
Port Mapping
+
Port mapping allows remote computers to connect to a specific device within your private network.
+
+ | Local IP | Protocol | Local port | Public Port | | |
+
+ | {{ r.dest_ip }} |
+ {{ r.proto }} |
+ {{ r.dest_port }} |
+ {{ r.src_dport }} |
+ |
+ |
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+
diff --git a/luciexpress/htdocs/plugins/router/pages/internet.port_mapping.js b/luciexpress/htdocs/plugins/router/pages/internet.port_mapping.js
index c6061dfdb..79986ec1c 100644
--- a/luciexpress/htdocs/plugins/router/pages/internet.port_mapping.js
+++ b/luciexpress/htdocs/plugins/router/pages/internet.port_mapping.js
@@ -1,4 +1,72 @@
$juci.module("router")
-.controller("InternetPortMappingPageCtrl", function($scope){
+.controller("InternetPortMappingPageCtrl", function($scope, $uci, ModalService, $rpc){
+ function reload(){
+ $uci.show("firewall").done(function(firewall){
+ $scope.redirects = Object.keys(firewall).filter(function(x) { return firewall[x][".type"] == "redirect"; }).map(function(x){ return firewall[x]; });
+
+ $scope.$apply();
+ });
+
+
+ } reload();
+ $scope.showModal = 0;
+ $scope.onAcceptModal = function(){
+ var rule = $scope.rule;
+ Object.keys($scope.redirects).map(function(idx) { var x = $scope.redirects[idx]; if(x[".name"] == rule[".name"]) $scope.redirects[idx] = rule; });
+ console.log(JSON.stringify(rule));
+ if(!rule[".name"]){
+ // set up the rule in uci
+ $uci.add("firewall", "redirect", rule).done(function(x){
+ console.log("Added new section: "+x);
+ if(x.section) rule[".name"] = x.section;
+ });
+ } else {
+ $uci.set("firewall."+rule[".name"], rule).always(function(firewall){
+
+ });
+ }
+ $scope.showModal = 0;
+ }
+ $scope.onDismissModal = function(){
+ $scope.showModal = 0;
+ }
+ $scope.onAddRule = function(){
+ $scope.rule = {};
+ $scope.redirects.push($scope.rule);
+ $scope.showModal = 1;
+ }
+ $scope.onEditRule = function(rule){
+ $scope.rule = Object.create(rule);
+ $scope.modalTitle = "Edit port mapping ("+(rule['.name'] || 'new')+")";
+ $scope.showModal = 1;
+ }
+ $scope.onDeleteRule = function(rule){
+ function removeFromList(){
+ if($scope.redirects) {
+ $scope.redirects = $scope.redirects.filter(function(x){ return x !== rule; });
+ $scope.$apply();
+ }
+ }
+ console.log("Deleting rule: "+rule[".name"]);
+ if(rule[".name"]){
+ $uci.delete("firewall."+rule[".name"]).always(function(){
+ removeFromList();
+ });
+ } else {
+ retmoveFromList();
+ }
+ }
+ $scope.onCommit = function(){
+ if(!$scope.redirects) return;
+ $uci.commit("firewall").always(function(){
+ $scope.$apply();
+ console.log("Saved firewall settings!");
+ });
+ }
+ $scope.onCancel = function(){
+ $uci.rollback().always(function(){
+ reload();
+ });
+ }
});
diff --git a/luciexpress/server.js b/luciexpress/server.js
index 34bb37461..b68e8c310 100644
--- a/luciexpress/server.js
+++ b/luciexpress/server.js
@@ -82,7 +82,13 @@ app.post('/ubus', function(req, res) {
body: data
}, function (error, response, body) {
if(error){
- doLocalRPC();
+ console.log("ERROR: "+error);
+ res.write(JSON.stringify({
+ jsonrpc: "2.0",
+ result: [1, error]
+ }));
+ res.end();
+ //doLocalRPC();
return;
}
var json = JSON.stringify(body);