this.render_one(e,t))},render_one:function(e,t){const n=new g({type:this.type,model:t});n.render(),e.append(n.$el)},get_log:function(e,t){this.collection.uuid=t,this.collection.logType=e,this.type=e,this.$el.html(""),this.filter_model.clear(),this.update()},update:function(){this.collection.fetch()},update_filter:function(e){const t=e.target;this.filter_model.set(t.name,$(t).val())}})),v=new _({collection:l,logview:y});$(document.getElementById("logapplication")).append(v.$el).append(y.$el),v.render()}]);
diff --git a/www/nginx/src/opnsense/www/js/nginx/dist/configuration.js b/www/nginx/src/opnsense/www/js/nginx/dist/configuration.js
new file mode 100644
index 000000000..be46cc622
--- /dev/null
+++ b/www/nginx/src/opnsense/www/js/nginx/dist/configuration.js
@@ -0,0 +1 @@
+!function(e){var t={};function i(n){if(t[n])return t[n].exports;var s=t[n]={i:n,l:!1,exports:{}};return e[n].call(s.exports,s,s.exports,i),s.l=!0,s.exports}i.m=e,i.c=t,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)i.d(n,s,function(t){return e[t]}.bind(null,s));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i(i.s=21)}({21:function(e,t,i){"use strict";i.r(t);var n=Backbone.View.extend({tagName:"div",attributes:{class:"container-fluid"},child_views:[],createModel:null,upstreamCollection:null,initialize:function(e){this.dataField=$(e.dataField),this.entryclass=e.entryclass,this.createModel=e.createModel,this.upstreamCollection=e.upstreamCollection,this.listenTo(this.collection,"add remove reset",this.render),this.listenTo(this.collection,"change",this.update),this.dataField.after(this.$el)},events:{"click .add":"addEntry"},render:function(){this.child_views.forEach(e=>e.remove()),this.$el.html(""),this.child_views=[],this.update(),this.collection.each(e=>{const t=new this.entryclass({model:e,collection:this.collection,upstreamCollection:this.upstreamCollection});this.child_views.push(t),this.$el.append(t.$el),t.render()}),this.$el.append($('\n \n \n
'))},update:function(){this.dataField.val(JSON.stringify(this.collection.toJSON()))},addEntry:function(e){e.preventDefault(),this.collection.add(this.createModel())}});var s=Backbone.Collection.extend({url:"/api/nginx/settings/searchupstream",parse:function(e){return e.rows}});const l=Backbone.View.extend({tagName:"div",attributes:{class:"row"},events:{"keyup .key":function(){this.model.set("hostname",this.key.value)},"change .value":function(){this.model.set("upstream",this.value.value)},"click .delete":"deleteEntry"},key:null,value:null,delBtn:null,first:null,second:null,third:null,upstreamCollection:null,initialize:function(e){this.upstreamCollection=e.upstreamCollection,this.listenTo(this.upstreamCollection,"update reset add remove",this.regenerate_list),this.first=document.createElement("div"),this.first.classList.add("col-sm-5"),this.key=document.createElement("input"),this.first.append(this.key),this.key.type="text",this.key.classList.add("key"),this.key.value=this.model.get("hostname"),this.second=document.createElement("div"),this.second.classList.add("col-sm-5"),this.value=document.createElement("select"),this.second.append(this.value),this.value.classList.add("value"),this.value.classList.add("form-control"),this.value.value=this.model.get("upstream"),this.third=document.createElement("div"),this.third.classList.add("col-sm-2"),this.third.style.textAlign="right",this.delBtn=document.createElement("button"),this.delBtn.classList.add("delete"),this.delBtn.classList.add("btn"),this.delBtn.innerHTML='',this.third.append(this.delBtn),this.model.has("upstream")&&0!==this.upstreamCollection.where({uuid:this.model.get("upstream")}).length||this.upstreamCollection.length>0&&this.model.set("upstream",this.upstreamCollection.at(0).get("uuid")),this.$el.append(this.first).append(this.second).append(this.third)},render:function(){$(this.key).val(this.model.get("hostname")),this.regenerate_list(),$(this.value).val(this.model.get("upstream"))},deleteEntry:function(e){e.preventDefault(),this.collection.remove(this.model)},regenerate_list:function(){const e=$(this.value);e.html(""),this.upstreamCollection.each(t=>e.append(``)),e.val(this.model.get("upstream")),e.selectpicker("refresh")}}),a=Backbone.View.extend({tagName:"div",attributes:{class:"row"},events:{"keyup .key":function(){this.model.set("network",this.key.value)},"change .value":function(){this.model.set("action",this.value.value)},"click .delete":"deleteEntry"},key:null,value:null,delBtn:null,first:null,second:null,third:null,upstreamCollection:null,initialize:function(e){this.upstreamCollection=e.upstreamCollection,this.listenTo(this.upstreamCollection,"update reset add remove",this.regenerate_list),this.first=document.createElement("div"),this.first.classList.add("col-sm-5"),this.key=document.createElement("input"),this.first.append(this.key),this.key.type="text",this.key.classList.add("key"),this.key.value=this.model.get("network"),this.second=document.createElement("div"),this.second.classList.add("col-sm-5"),this.value=document.createElement("select"),this.second.append(this.value),this.value.classList.add("value"),this.value.classList.add("form-control"),this.value.value=this.model.get("action"),this.third=document.createElement("div"),this.third.classList.add("col-sm-2"),this.third.style.textAlign="right",this.delBtn=document.createElement("button"),this.delBtn.classList.add("delete"),this.delBtn.classList.add("btn"),this.delBtn.innerHTML='',this.third.append(this.delBtn),this.$el.append(this.first).append(this.second).append(this.third)},render:function(){$(this.key).val(this.model.get("network")),this.regenerate_list(),$(this.value).val(this.model.get("action"))},deleteEntry:function(e){e.preventDefault(),this.collection.remove(this.model)},regenerate_list:function(){const e=$(this.value);e.html(""),this.upstreamCollection.each(t=>e.append(``)),e.val(this.model.get("action")),e.selectpicker("refresh")}});var o=Backbone.Collection.extend({initialize:function(){let e=this;$("#snihostname\\.data").change(function(){e.regenerateFromView()})},regenerateFromView:function(){let e=JSON.parse($("#snihostname\\.data").val());_.isArray(e)||(e=[]),this.reset(e)}}),r=Backbone.Model.extend({}),c=Backbone.Model.extend({}),d=Backbone.Collection.extend({initialize:function(){let e=this;$("#ipacl\\.data").change(function(){e.regenerateFromView()})},regenerateFromView:function(){let e=JSON.parse($("#ipacl\\.data").val());_.isArray(e)||(e=[]),this.reset(e)}});const u=new s,h=new Backbone.Collection([{name:"Deny",value:"deny"},{name:"Allow",value:"allow"}]);$(document).ready(function(){mapDataToFormUI({frm_nginx:"/api/nginx/settings/get"}).done(function(){formatTokenizersUI(),$('select[data-allownew="false"]').selectpicker("refresh"),updateServiceControlUI("nginx")}),""!==window.location.hash&&$('a[href="'+window.location.hash+'"]').click(),$(".nav-tabs a").on("shown.bs.tab",function(e){history.pushState(null,null,e.target.hash)}),$(".reload_btn").click(function(){$(".reloadAct_progress").addClass("fa-spin"),ajaxCall(url="/api/nginx/service/reconfigure",sendData={},callback=function(e,t){$(".reloadAct_progress").removeClass("fa-spin")})}),$('[id*="save_"]').each(function(){$(this).click(function(e){let t=$(this).closest("form").attr("id"),i=$(this).closest("form").attr("data-title");saveFormToEndpoint(url="/api/nginx/settings/set",formid=t,callback_ok=function(){$("#"+t+"_progress").addClass("fa fa-spinner fa-pulse"),ajaxCall(url="/api/nginx/service/reconfigure",sendData={},callback=function(e,n){$("#"+t+"_progress").removeClass("fa fa-spinner fa-pulse"),void 0===e||"success"===n&&"ok"===e.status?updateServiceControlUI("nginx"):BootstrapDialog.show({type:BootstrapDialog.TYPE_WARNING,title:i,message:JSON.stringify(e),draggable:!0})})})})}),["upstream","upstreamserver","location","credential","userlist","httpserver","streamserver","httprewrite","custompolicy","security_header","ipacl","limit_zone","cache_path","limit_request_connection","snifwd","naxsirule"].forEach(function(e){$("#grid-"+e).UIBootgrid({search:"/api/nginx/settings/search"+e,get:"/api/nginx/settings/get"+e+"/",set:"/api/nginx/settings/set"+e+"/",add:"/api/nginx/settings/add"+e+"/",del:"/api/nginx/settings/del"+e+"/",options:{selection:!1,multiSelect:!1}})}),bind_naxsi_rule_dl_button(),function(){let e=new n({dataField:document.getElementById("snihostname.data"),upstreamCollection:u,entryclass:l,collection:new o,createModel:function(){return new r({hostname:"localhost"})}});window.snifield=e,e.render(),$("#grid-upstream").on("loaded.rs.jquery.bootgrid",function(){u.fetch()}),u.fetch()}();let e=new n({dataField:document.getElementById("ipacl.data"),upstreamCollection:h,entryclass:a,collection:new d,createModel:function(){return new c({network:"::",action:"deny"})}});window.ipaclfield=e,e.render()})}});
\ No newline at end of file
diff --git a/www/nginx/src/opnsense/www/js/nginx/dist/logviewer.js b/www/nginx/src/opnsense/www/js/nginx/dist/logviewer.js
new file mode 100644
index 000000000..7ac306cda
--- /dev/null
+++ b/www/nginx/src/opnsense/www/js/nginx/dist/logviewer.js
@@ -0,0 +1 @@
+!function(e){var t={};function n(l){if(t[l])return t[l].exports;var o=t[l]={i:l,l:!1,exports:{}};return e[l].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,l){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:l})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var l=Object.create(null);if(n.r(l),Object.defineProperty(l,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(l,o,function(t){return e[t]}.bind(null,o));return l},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=20)}([function(e,t,n){var l=n(6),o=n(8),i=/[&<>"']/g,r=RegExp(i.source);e.exports=function(e){return(e=o(e))&&r.test(e)?e.replace(i,l):e}},function(e,t,n){var l=n(10).Symbol;e.exports=l},function(module,exports,__webpack_require__){var _={escape:__webpack_require__(0)};module.exports=function(obj){obj||(obj={});var __t,__p="",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,"")}with(obj)__p+='\n\n \n\n'+(null==(__t=_.escape(name))?"":__t)+'\n\n";return __p}},function(module,exports,__webpack_require__){var _={escape:__webpack_require__(0)};module.exports=function(obj){obj||(obj={});var __t,__p="";with(obj)__p+=''+(null==(__t=model.escape("time"))?"":__t)+' | \n'+(null==(__t=model.escape("remote_ip"))?"":__t)+' | \n'+(null==(__t=model.escape("username"))?"":__t)+' | \n'+(null==(__t=model.escape("status"))?"":__t)+' | \n'+(null==(__t=model.escape("size"))?"":__t)+' | \n'+(null==(__t=model.escape("http_referer"))?"":__t)+' | \n'+(null==(__t=model.escape("user_agent"))?"":__t)+' | \n'+(null==(__t=model.escape("forwarded_for"))?"":__t)+' | \n'+(null==(__t=model.escape("request_line"))?"":__t)+" | \n";return __p}},function(module,exports,__webpack_require__){var _={escape:__webpack_require__(0)};module.exports=function(obj){obj||(obj={});var __t,__p="";with(obj)__p+=''+(null==(__t=model.escape("date"))?"":__t)+' | \n'+(null==(__t=model.escape("time"))?"":__t)+' | \n'+(null==(__t=model.escape("severity"))?"":__t)+' | \n'+(null==(__t=model.escape("number"))?"":__t)+' | \n'+(null==(__t=model.escape("message"))?"":__t)+" | \n";return __p}},function(module,exports,__webpack_require__){var _={escape:__webpack_require__(0)};module.exports=function(obj){obj||(obj={});var __t,__p="",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,"")}with(obj)__p+='\n";return __p}},function(e,t,n){var l=n(7)({"&":"&","<":"<",">":">",'"':""","'":"'"});e.exports=l},function(e,t){e.exports=function(e){return function(t){return null==e?void 0:e[t]}}},function(e,t,n){var l=n(9);e.exports=function(e){return null==e?"":l(e)}},function(e,t,n){var l=n(1),o=n(13),i=n(14),r=n(15),s=1/0,a=l?l.prototype:void 0,_=a?a.toString:void 0;e.exports=function e(t){if("string"==typeof t)return t;if(i(t))return o(t,e)+"";if(r(t))return _?_.call(t):"";var n=t+"";return"0"==n&&1/t==-s?"-0":n}},function(e,t,n){var l=n(11),o="object"==typeof self&&self&&self.Object===Object&&self,i=l||o||Function("return this")();e.exports=i},function(e,t,n){(function(t){var n="object"==typeof t&&t&&t.Object===Object&&t;e.exports=n}).call(this,n(12))},function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t){e.exports=function(e,t){for(var n=-1,l=null==e?0:e.length,o=Array(l);++nthis.render_one(e))},render_one:function(e){const t=new i({uuid:e.get("url"),logType:e.get("logType")}),n=new a({collection:t,model:e,logview:this.logview});this.$el.append(n.$el),t.fetch()}}),c=n(3),u=n.n(c),d=n(4),p=n.n(d),m=n(5),h=n.n(m);var f=Backbone.Model.extend({});var b=Backbone.Collection.extend({model:f,url:function(){return`/api/nginx/logs/${this.logType}/${this.uuid}`},initialize:function(){this.logType="none",this.uuid="none"},filter_collection:function(e){const t=e.keys();return this.filter(function(n){if(!n)return!1;for(let l=0;lthis.render_one(e,t))},render_one:function(e,t){const n=new g({type:this.type,model:t});n.render(),e.append(n.$el)},get_log:function(e,t){this.collection.uuid=t,this.collection.logType=e,this.type=e,this.$el.html(""),this.filter_model.clear(),this.update()},update:function(){this.collection.fetch()},update_filter:function(e){const t=e.target;this.filter_model.set(t.name,$(t).val())}})),v=new _({collection:l,logview:y});$(document.getElementById("logapplication")).append(v.$el).append(y.$el),v.render()}]);
\ No newline at end of file
diff --git a/www/nginx/src/opnsense/www/js/nginx/src/controller/KeyValueMapField.js b/www/nginx/src/opnsense/www/js/nginx/src/controller/KeyValueMapField.js
new file mode 100644
index 000000000..66f88e83d
--- /dev/null
+++ b/www/nginx/src/opnsense/www/js/nginx/src/controller/KeyValueMapField.js
@@ -0,0 +1,50 @@
+export default Backbone.View.extend({
+ tagName: 'div',
+ attributes: {'class': 'container-fluid'},
+ child_views: [],
+ createModel: null,
+ upstreamCollection: null,
+ initialize: function (params) {
+ this.dataField = $(params.dataField);
+ this.entryclass = params.entryclass;
+ this.createModel = params.createModel;
+ this.upstreamCollection = params.upstreamCollection;
+ this.listenTo(this.collection, "add remove reset", this.render);
+ this.listenTo(this.collection, "change", this.update);
+ // inject our table holder
+ this.dataField.after(this.$el);
+ },
+ events: {
+ "click .add": "addEntry"
+ },
+ render: function () {
+ // clear table
+ this.child_views.forEach((model) => model.remove());
+ this.$el.html('');
+ this.child_views = [];
+ this.update();
+ this.collection.each((model) => {
+ const childView = new this.entryclass({
+ model: model,
+ collection: this.collection,
+ upstreamCollection: this.upstreamCollection
+ });
+ this.child_views.push(childView);
+ this.$el.append(childView.$el);
+ childView.render();
+ });
+ this.$el.append($(`
+
+
+
`));
+ },
+ update: function () {
+ this.dataField.val(JSON.stringify(this.collection.toJSON()));
+ },
+ addEntry: function (e) {
+ e.preventDefault();
+ this.collection.add(this.createModel());
+ }
+});
\ No newline at end of file
diff --git a/www/nginx/src/opnsense/www/js/nginx/src/controller/KeyValueMapFieldEntry.js b/www/nginx/src/opnsense/www/js/nginx/src/controller/KeyValueMapFieldEntry.js
new file mode 100644
index 000000000..6fcd6fea4
--- /dev/null
+++ b/www/nginx/src/opnsense/www/js/nginx/src/controller/KeyValueMapFieldEntry.js
@@ -0,0 +1,152 @@
+export const KeyValueMapFieldEntryUpstreamMap = Backbone.View.extend({
+
+ tagName: 'div',
+ attributes: {'class': 'row'},
+ events: {
+ 'keyup .key': function () {
+ this.model.set('hostname', this.key.value);
+ },
+ 'change .value': function () {
+ this.model.set('upstream', this.value.value);
+ },
+ "click .delete" : "deleteEntry"
+ },
+ key: null,
+ value: null,
+ delBtn: null,
+ first: null,
+ second: null,
+ third: null,
+ upstreamCollection: null,
+ initialize: function (params) {
+ this.upstreamCollection = params.upstreamCollection;
+ this.listenTo(this.upstreamCollection, "update reset add remove", this.regenerate_list);
+ this.first = document.createElement('div');
+ this.first.classList.add('col-sm-5');
+ this.key = document.createElement('input');
+ this.first.append(this.key);
+ this.key.type = 'text';
+ this.key.classList.add('key');
+ this.key.value = this.model.get('hostname');
+
+ this.second = document.createElement('div');
+ this.second.classList.add('col-sm-5');
+ this.value = document.createElement('select');
+ this.second.append(this.value);
+ this.value.classList.add('value');
+ this.value.classList.add('form-control');
+ this.value.value = this.model.get('upstream');
+
+ this.third = document.createElement('div');
+ this.third.classList.add('col-sm-2');
+ this.third.style.textAlign = 'right';
+ this.delBtn = document.createElement("button");
+ this.delBtn.classList.add('delete');
+ this.delBtn.classList.add('btn');
+ this.delBtn.innerHTML = '';
+ this.third.append(this.delBtn);
+ if (!this.model.has('upstream') ||
+ this.upstreamCollection.where ({'uuid' : this.model.get('upstream')}).length === 0) {
+ if (this.upstreamCollection.length > 0) {
+ this.model.set('upstream', this.upstreamCollection.at(0).get('uuid'));
+ }
+ }
+
+
+ this.$el.append(this.first).append(this.second).append(this.third);
+ },
+ render: function() {
+ $(this.key).val(this.model.get('hostname'));
+ this.regenerate_list();
+ $(this.value).val(this.model.get('upstream'));
+ },
+ deleteEntry: function (e) {
+ e.preventDefault();
+ this.collection.remove(this.model);
+ },
+ regenerate_list: function () {
+ // backup value
+ const v = $(this.value);
+ // clear the dropdown
+ v.html('');
+ this.upstreamCollection.each(
+ (mdl) => v.append(``)
+ );
+ // restore
+ v.val(this.model.get('upstream'));
+ v.selectpicker('refresh');
+ }
+});
+export const KeyValueMapFieldEntryACL = Backbone.View.extend({
+
+ tagName: 'div',
+ attributes: {'class': 'row'},
+ events: {
+ 'keyup .key': function () {
+ this.model.set('network', this.key.value);
+ },
+ 'change .value': function () {
+ this.model.set('action', this.value.value);
+ },
+ "click .delete" : "deleteEntry"
+ },
+ key: null,
+ value: null,
+ delBtn: null,
+ first: null,
+ second: null,
+ third: null,
+ upstreamCollection: null,
+ initialize: function (params) {
+ this.upstreamCollection = params.upstreamCollection;
+ this.listenTo(this.upstreamCollection, "update reset add remove", this.regenerate_list);
+ this.first = document.createElement('div');
+ this.first.classList.add('col-sm-5');
+ this.key = document.createElement('input');
+ this.first.append(this.key);
+ this.key.type = 'text';
+ this.key.classList.add('key');
+ this.key.value = this.model.get('network');
+
+ this.second = document.createElement('div');
+ this.second.classList.add('col-sm-5');
+ this.value = document.createElement('select');
+ this.second.append(this.value);
+ this.value.classList.add('value');
+ this.value.classList.add('form-control');
+ this.value.value = this.model.get('action');
+
+ this.third = document.createElement('div');
+ this.third.classList.add('col-sm-2');
+ this.third.style.textAlign = 'right';
+ this.delBtn = document.createElement("button");
+ this.delBtn.classList.add('delete');
+ this.delBtn.classList.add('btn');
+ this.delBtn.innerHTML = '';
+ this.third.append(this.delBtn);
+
+
+ this.$el.append(this.first).append(this.second).append(this.third);
+ },
+ render: function() {
+ $(this.key).val(this.model.get('network'));
+ this.regenerate_list();
+ $(this.value).val(this.model.get('action'));
+ },
+ deleteEntry: function (e) {
+ e.preventDefault();
+ this.collection.remove(this.model);
+ },
+ regenerate_list: function () {
+ // backup value
+ const v = $(this.value);
+ // clear the dropdown
+ v.html('');
+ this.upstreamCollection.each(
+ (mdl) => v.append(``)
+ );
+ // restore
+ v.val(this.model.get('action'));
+ v.selectpicker('refresh');
+ }
+});
diff --git a/www/nginx/src/opnsense/www/js/nginx/src/models/IPACLCollection.js b/www/nginx/src/opnsense/www/js/nginx/src/models/IPACLCollection.js
new file mode 100644
index 000000000..c814ee920
--- /dev/null
+++ b/www/nginx/src/opnsense/www/js/nginx/src/models/IPACLCollection.js
@@ -0,0 +1,15 @@
+export default Backbone.Collection.extend({
+ initialize: function() {
+ let that = this;
+ $('#ipacl\\.data').change(function () {
+ that.regenerateFromView();
+ });
+ },
+ regenerateFromView: function () {
+ let data = JSON.parse($('#ipacl\\.data').val());
+ if (!_.isArray(data)) {
+ data = [];
+ }
+ this.reset(data);
+ }
+});
\ No newline at end of file
diff --git a/www/nginx/src/opnsense/www/js/nginx/src/models/IPACLModel.js b/www/nginx/src/opnsense/www/js/nginx/src/models/IPACLModel.js
new file mode 100644
index 000000000..457325136
--- /dev/null
+++ b/www/nginx/src/opnsense/www/js/nginx/src/models/IPACLModel.js
@@ -0,0 +1,3 @@
+export default Backbone.Model.extend({
+ // standard model
+});
\ No newline at end of file
diff --git a/www/nginx/src/opnsense/www/js/nginx/src/models/SNIHostnameUpstreamCollection.js b/www/nginx/src/opnsense/www/js/nginx/src/models/SNIHostnameUpstreamCollection.js
new file mode 100644
index 000000000..3258b0722
--- /dev/null
+++ b/www/nginx/src/opnsense/www/js/nginx/src/models/SNIHostnameUpstreamCollection.js
@@ -0,0 +1,15 @@
+export default Backbone.Collection.extend({
+ initialize: function() {
+ let that = this;
+ $('#snihostname\\.data').change(function () {
+ that.regenerateFromView();
+ });
+ },
+ regenerateFromView: function () {
+ let data = JSON.parse($('#snihostname\\.data').val());
+ if (!_.isArray(data)) {
+ data = [];
+ }
+ this.reset(data);
+ }
+});
\ No newline at end of file
diff --git a/www/nginx/src/opnsense/www/js/nginx/src/models/SNIHostnameUpstreamModel.js b/www/nginx/src/opnsense/www/js/nginx/src/models/SNIHostnameUpstreamModel.js
new file mode 100644
index 000000000..457325136
--- /dev/null
+++ b/www/nginx/src/opnsense/www/js/nginx/src/models/SNIHostnameUpstreamModel.js
@@ -0,0 +1,3 @@
+export default Backbone.Model.extend({
+ // standard model
+});
\ No newline at end of file
diff --git a/www/nginx/src/opnsense/www/js/nginx/src/models/UpstreamCollection.js b/www/nginx/src/opnsense/www/js/nginx/src/models/UpstreamCollection.js
new file mode 100644
index 000000000..e6ae51120
--- /dev/null
+++ b/www/nginx/src/opnsense/www/js/nginx/src/models/UpstreamCollection.js
@@ -0,0 +1,7 @@
+const UpstreamCollection = Backbone.Collection.extend({
+ url: '/api/nginx/settings/searchupstream',
+ parse: function(response) {
+ return response.rows;
+ }
+});
+export default UpstreamCollection;
\ No newline at end of file
diff --git a/www/nginx/src/opnsense/www/js/nginx/src/nginx_config.js b/www/nginx/src/opnsense/www/js/nginx/src/nginx_config.js
new file mode 100644
index 000000000..a3c9d11f5
--- /dev/null
+++ b/www/nginx/src/opnsense/www/js/nginx/src/nginx_config.js
@@ -0,0 +1,151 @@
+import KeyValueMapField from './controller/KeyValueMapField';
+import UpstreamCollection from './models/UpstreamCollection';
+import {
+ KeyValueMapFieldEntryACL,
+ KeyValueMapFieldEntryUpstreamMap} from "./controller/KeyValueMapFieldEntry";
+import SNIHostnameUpstreamCollection from "./models/SNIHostnameUpstreamCollection";
+import SNIHostnameUpstreamModel from "./models/SNIHostnameUpstreamModel";
+import IPACLModel from "./models/IPACLModel";
+import IPACLCollection from "./models/IPACLCollection";
+
+const uc = new UpstreamCollection();
+const actioncollection = new Backbone.Collection([
+ {
+ 'name': 'Deny',
+ 'value': 'deny'
+ },
+ {
+ 'name': 'Allow',
+ 'value': 'allow'
+ }
+]);
+
+function bind_save_buttons() {
+// form save event handlers for all defined forms
+ $('[id*="save_"]').each(function () {
+ $(this).click(function (event) {
+ let frm_id = $(this).closest("form").attr("id");
+ let frm_title = $(this).closest("form").attr("data-title");
+ // save data for General TAB
+ saveFormToEndpoint(url = "/api/nginx/settings/set", formid = frm_id, callback_ok = function () {
+ // on correct save, perform reconfigure. set progress animation when reloading
+ $("#" + frm_id + "_progress").addClass("fa fa-spinner fa-pulse");
+
+ ajaxCall(url = "/api/nginx/service/reconfigure", sendData = {}, callback = function (data, status) {
+ // when done, disable progress animation.
+ $("#" + frm_id + "_progress").removeClass("fa fa-spinner fa-pulse");
+
+ if (data !== undefined && (status !== "success" || data['status'] !== 'ok')) {
+ // fix error handling
+ BootstrapDialog.show({
+ type: BootstrapDialog.TYPE_WARNING,
+ title: frm_title,
+ message: JSON.stringify(data),
+ draggable: true
+ });
+ } else {
+ updateServiceControlUI('nginx');
+ }
+ });
+ });
+ });
+ });
+}
+
+function init_grids() {
+ ['upstream',
+ 'upstreamserver',
+ 'location',
+ 'credential',
+ 'userlist',
+ 'httpserver',
+ 'streamserver',
+ 'httprewrite',
+ 'custompolicy',
+ 'security_header',
+ 'ipacl',
+ 'limit_zone',
+ 'cache_path',
+ 'limit_request_connection',
+ 'snifwd',
+ 'naxsirule'].forEach(function (element) {
+ $("#grid-" + element).UIBootgrid(
+ {
+ 'search': '/api/nginx/settings/search' + element,
+ 'get': '/api/nginx/settings/get' + element + '/',
+ 'set': '/api/nginx/settings/set' + element + '/',
+ 'add': '/api/nginx/settings/add' + element + '/',
+ 'del': '/api/nginx/settings/del' + element + '/',
+ 'options': {selection: false, multiSelect: false}
+ }
+ );
+ });
+}
+
+function initSNIFieldComponent() {
+ let snifield = new KeyValueMapField({
+ dataField: document.getElementById('snihostname.data'),
+ upstreamCollection: uc,
+ entryclass: KeyValueMapFieldEntryUpstreamMap,
+ collection: new SNIHostnameUpstreamCollection(),
+ createModel: function () {
+ return new SNIHostnameUpstreamModel({
+ hostname: 'localhost',
+ });
+ }
+ });
+ window.snifield = snifield;
+ snifield.render();
+ $("#grid-upstream").on("loaded.rs.jquery.bootgrid", function () {
+ /* we always have to reload too after bootgrid reloads */
+ uc.fetch();
+ });
+ uc.fetch();
+}
+
+$( document ).ready(function() {
+
+ let data_get_map = {'frm_nginx':'/api/nginx/settings/get'};
+
+ // load initial data
+ mapDataToFormUI(data_get_map).done(function(){
+ formatTokenizersUI();
+ $('select[data-allownew="false"]').selectpicker('refresh');
+ updateServiceControlUI('nginx');
+ });
+
+ // update history on tab state and implement navigation
+ if(window.location.hash !== "") {
+ $('a[href="' + window.location.hash + '"]').click()
+ }
+ $('.nav-tabs a').on('shown.bs.tab', function (e) {
+ history.pushState(null, null, e.target.hash);
+ });
+
+ $('.reload_btn').click(function() {
+ $(".reloadAct_progress").addClass("fa-spin");
+ ajaxCall(url="/api/nginx/service/reconfigure", sendData={}, callback=function(data,status) {
+ $(".reloadAct_progress").removeClass("fa-spin");
+ });
+ });
+
+
+ bind_save_buttons();
+ init_grids();
+ bind_naxsi_rule_dl_button();
+ initSNIFieldComponent();
+ let ipaclfield = new KeyValueMapField({
+ dataField: document.getElementById('ipacl.data'),
+ upstreamCollection: actioncollection,
+ entryclass: KeyValueMapFieldEntryACL,
+ collection: new IPACLCollection(),
+ createModel: function () {
+ return new IPACLModel({
+ network: '::',
+ action: 'deny'
+ });
+ }
+ });
+ window.ipaclfield = ipaclfield;
+ ipaclfield.render();
+});
diff --git a/www/nginx/src/opnsense/www/js/nginx/webpack.conf.js b/www/nginx/src/opnsense/www/js/nginx/webpack.conf.js
index 09244d3bf..c28a654fd 100644
--- a/www/nginx/src/opnsense/www/js/nginx/webpack.conf.js
+++ b/www/nginx/src/opnsense/www/js/nginx/webpack.conf.js
@@ -1,10 +1,13 @@
const path = require('path'), webpack = require('webpack');
module.exports = {
- entry: './src/logviewer.js',
+ entry: {
+ 'logviewer': './src/logviewer.js',
+ 'configuration': './src/nginx_config.js'
+ },
output: {
path: path.resolve(__dirname, 'dist'),
- filename: 'bundle.js'
+ filename: '[name].js'
},
mode: 'production',
module: {