From cfa70932c3b80c116144fc95ecb93dd7ffed4366 Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Tue, 7 Jan 2020 19:31:04 +0100 Subject: luci-app-upnp: convert to client side implementation Implement luci.upnp ubus app. Convert lua page to js client side implementation. Signed-off-by: Ansuel Smith --- .../resources/view/status/include/80_upnp.js | 72 ++++++++ .../htdocs/luci-static/resources/view/upnp/upnp.js | 201 +++++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 applications/luci-app-upnp/htdocs/luci-static/resources/view/status/include/80_upnp.js create mode 100644 applications/luci-app-upnp/htdocs/luci-static/resources/view/upnp/upnp.js (limited to 'applications/luci-app-upnp/htdocs/luci-static') diff --git a/applications/luci-app-upnp/htdocs/luci-static/resources/view/status/include/80_upnp.js b/applications/luci-app-upnp/htdocs/luci-static/resources/view/status/include/80_upnp.js new file mode 100644 index 000000000..b1a2531c8 --- /dev/null +++ b/applications/luci-app-upnp/htdocs/luci-static/resources/view/status/include/80_upnp.js @@ -0,0 +1,72 @@ +'use strict'; +'require rpc'; +'require uci'; + +var callUpnpGetStatus, callUpnpDeleteRule, handleDelRule; + +callUpnpGetStatus = rpc.declare({ + object: 'luci.upnp', + method: 'get_status', + expect: { } +}); + +callUpnpDeleteRule = rpc.declare({ + object: 'luci.upnp', + method: 'delete_rule', + params: [ 'token' ], + expect: { result : "OK" }, +}); + +handleDelRule = function(num, ev) { + L.dom.parent(ev.currentTarget, '.tr').style.opacity = 0.5; + ev.currentTarget.classList.add('spinning'); + ev.currentTarget.disabled = true; + ev.currentTarget.blur(); + callUpnpDeleteRule(num); +}; + +return L.Class.extend({ + title: _('Active UPnP Redirects'), + + load: function() { + return Promise.all([ + callUpnpGetStatus(), + ]); + }, + + render: function(data) { + + var table = E('div', { 'class': 'table', 'id': 'upnp_status_table' }, [ + E('div', { 'class': 'tr table-titles' }, [ + E('div', { 'class': 'th' }, _('Protocol')), + E('div', { 'class': 'th' }, _('External Port')), + E('div', { 'class': 'th' }, _('Client Address')), + E('div', { 'class': 'th' }, _('Host')), + E('div', { 'class': 'th' }, _('Client Port')), + E('div', { 'class': 'th' }, _('Description')), + E('div', { 'class': 'th cbi-section-actions' }, '') + ]) + ]); + + var rules = Array.isArray(data[0].rules) ? data[0].rules : []; + + var rows = rules.map(function(rule) { + return [ + rule.proto, + rule.extport, + rule.intaddr, + rule.host_hint || _('Unknown'), + rule.intport, + rule.descr, + E('button', { + 'class': 'btn cbi-button-remove', + 'click': L.bind(handleDelRule, this, rule.num) + }, [ _('Delete') ]) + ]; + }); + + cbi_update_table(table, rows, E('em', _('There are no active redirects.'))); + + return table; + } +}); diff --git a/applications/luci-app-upnp/htdocs/luci-static/resources/view/upnp/upnp.js b/applications/luci-app-upnp/htdocs/luci-static/resources/view/upnp/upnp.js new file mode 100644 index 000000000..b85fd6d9d --- /dev/null +++ b/applications/luci-app-upnp/htdocs/luci-static/resources/view/upnp/upnp.js @@ -0,0 +1,201 @@ +'use strict'; +'require uci'; +'require rpc'; +'require form'; + +var callInitAction, callUpnpGetStatus, callUpnpDeleteRule, handleDelRule; + +callInitAction = rpc.declare({ + object: 'luci', + method: 'setInitAction', + params: [ 'name', 'action' ], + expect: { result: false } +}); + +callUpnpGetStatus = rpc.declare({ + object: 'luci.upnp', + method: 'get_status', + expect: { } +}); + +callUpnpDeleteRule = rpc.declare({ + object: 'luci.upnp', + method: 'delete_rule', + params: [ 'token' ], + expect: { result : "OK" }, +}); + +handleDelRule = function(num, ev) { + L.dom.parent(ev.currentTarget, '.tr').style.opacity = 0.5; + ev.currentTarget.classList.add('spinning'); + ev.currentTarget.disabled = true; + ev.currentTarget.blur(); + callUpnpDeleteRule(num); +}; + +return L.view.extend({ + load: function() { + return Promise.all([ + callUpnpGetStatus(), + uci.load('upnpd') + ]); + }, + + poll_status: function(nodes, data) { + + var rules = Array.isArray(data[0].rules) ? data[0].rules : []; + + var rows = rules.map(function(rule) { + return [ + rule.proto, + rule.extport, + rule.intaddr, + rule.host_hint || _('Unknown'), + rule.intport, + rule.descr, + E('button', { + 'class': 'btn cbi-button-remove', + 'click': L.bind(handleDelRule, this, rule.num) + }, [ _('Delete') ]) + ]; + }); + + cbi_update_table(nodes.querySelector('#upnp_status_table'), rows, E('em', _('There are no active redirects.'))); + + return; + }, + + render: function(data) { + + var m, s, o; + + m = new form.Map('upnpd', _('Universal Plug & Play'), + _('UPnP allows clients in the local network to automatically configure the router.')); + + s = m.section(form.GridSection, '_active_rules'); + + s.render = L.bind(function(view, section_id) { + var table = E('div', { 'class': 'table cbi-section-table', 'id': 'upnp_status_table' }, [ + E('div', { 'class': 'tr table-titles' }, [ + E('div', { 'class': 'th' }, _('Protocol')), + E('div', { 'class': 'th' }, _('External Port')), + E('div', { 'class': 'th' }, _('Client Address')), + E('div', { 'class': 'th' }, _('Host')), + E('div', { 'class': 'th' }, _('Client Port')), + E('div', { 'class': 'th' }, _('Description')), + E('div', { 'class': 'th cbi-section-actions' }, '') + ]) + ]); + + var rules = Array.isArray(data[0].rules) ? data[0].rules : []; + + var rows = rules.map(function(rule) { + return [ + rule.proto, + rule.extport, + rule.intaddr, + rule.host_hint || _('Unknown'), + rule.intport, + rule.descr, + E('button', { + 'class': 'btn cbi-button-remove', + 'click': L.bind(handleDelRule, this, rule.num) + }, [ _('Delete') ]) + ]; + }); + + cbi_update_table(table, rows, E('em', _('There are no active redirects.'))); + + return E('div', { 'class': 'cbi-section cbi-tblsection' }, [ + E('h3', _('Active UPnP Redirects')), table ]); + }, o, this); + + s = m.section(form.NamedSection, 'config', 'upnpd', _('MiniUPnP settings')); + s.addremove = false; + s.tab('general', _('General Settings')); + s.tab('advanced', _('Advanced Settings')); + + o = s.taboption('general', form.Flag, 'enabled', _('Start UPnP and NAT-PMP service')); + o.rmempty = false; + + s.taboption('general', form.Flag, 'enable_upnp', _('Enable UPnP functionality')).default = '1' + s.taboption('general', form.Flag, 'enable_natpmp', _('Enable NAT-PMP functionality')).default = '1' + + s.taboption('general', form.Flag, 'secure_mode', _('Enable secure mode'), + _('Allow adding forwards only to requesting ip addresses')).default = '1' + + s.taboption('general', form.Flag, 'igdv1', _('Enable IGDv1 mode'), + _('Advertise as IGDv1 device instead of IGDv2')).default = '0' + + s.taboption('general', form.Flag, 'log_output', _('Enable additional logging'), + _('Puts extra debugging information into the system log')) + + s.taboption('general', form.Value, 'download', _('Downlink'), + _('Value in KByte/s, informational only')).rmempty = true + + s.taboption('general', form.Value, 'upload', _('Uplink'), + _('Value in KByte/s, informational only')).rmempty = true + + o = s.taboption('general', form.Value, 'port', _('Port')) + o.datatype = 'port' + o.default = 5000 + + s.taboption('advanced', form.Flag, 'system_uptime', _('Report system instead of daemon uptime')).default = '1' + + s.taboption('advanced', form.Value, 'uuid', _('Device UUID')) + s.taboption('advanced', form.Value, 'serial_number', _('Announced serial number')) + s.taboption('advanced', form.Value, 'model_number', _('Announced model number')) + + o = s.taboption('advanced', form.Value, 'notify_interval', _('Notify interval')) + o.datatype = 'uinteger' + o.placeholder = 30 + + o = s.taboption('advanced', form.Value, 'clean_ruleset_threshold', _('Clean rules threshold')) + o.datatype = 'uinteger' + o.placeholder = 20 + + o = s.taboption('advanced', form.Value, 'clean_ruleset_interval', _('Clean rules interval')) + o.datatype = 'uinteger' + o.placeholder = 600 + + o = s.taboption('advanced', form.Value, 'presentation_url', _('Presentation URL')) + o.placeholder = 'http://192.168.1.1/' + + o = s.taboption('advanced', form.Value, 'upnp_lease_file', _('UPnP lease file')) + o.placeholder = '/var/run/miniupnpd.leases' + + s = m.section(form.GridSection, 'perm_rule', _('MiniUPnP ACLs'), + _('ACLs specify which external ports may be redirected to which internal addresses and ports')) + + s.sortable = true + s.anonymous = true + s.addremove = true + + s.option(form.Value, 'comment', _('Comment')) + + o = s.option(form.Value, 'ext_ports', _('External ports')) + o.datatype = 'portrange' + o.placeholder = '0-65535' + + o = s.option(form.Value, 'int_addr', _('Internal addresses')) + o.datatype = 'ip4addr' + o.placeholder = '0.0.0.0/0' + + o = s.option(form.Value, 'int_ports', _('Internal ports')) + o.datatype = 'portrange' + o.placeholder = '0-65535' + + o = s.option(form.ListValue, 'action', _('Action')) + o.value('allow') + o.value('deny') + + return m.render().then(L.bind(function(m, nodes) { + L.Poll.add(L.bind(function() { + return Promise.all([ + callUpnpGetStatus() + ]).then(L.bind(this.poll_status, this, nodes)); + }, this), 5); + return nodes; + }, this, m)); + } +}); -- cgit v1.2.3