From 3b5d7b368e92026fbcb3e730f8cf6b4cbe529fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Thu, 30 Apr 2015 13:35:55 +0200 Subject: [PATCH] Experimental js schema validation for UCI objects. --- luciexpress/htdocs/index.html | 1 + luciexpress/htdocs/js/app.js | 79 +++++-- luciexpress/htdocs/js/uci.js | 213 +++++++++++++++++- luciexpress/htdocs/lib/js/js-schema.min.js | 1 + luciexpress/htdocs/plugins/core/plugin.json | 1 + .../widgets/uci.wireless.device.edit.html | 25 ++ .../core/widgets/uci.wireless.device.edit.js | 18 ++ .../core/widgets/uci.wireless.device.js | 1 + .../core/widgets/uci.wireless.interface.html | 2 + .../core/widgets/uci.wireless.interface.js | 33 +-- ...uci.wireless.interface.macfilter.edit.html | 4 +- .../uci.wireless.interface.macfilter.edit.js | 56 ++--- .../htdocs/plugins/wifi/pages/wifi.general.js | 3 +- .../plugins/wifi/pages/wifi.mac_filter.js | 2 + .../plugins/wifi/pages/wifi.settings.html | 2 + .../plugins/wifi/pages/wifi.settings.js | 42 +++- luciexpress/share/acl.d/luci2.json | 12 +- 17 files changed, 392 insertions(+), 103 deletions(-) create mode 100644 luciexpress/htdocs/lib/js/js-schema.min.js diff --git a/luciexpress/htdocs/index.html b/luciexpress/htdocs/index.html index d8d37b67e..16744420c 100644 --- a/luciexpress/htdocs/index.html +++ b/luciexpress/htdocs/index.html @@ -23,6 +23,7 @@ -->
+ diff --git a/luciexpress/htdocs/js/app.js b/luciexpress/htdocs/js/app.js index 000886ff1..4e3a7671e 100644 --- a/luciexpress/htdocs/js/app.js +++ b/luciexpress/htdocs/js/app.js @@ -147,31 +147,62 @@ angular.module("luci", [ }); }) -//window.app = angular.module("luci"); - -/* -angular.module("luci").controller("BodyCtrl", function ($scope, $templateCache, $localStorage, $state, $session, $location, $window, $rootScope, $config, $http) { - $scope.menuClass = function(page) { - var current = $location.path().substring(1); - return page === current ? "active" : ""; - }; - $scope.modeList = [{ - id: 0, - label: "Basic Mode" - }]; - +angular.module("luci") +.factory("$hosts", function($rpc, $uci){ + var hosts = {}; + var host_schema = schema({ + hostname: /[a-zA-Z0-9]*/, + macaddr: /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/ + }); + return { + insert: function(obj){ + var deferred = $.Deferred(); + deferred.resolve(obj); + return deferred.promise(); + }, + select: function(rules){ + var mac = rules.macaddr; + var deferred = $.Deferred(); + if(mac in hosts) deferred.resolve(hosts[mac]); + else { + async.series([ + function(next){ + $uci.show("hosts").done(function(result){ + Object.keys(result).map(function(k){ + var host = result[k]; + if(!host_schema(host)) { + console.log("ERROR processing host "+k+": "+JSON.stringify(host_schema.errors(host))); + //return; + } + hosts[host.macaddr] = host; + }); + next(); + }).fail(function(){ next(); }); + }, + function(next){ + $rpc.router.clients().done(function(clients){ + Object.keys(clients).map(function(x){ + var cl = clients[x]; + if(!(cl.macaddr in hosts)){ + hosts[cl.macaddr] = { + hostname: cl.hostname, + macaddr: cl.macaddr + }; + } + }); + next(); + }).fail(function(){ next(); }); + } + ], function(){ + console.log("HOSTS: "+JSON.stringify(hosts)); + if(!(mac in hosts)) deferred.reject(); + else deferred.resolve(hosts[mac]); + }); + } + return deferred.promise(); + } + } }); -*/ -/* -angular.module("luci").directive('inverted', function() { - return { - require: 'ngModel', - link: function(scope, element, attrs, ngModel) { - ngModel.$parsers.push(function(val) { return !val; }); - ngModel.$formatters.push(function(val) { return !val; }); - } - }; -});*/ $(document).ready(function(){ diff --git a/luciexpress/htdocs/js/uci.js b/luciexpress/htdocs/js/uci.js index 90b0bf859..c39481a3f 100644 --- a/luciexpress/htdocs/js/uci.js +++ b/luciexpress/htdocs/js/uci.js @@ -19,10 +19,122 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ - + // uci module for interacting with uci tables angular.module("luci") .factory('$uci', function($rpc, $rootScope){ + // TODO: schemas must be supplied by the router. + var schemas = { + "wifi-device": { + schema: { + "type": String, + "country": String, + "band": [ "a", "b" ], + "bandwidth": Number, + "channel": [ "auto", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ], + "scantimer": Number, + "wmm": Boolean, + "wmm_noack": Boolean, + "wmm_apsd": Boolean, + "txpower": Number, + "rateset": [ "default" ], + "frag": Number, + "rts": Number, + "dtim_period": Number, + "beacon_int": Number, + "rxchainps": Boolean, + "rxchainps_qt": Number, + "rxchainps_pps": Number, + "rifs": Boolean, + "rifs_advert": Boolean, + "maxassoc": Number, + "doth": Boolean, + "hwmode": [ "auto", "11ac" ], + "radio": [ "on", "off" ] + }, + defaults: { + "type": "broadcom", + "country": "EU\/13", + "band": "b", + "bandwidth": 20, + "channel": "auto", + "scantimer": 15, + "wmm": 1, + "wmm_noack": 0, + "wmm_apsd": 0, + "txpower": 100, + "rateset": "default", + "frag": 0, + "rts": 0, + "dtim_period": 1, + "beacon_int": 100, + "rxchainps": 0, + "rxchainps_qt": 10, + "rxchainps_pps": 10, + "rifs": 0, + "rifs_advert": 0, + "maxassoc": 16, + "doth": 0, + "hwmode": "auto", + "radio": "on" + } + }, + "wifi-iface": { + schema: { + "device": /^wl0|wl1$/, + "network": [ "wan", "lan" ], + "mode": [ "ap" ], + "ssid": String, + "encryption": /^none|wpa|wpa2|mixed-wpa|wep-shared|mixed-psk$/, + "cipher": [ "auto" ], + "key": String, + "gtk_rekey": Boolean, + "wps_pbc": Boolean, + "wmf_bss_enable": Boolean, + "bss_max": Number, + "instance": Number, + "up": Boolean, + "disabled": Boolean, + "macmode": [ 0, 1, 2 ], + "macfilter": Boolean, + "maclist": Array(/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/) + }, + defaults: { + "device": "wl0", + "network": "lan", + "mode": "ap", + "ssid": "", + "encryption": "mixed-psk", + "cipher": "auto", + "key": "", + "gtk_rekey": 0, + "wps_pbc": 1, + "wmf_bss_enable": 1, + "bss_max": 16, + "instance": 1.2, + "up": 1, + "disabled": 0, + "open": 0, + "macfilter_mode": 2, + "enabled": 1, + "macfilter": 1, + "maclist": ["00:00:00:00:00:00"], + "macmode": 0, + "ifname": "wl1" + } + }, + "host": { + schema: { + "hostname": String, + "macaddr": /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/ + }, + defaults: { + "hostname": "", + "macaddr": "00:00:00:00:00:00" + } + } + }; + function initReq(path){ var parts = path.split("."); var req = {}; @@ -32,13 +144,106 @@ angular.module("luci") if(parts.length > 2) req.option = parts[2]; return req; } + + function stringify(obj) { + var ret = {}; + for (var property in obj) { + if (obj.hasOwnProperty(property)) { + //console.log(property+": "+(typeof obj[property])+", array: "+(obj[property] instanceof Array)); + if(obj[property] instanceof Array){ + ret[property] = obj[property]; // skip arrays + } else if (typeof obj[property] == "object"){ + ret[property] = stringify(obj[property]); + } else { + ret[property] = String(obj[property]); + if(ret[property] === "true") ret[property] = "1"; + if(ret[property] === "false") ret[property] = "0"; + } + } + } + return ret; + } + // validates a uci object against scheme using it's type and replaces + // invalid values with defaults. + function fixup_values(values, insert_defaults, path){ + // converts all strings that are numbers to actual numbers in object + function fixup(obj, sc) { + for (var property in obj) { + if (obj.hasOwnProperty(property)) { + if (typeof obj[property] == "object" && !(obj[property] instanceof Array)){ + fixup(obj[property], (sc.schema||{})[property]); + } else { + var num = Number(obj[property]); + if(!isNaN(num)) obj[property] = num; + var type = (sc.schema||{})[property]; + if(!(property in obj)) { + obj[property] = sc.defaults[property]; + } + if(type) { + var errors = []; + var def = sc.defaults[property]; + if(type == Boolean){ + if(obj[property] == 0) obj[property] = false; + if(obj[property] == 1) obj[property] = true; + } else if(type instanceof Array && obj[property] instanceof Array){ + obj[property] = obj[property].map(function(x){ + var err = schema(type).errors(x); + if(err) { + errors.push(err); + return ((def instanceof Array)?def[0]:def); + } + return x; + }); + } else { + var err = schema(type).errors(obj[property]); + if(err) { + obj[property] = def; + errors.push(err); + } + } + if(errors.length){ + var name = (path||"")+".@"+obj[".type"]+"["+obj[".name"]+"]."+property; + console.error("UCI: Failed to validate field "+name+", resettings to: "+JSON.stringify(sc.defaults[property]+": "+JSON.stringify(errors))); + } + } + } + } + } + return obj; + } + + var fixed = {}; + if(".type" in values){ + var sc = (schemas[values[".type"]]||{}); + fixed = fixup(values, sc); + //validate(sc, values); + } else { + fixed = {}; + Object.keys(values).map(function(k){ + var obj = values[k]; + if(!(".type" in obj)){ + console.log("Object missing type! ("+k+")"); + } else { + var sc = (schemas[obj[".type"]]||{}); + fixed[k] = fixup(obj, sc); + //validate(sc, obj); + } + }); + } + return fixed; + } return { show: function(path){ var deferred = $.Deferred(); var req = initReq(path); + $rpc.uci.state(req).done(function(state){ - if(state && state.values) deferred.resolve(state.values); - else if(state && state.value) deferred.resolve(state.value); + var fixed = null; + try { + if(state && state.values) fixed = fixup_values(state.values); + else if(state && state.value) fixed = fixup_values(state.value); + } catch(err) { console.error(err); deferred.reject(err); }; + if(fixed) deferred.resolve(fixed); else deferred.reject(); }).fail(function(){ deferred.reject(); @@ -48,7 +253,7 @@ angular.module("luci") set: function(path, values){ var deferred = $.Deferred(); var req = initReq(path); - req.values = values; + req.values = stringify(fixup_values(values)); $rpc.uci.set(req).done(function(state){ deferred.resolve(); }).fail(function(){ diff --git a/luciexpress/htdocs/lib/js/js-schema.min.js b/luciexpress/htdocs/lib/js/js-schema.min.js new file mode 100644 index 000000000..b91982eb6 --- /dev/null +++ b/luciexpress/htdocs/lib/js/js-schema.min.js @@ -0,0 +1 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0&&instance.lengththis.max)return"Array length should not be more than "+this.max+" and is "+instance.length}var results={};for(var i=0;i0){return results}return false},validate:function(instance){if(!(instance instanceof Array))return false;if(this.min===this.max){if(instance.length!==this.min)return false}else{if(this.min>0&&instance.lengththis.max)return false}for(var i=0;i0)json.minItems=this.min;if(this.maxthis.minimum:instance>=this.minimum)&&(this.exclusiveMaximum?instance2?args[2]:args[1],regexp="^"+charset+"{"+(min||0)+","+(max||"")+"}$";return new RegexpSchema(RegExp(regexp)).wrap()};String.schema=(new RegexpSchema).wrap()},{"../patterns/regexp":17}],10:[function(require,module,exports){var Schema=require("../BaseSchema");var AnythingSchema=module.exports=Schema.patterns.AnythingSchema=Schema.extend({errors:function(instance){if(instance==null)return"anything cannot be null";return false},validate:function(instance){return instance!=null},toJSON:function(){return{type:"any"}}});var anything=AnythingSchema.instance=new AnythingSchema;Schema.fromJS.def(function(sch){if(sch===undefined)return anything});Schema.fromJSON.def(function(sch){if(sch.type==="any")return anything})},{"../BaseSchema":2}],11:[function(require,module,exports){var Schema=require("../BaseSchema");var ClassSchema=module.exports=Schema.patterns.ClassSchema=Schema.extend({initialize:function(constructor){this.constructor=constructor},getName:function(obj){if(!obj)return obj;if(obj instanceof Object){return obj.constructor.name}else{return typeof obj+" = "+obj}},errors:function(instance){var middleMessage=" is not instance of ";if(instance==null){return this.getName(instance)+middleMessage+this.getName(this.constructor)}if(!(instance instanceof this.constructor)){return this.getName(instance)+middleMessage+this.getName(this.constructor)}return false},validate:function(instance){return instance instanceof this.constructor}});Schema.fromJS.def(function(constructor){if(!(constructor instanceof Function))return;if(constructor.schema instanceof Function){return constructor.schema.unwrap()}else{return new ClassSchema(constructor)}})},{"../BaseSchema":2}],12:[function(require,module,exports){var Schema=require("../BaseSchema");var equal=function(a,b){if(Object(a)!==a||Object(b)!==b)return a===b;if(a instanceof Array!==b instanceof Array)return false;if(Object.keys(a).length!==Object.keys(b).length)return false;for(var key in a){if(!equal(a[key],b[key]))return false}return true};var EqualitySchema=module.exports=Schema.patterns.EqualitySchema=Schema.extend({initialize:function(object){this.object=object},errors:function(instance){if(!equal(instance,this.object)){return instance+" is not equal to "+this.object}return false},validate:function(instance){return equal(instance,this.object)},toJSON:function(){var json=Schema.prototype.toJSON.call(this);json["enum"]=[this.object];return json}});Schema.fromJS.def(function(sch){if(sch instanceof Array&&sch.length===1)return new EqualitySchema(sch[0])})},{"../BaseSchema":2}],13:[function(require,module,exports){var Schema=require("../BaseSchema");var NothingSchema=module.exports=Schema.patterns.NothingSchema=Schema.extend({errors:function(instance){return false},validate:function(instance){return instance==null},toJSON:function(){return{type:"null"}}});var nothing=NothingSchema.instance=new NothingSchema;Schema.fromJS.def(function(sch){if(sch===null)return nothing});Schema.fromJSON.def(function(sch){if(sch.type==="null")return nothing})},{"../BaseSchema":2}],14:[function(require,module,exports){var Schema=require("../BaseSchema"),anything=require("./anything").instance,nothing=require("./nothing").instance;var ObjectSchema=module.exports=Schema.patterns.ObjectSchema=Schema.extend({initialize:function(properties,other){var self=this;this.other=other||anything;this.properties=properties||[];this.stringProps={},this.regexpProps=[];this.properties.forEach(function(property){if(typeof property.key==="string"){self.stringProps[property.key]=property}else{self.regexpProps.push(property)}})},errors:function(instance){var self=this;if(instance==null)return instance+" is not Object";var errors={};Object.keys(this.stringProps).forEach(function(key){var result=self.stringProps[key].value.errors(instance[key]);if(result){errors[key]=result}});if(Object.keys(errors).length>0){return errors}return false},validate:function(instance){var self=this;if(instance==null)return false;var stringPropsValid=Object.keys(this.stringProps).every(function(key){return self.stringProps[key].min===0&&!(key in instance)||self.stringProps[key].value.validate(instance[key])});if(!stringPropsValid)return false;if(!this.regexpProps.length&&this.other===anything)return true;var checked;for(var key in instance){checked=false;var regexpPropsValid=Object.keys(this.regexpProps).every(function(key){return!self.regexpProps[key].key.test(key)||(checked=true)&&self.regexpProps[key].value.validate(instance[key])});if(!regexpPropsValid)return false;if(!checked&&!(key in this.stringProps)&&!this.other.validate(instance[key]))return false}return true},toJSON:Schema.session(function(){var i,property,regexp,json=Schema.prototype.toJSON.call(this,true);if(json["$ref"]!=null)return json;json.type="object";for(i in this.stringProps){property=this.stringProps[i];json.properties=json.properties||{};json.properties[property.key]=property.value.toJSON();if(property.min===1)json.properties[property.key].required=true;if(property.title)json.properties[property.key].title=property.title}for(i=0;i0){return errors}}return false},validate:function(instance){return this.schemas.some(function(sch){return sch.validate(instance)})},toJSON:Schema.session(function(){var json=Schema.prototype.toJSON.call(this,true),subjsons=this.schemas.map(function(sch){return sch.toJSON()}),onlyEquality=subjsons.every(function(json){return json["enum"]instanceof Array&&json["enum"].length===1});if(json["$ref"]!=null)return json;if(onlyEquality){json["enum"]=subjsons.map(function(json){return json["enum"][0]})}else{json["type"]=subjsons.map(function(json){var simpleType=typeof json.type==="string"&&Object.keys(json).length===1;return simpleType?json.type:json})}return json})});Schema.fromJS.def(function(schemas){if(schemas instanceof Array)return new OrSchema(schemas.map(function(sch){return sch===undefined?Schema.self:Schema.fromJS(sch)}))});Schema.fromJSON.def(function(sch){if(!sch)return;if(sch["enum"]instanceof Array){return new OrSchema(sch["enum"].map(function(object){return new EqualitySchema(object)}))}if(sch["type"]instanceof Array){return new OrSchema(sch["type"].map(function(type){return Schema.fromJSON(typeof type==="string"?{type:type}:type)}))}})},{"../BaseSchema":2,"../patterns/equality":12}],16:[function(require,module,exports){var Schema=require("../BaseSchema");var ReferenceSchema=module.exports=Schema.patterns.ReferenceSchema=Schema.extend({initialize:function(value){this.value=value},getName:function(obj){if(obj instanceof Object){return obj.constructor.name+" = "+obj}else{return typeof obj+" = "+obj}},errors:function(instance){if(instance==null){return instance+" is not a reference"}if(instance!==this.value){var middleMessage=" is not reference to ";return this.getName(instance)+middleMessage+this.getName(this.value)}return false},validate:function(instance){return instance===this.value},toJSON:function(){var json=Schema.prototype.toJSON.call(this);json["enum"]=[this.value];return json}});Schema.fromJS.def(function(value){return new ReferenceSchema(value)})},{"../BaseSchema":2}],17:[function(require,module,exports){var Schema=require("../BaseSchema");var RegexpSchema=module.exports=Schema.patterns.RegexpSchema=Schema.extend({initialize:function(regexp){this.regexp=regexp},errors:function(instance){var message;if(!(Object(instance)instanceof String)){message=instance+" is not a String"}else if(this.regexp&&!this.regexp.test(instance)){message=instance+" is not matched with RegExp -> "+this.regexp}if(message)return message;return false},validate:function(instance){return Object(instance)instanceof String&&(!this.regexp||this.regexp.test(instance))},toJSON:function(){var json=Schema.prototype.toJSON.call(this);json.type="string";if(this.regexp){json.pattern=this.regexp.toString();json.pattern=json.pattern.substr(1,json.pattern.length-2)}return json}});Schema.fromJSON.def(function(sch){if(!sch||sch.type!=="string")return;if("pattern"in sch){return new RegexpSchema(RegExp("^"+sch.pattern+"$"))}else if("minLength"in sch||"maxLength"in sch){return new RegexpSchema(RegExp("^.{"+[sch.minLength||0,sch.maxLength].join(",")+"}$"))}else{return new RegexpSchema}});Schema.fromJS.def(function(regexp){if(regexp instanceof RegExp)return new RegexpSchema(regexp)})},{"../BaseSchema":2}],18:[function(require,module,exports){var Schema=require("../BaseSchema");Schema.fromJS.def(function(sch){if(sch instanceof Schema)return sch})},{"../BaseSchema":2}],19:[function(require,module,exports){var Schema=require("./BaseSchema");schema=module.exports=function(schemaDescription){var doc,schemaObject;if(arguments.length===2){doc=schemaDescription;schemaDescription=arguments[1]}if(this instanceof schema){var constructor=Schema.extend(schemaDescription);schemaObject=new constructor;if(doc)schemaObject.doc=doc;return schemaObject.wrap()}else{schemaObject=Schema.fromJS(schemaDescription);schema.self.resolve(schemaObject);if(doc)schemaObject.doc=doc;return schemaObject.wrap()}};schema.Schema=Schema;schema.toJSON=function(sch){return Schema.fromJS(sch).toJSON()};schema.fromJS=function(sch){return Schema.fromJS(sch).wrap()};schema.fromJSON=function(sch){return Schema.fromJSON(sch).wrap()};if(typeof define==="function"&&define.amd){define([],function(){return schema})}},{"./BaseSchema":2}]},{},[1]); diff --git a/luciexpress/htdocs/plugins/core/plugin.json b/luciexpress/htdocs/plugins/core/plugin.json index 5666ab5a9..ee7c2eebe 100644 --- a/luciexpress/htdocs/plugins/core/plugin.json +++ b/luciexpress/htdocs/plugins/core/plugin.json @@ -14,6 +14,7 @@ "widgets/luci.config", "widgets/uci.wireless.interface", "widgets/uci.wireless.interface.macfilter.edit", + "widgets/uci.wireless.device.edit", "widgets/uci.firewall.nat.rule.edit", "widgets/core.modal" ], diff --git a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.device.edit.html b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.device.edit.html index e69de29bb..0be8a84ea 100644 --- a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.device.edit.html +++ b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.device.edit.html @@ -0,0 +1,25 @@ +
+ + + + + {{"mode."+$select.selected}} +
{{"mode."+item}}
+
+
+ + + {{"bandwidth."+$select.selected}} +
{{"bandwidth."+item}}
+
+
+ + + {{"channel."+$select.selected}} +
{{"channel."+item}}
+
+
+
+
+
+ diff --git a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.device.edit.js b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.device.edit.js index e69de29bb..9930594d2 100644 --- a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.device.edit.js +++ b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.device.edit.js @@ -0,0 +1,18 @@ +$juci.module("core") +.directive("uciWirelessDeviceEdit", function($compile){ + var plugin_root = $juci.module("core").plugin_root; + return { + templateUrl: plugin_root+"/widgets/uci.wireless.device.edit.html", + scope: { + device: "=ngModel" + }, + controller: "WifiDeviceEditController", + replace: true, + require: "^ngModel" + }; +}).controller("WifiDeviceEditController", function($scope){ + $scope.allChannels = [ "auto", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]; + $scope.allModes = ["802.11gn", "802.11bg", "802.11bgn", "802.11n"]; + $scope.allBandwidths = [ "20", "40", "2040", "80" ]; + +}); diff --git a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.device.js b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.device.js index e69de29bb..8b1378917 100644 --- a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.device.js +++ b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.device.js @@ -0,0 +1 @@ + diff --git a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.html b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.html index 88f4308a4..c7eb25343 100644 --- a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.html +++ b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.html @@ -2,6 +2,8 @@ + + diff --git a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.js b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.js index bf54d3f0e..ed916feaa 100644 --- a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.js +++ b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.js @@ -11,44 +11,17 @@ $juci.module("core") require: "^ngModel" }; }).controller("WifiInterfaceController", function($scope){ - //$scope.interface = {}; $scope.selectedProtection = "none"; $scope.selectedRadio = "wl0"; - /*$scope.allFreqs = [{ - label: "2.4 GHz / 5 GHz", - bands: ["a", "b"] - }, { - label: "2.4 GHz", - bands: ["b"] - }, { - label: "5 GHz", - bands: ["a"] - }]; */ + $scope.allRadios = ["wl0", "wl1", "wl0wl1"]; $scope.allCryptModes = ["none", "wpa", "wpa2", "mixed-wpa", "wep-shared"]; $scope.configName = ""; // TODO: need to have generic uci variables that reflect the gui $scope.$watch("interface", function(value){ - value.disabled = (value.disabled == '1')?true:false; - value.closed = (value.closed == '1')?true:false; + //value.disabled = (value.disabled == '1')?true:false; + //value.closed = (value.closed == '1')?true:false; $scope.configName = $scope.interface[".name"] + ".disabled"; - }); - /*$scope.allCryptModes = [{ - label: "Off", - encryption: "none", - }, { - label: "WPA", - encryption: "wpa" - }, { - label: "WPA2", - encryption: "wpa2" - }, { - label: "WPA + WPA2", - encryption: "mixed-wpa" - }, { - label: "WEP", - encryption: "wep-shared" - }]; */ }); diff --git a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.macfilter.edit.html b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.macfilter.edit.html index 09999c45a..b6d9eda5d 100644 --- a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.macfilter.edit.html +++ b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.macfilter.edit.html @@ -13,7 +13,7 @@
- +
@@ -23,7 +23,7 @@ - + diff --git a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.macfilter.edit.js b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.macfilter.edit.js index 882599a7e..a9a1c461b 100644 --- a/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.macfilter.edit.js +++ b/luciexpress/htdocs/plugins/core/widgets/uci.wireless.interface.macfilter.edit.js @@ -10,33 +10,37 @@ $juci.module("core") replace: true, require: "^ngModel" }; -}).controller("uciWirelessInterfaceMacfilterEditController", function($scope, $rpc, $uci){ +}).controller("uciWirelessInterfaceMacfilterEditController", function($scope, $rpc, $uci, $hosts){ $scope.maclist = []; $scope.filterEnabled = 0; // updates scratch model for the view function updateMaclist(i){ - $scope.maclist = []; + var maclist = []; if(i && i.maclist) { - var clients = $scope.clients || {}; - console.log("Updating: "+JSON.stringify(i.maclist)); - i.maclist.map(function(x){ - var parts = x.split("|"); - $scope.maclist.push({ hostname: (clients[parts[0]]||parts[1]||""), macaddr: parts[0] }); + async.eachSeries(i.maclist, function(mac, next){ + $hosts.select({ macaddr: mac }).done(function(host){ + maclist.push(host); + next(); + }).fail(function(){ + $hosts.insert({ hostname: "", macaddr: mac }).done(function(host){ + maclist.push(host); + }).always(function(){ next(); }); + }); + }, function(){ + $scope.maclist = maclist; }); + } else { + $scope.maclist = []; } } // watch for model change $scope.$watch("interface", function(i){ - $scope.filterEnabled = (i.macfilter && i.macfilter == "1"); - }); - - // watch for changes in list of connected clients - $scope.$watchCollection("clients", function(clients){ - updateMaclist($scope.interface); - }); + $scope.filterEnabled = i.macfilter; + updateMaclist(i); + }, true); // watch maclist for changes by the user $scope.$watch("maclist", function(list){ @@ -46,29 +50,23 @@ $juci.module("core") interface.maclist = []; list.map(function(x){ // save the hostname - x.macaddr = x.macaddr||""; - x.hostname = x.hostname||""; - interface.maclist.push(x.macaddr.replace("|", "")+"|"+x.hostname.replace("|", "")); + var macaddr = x.macaddr||""; + interface.maclist.push(macaddr); }); } - }, true); + }); $scope.$watch("filterEnabled", function(value){ $scope.interface.macfilter = value; }); $rpc.router.clients().done(function(clients){ - $scope.clients = {}; - $scope.client_list = []; - Object.keys(clients).map(function(x){ - var cl = clients[x]; - $scope.clients[cl.macaddr] = cl.hostname; - $scope.client_list.push({ + $scope.client_list = Object.keys(clients).map(function(x){ + return { checked: false, - client: cl - }); + client: clients[x] + } }); - console.log(JSON.stringify($scope.clients)); $scope.$apply(); }); @@ -112,10 +110,6 @@ $juci.module("core") $scope.showModal = 0; } - $scope.validation = function(form){ - - } - $scope.onDismissModal = function(){ $scope.showModal = 0; } diff --git a/luciexpress/htdocs/plugins/wifi/pages/wifi.general.js b/luciexpress/htdocs/plugins/wifi/pages/wifi.general.js index 4fe03a190..eb500bd25 100644 --- a/luciexpress/htdocs/plugins/wifi/pages/wifi.general.js +++ b/luciexpress/htdocs/plugins/wifi/pages/wifi.general.js @@ -33,7 +33,7 @@ $juci.module("wifi") }); }, function(){ $scope.info = null; - $scope.$apply(); + //$scope.$apply(); }); } $scope.onGuestEnable = function(){ @@ -64,6 +64,7 @@ $juci.module("wifi") $scope.main_wifi = $scope.interfaces[0]; //$scope.interfaces.filter(function(x) { return x[".name"] == "main"; })[0] || {}; $scope.guest_wifi = $scope.interfaces[1]; //$scope.interfaces.filter(function(x) { return x[".name"] == "guest"; })[0] || {}; + console.log(JSON.stringify($scope.main_wifi)); //$scope.guestWifiEnabled = ($scope.guest_wifi && $scope.guest_wifi.up == '1'); $scope.$apply(); }); diff --git a/luciexpress/htdocs/plugins/wifi/pages/wifi.mac_filter.js b/luciexpress/htdocs/plugins/wifi/pages/wifi.mac_filter.js index 67dbca1e3..84c27a3af 100644 --- a/luciexpress/htdocs/plugins/wifi/pages/wifi.mac_filter.js +++ b/luciexpress/htdocs/plugins/wifi/pages/wifi.mac_filter.js @@ -21,11 +21,13 @@ $juci.module("wifi") $scope.busy = 1; async.series([ function(next){ + console.log("Saving object: "+JSON.stringify($scope.main_wifi)); $uci.set("wireless."+$scope.main_wifi[".name"], $scope.main_wifi).done(function(){ }).always(function(){ next(); }); }, function(next){ + console.log("Saving object: "+JSON.stringify($scope.guest_wifi)); $uci.set("wireless."+$scope.guest_wifi[".name"], $scope.guest_wifi).done(function(){ }).always(function(){ next(); }); diff --git a/luciexpress/htdocs/plugins/wifi/pages/wifi.settings.html b/luciexpress/htdocs/plugins/wifi/pages/wifi.settings.html index 088ab69d0..824784e8f 100644 --- a/luciexpress/htdocs/plugins/wifi/pages/wifi.settings.html +++ b/luciexpress/htdocs/plugins/wifi/pages/wifi.settings.html @@ -4,7 +4,9 @@ Settings Below you can change parameters for the two WiFi frequencies 2.4 GHz and 5 GHz. +

2.4Ghz

+

5Ghz

diff --git a/luciexpress/htdocs/plugins/wifi/pages/wifi.settings.js b/luciexpress/htdocs/plugins/wifi/pages/wifi.settings.js index 7d38ac854..189882b96 100644 --- a/luciexpress/htdocs/plugins/wifi/pages/wifi.settings.js +++ b/luciexpress/htdocs/plugins/wifi/pages/wifi.settings.js @@ -1,4 +1,44 @@ $juci.module("wifi") -.controller("WifiSettingsPageCtrl", function($scope){ +.controller("WifiSettingsPageCtrl", function($scope, $uci){ + function load(){ + $uci.show("wireless").done(function(interfaces){ + var list = Object.keys(interfaces) + .map(function(x){ return interfaces[x]; }); + $scope.devices = list.filter(function(x) { return x[".type"] == "wifi-device"; }); + $scope.interfaces = list.filter(function(x) { return x[".type"] == "wifi-iface"; }); + + $scope.wifi24 = $scope.devices[0]; //$scope.interfaces.filter(function(x) { return x[".name"] == "main"; })[0] || {}; + $scope.wifi5 = $scope.devices[1]; //$scope.interfaces.filter(function(x) { return x[".name"] == "guest"; })[0] || {}; + + $scope.$apply(); + }); + } load(); + + $scope.onApply = function(){ + $scope.busy = 1; + async.series([ + function(next){ + console.log("Saving object: "+JSON.stringify($scope.wifi24)); + $uci.set("wireless."+$scope.wifi24[".name"], $scope.wifi24).done(function(){ + + }).always(function(){ next(); }); + }, + function(next){ + console.log("Saving object: "+JSON.stringify($scope.wifi5)); + $uci.set("wireless."+$scope.wifi5[".name"], $scope.wifi5).done(function(){ + + }).always(function(){ next(); }); + } + ], function(){ + $uci.commit("wireless").done(function(){ + console.log("Saved wifi settings!"); + }).fail(function(){ + console.log("Failed to save wifi settings!"); + }).always(function(){ + $scope.busy = 0; + $scope.$apply(); + }); + }); + } }); diff --git a/luciexpress/share/acl.d/luci2.json b/luciexpress/share/acl.d/luci2.json index 61211017b..62294031a 100644 --- a/luciexpress/share/acl.d/luci2.json +++ b/luciexpress/share/acl.d/luci2.json @@ -113,11 +113,7 @@ }, "uci": [ "dropbear", - "set", - "get", - "commit", - "state", - "delete" + "hosts" ] }, "write": { @@ -129,11 +125,7 @@ }, "uci": [ "dropbear", - "set", - "get", - "commit", - "state", - "delete" + "hosts" ] } },