luci-app-v2raya: add new package
authorTianling Shen <cnsztl@immortalwrt.org>
Wed, 24 Jan 2024 17:03:44 +0000 (01:03 +0800)
committerPaul Donald <itsascambutmailmeanyway@gmail.com>
Fri, 26 Jan 2024 00:07:51 +0000 (01:07 +0100)
Add LuCI interface for the v2rayA package.

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
applications/luci-app-v2raya/Makefile [new file with mode: 0644]
applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js [new file with mode: 0644]
applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/log.js [new file with mode: 0644]
applications/luci-app-v2raya/po/templates/v2raya.pot [new file with mode: 0644]
applications/luci-app-v2raya/root/usr/share/luci/menu.d/luci-app-v2raya.json [new file with mode: 0644]
applications/luci-app-v2raya/root/usr/share/rpcd/acl.d/luci-app-v2raya.json [new file with mode: 0644]

diff --git a/applications/luci-app-v2raya/Makefile b/applications/luci-app-v2raya/Makefile
new file mode 100644 (file)
index 0000000..0b37120
--- /dev/null
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: Apache-2.0
+#
+# Copyright (C) 2024 ImmortalWrt.org
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=LuCI support for v2rayA
+LUCI_DEPENDS:=+v2raya
+
+PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js b/applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js
new file mode 100644 (file)
index 0000000..577d0ee
--- /dev/null
@@ -0,0 +1,110 @@
+'use strict';
+'require form';
+'require poll';
+'require rpc';
+'require uci';
+'require view';
+
+var callServiceList = rpc.declare({
+       object: 'service',
+       method: 'list',
+       params: ['name'],
+       expect: { '': {} }
+});
+
+function getServiceStatus() {
+       return L.resolveDefault(callServiceList('v2raya'), {}).then(function (res) {
+               var isRunning = false;
+               try {
+                       isRunning = res['v2raya']['instances']['v2raya']['running'];
+               } catch (e) { }
+               return isRunning;
+       });
+}
+
+function renderStatus(isRunning, port) {
+       var spanTemp = '<span style="color:%s"><strong>%s %s</strong></span>';
+       var renderHTML;
+       if (isRunning) {
+               var button = String.format('&#160;<a class="btn cbi-button" href="http://%s:%s" target="_blank" rel="noreferrer noopener">%s</a>',
+                       window.location.hostname, port, _('Open Web Interface'));
+               renderHTML = spanTemp.format('green', _('v2rayA'), _('RUNNING')) + button;
+       } else {
+               renderHTML = spanTemp.format('red', _('v2rayA'), _('NOT RUNNING'));
+       }
+
+       return renderHTML;
+}
+
+return view.extend({
+       load: function() {
+               return Promise.all([
+                       uci.load('v2raya')
+               ]);
+       },
+
+       render: function(data) {
+               var m, s, o;
+               var webport = (uci.get(data[0], 'config', 'address') || '0.0.0.0:2017').split(':').slice(-1)[0];
+
+               m = new form.Map('v2raya', _('v2rayA'),
+                       _('v2rayA is a V2Ray Linux client supporting global transparent proxy, compatible with SS, SSR, Trojan(trojan-go), PingTunnel protocols.'));
+
+               s = m.section(form.TypedSection);
+               s.anonymous = true;
+               s.render = function () {
+                       poll.add(function () {
+                               return L.resolveDefault(getServiceStatus()).then(function (res) {
+                                       var view = document.getElementById('service_status');
+                                       view.innerHTML = renderStatus(res, webport);
+                               });
+                       });
+
+                       return E('div', { class: 'cbi-section', id: 'status_bar' }, [
+                                       E('p', { id: 'service_status' }, _('Collecting data…'))
+                       ]);
+               }
+
+               s = m.section(form.NamedSection, 'config', 'v2raya');
+
+               o = s.option(form.Flag, 'enabled', _('Enable'));
+               o.rmempty = false;
+
+               o = s.option(form.Value, 'address', _('Listening address'));
+               o.datatype = 'ipaddrport(1)';
+               o.placeholder = '0.0.0.0:2017';
+
+               o = s.option(form.ListValue, 'ipv6_support', _('IPv6 support'),
+                       _('Requires working IPv6 connectivity.'));
+               o.value('auto', _('Auto'));
+               o.value('on', _('On'));
+               o.value('off', _('Off'));
+               o.default = 'auto';
+
+               o = s.option(form.ListValue, 'nftables_support', _('Nftables support'),
+                       _('Requires nftables.'));
+               o.value('auto', _('Auto'));
+               o.value('on', _('On'));
+               o.value('off', _('Off'));
+               o.default = 'auto';
+
+               o = s.option(form.ListValue, 'log_level', _('Log level'));
+               o.value('trace', _('Trace'));
+               o.value('debug', _('Debug'));
+               o.value('info', _('Info'));
+               o.value('warn', _('Warn'));
+               o.value('error', _('Error'));
+               o.default = 'info';
+
+               o = s.option(form.Value, 'log_max_days', _('Max log retention period'),
+                       _('Unit: days.'));
+               o.datatype = 'uinteger';
+               o.placeholder = '3';
+
+               o = s.option(form.Flag, 'log_disable_color', _('Disable log color output'));
+
+               o = s.option(form.Flag, 'log_disable_timestamp', _('Disable log timestamp'));
+
+               return m.render();
+       }
+});
diff --git a/applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/log.js b/applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/log.js
new file mode 100644 (file)
index 0000000..b614f3c
--- /dev/null
@@ -0,0 +1,75 @@
+'use strict';
+'require dom';
+'require fs';
+'require poll';
+'require uci';
+'require view';
+
+return view.extend({
+       render: function() {
+               /* Thanks to luci-app-aria2 */
+               var css = '                                     \
+                       #log_textarea {                         \
+                               padding: 10px;                  \
+                               text-align: left;               \
+                       }                                       \
+                       #log_textarea pre {                     \
+                               padding: .5rem;                 \
+                               word-break: break-all;          \
+                               margin: 0;                      \
+                       }                                       \
+                       .description {                          \
+                               background-color: #33ccff;      \
+                       }';
+
+               var log_textarea = E('div', { 'id': 'log_textarea' },
+                       E('img', {
+                               'src': L.resource(['icons/loading.gif']),
+                               'alt': _('Loading…'),
+                               'style': 'vertical-align:middle'
+                       }, _('Collecting data…'))
+               );
+
+               var log_path = '/var/log/v2raya/v2raya.log';
+
+               poll.add(L.bind(function() {
+                       return fs.read_direct(log_path, 'text')
+                       .then(function(res) {
+                               var log = E('pre', { 'wrap': 'pre' }, [
+                                       res.trim() || _('Log is clean.')
+                               ]);
+
+                               dom.content(log_textarea, log);
+                       }).catch(function(err) {
+                               var log;
+
+                               if (err.toString().includes('NotFoundError'))
+                                       log = E('pre', { 'wrap': 'pre' }, [
+                                               _('Log file does not exist.')
+                                       ]);
+                               else
+                                       log = E('pre', { 'wrap': 'pre' }, [
+                                               _('Unknown error: %s').format(err)
+                                       ]);
+
+                               dom.content(log_textarea, log);
+                       });
+               }));
+
+               return E([
+                       E('style', [ css ]),
+                       E('div', {'class': 'cbi-map'}, [
+                               E('div', {'class': 'cbi-section'}, [
+                                       log_textarea,
+                                       E('div', {'style': 'text-align:right'},
+                                       E('small', {}, _('Refresh every %d seconds.').format(L.env.pollinterval))
+                                       )
+                               ])
+                       ])
+               ]);
+       },
+
+       handleSaveApply: null,
+       handleSave: null,
+       handleReset: null
+});
diff --git a/applications/luci-app-v2raya/po/templates/v2raya.pot b/applications/luci-app-v2raya/po/templates/v2raya.pot
new file mode 100644 (file)
index 0000000..8e224db
--- /dev/null
@@ -0,0 +1,143 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:79
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:86
+msgid "Auto"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:64
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/log.js:30
+msgid "Collecting data…"
+msgstr ""
+
+#: applications/luci-app-v2raya/root/usr/share/luci/menu.d/luci-app-v2raya.json:13
+msgid "Configuration"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:93
+msgid "Debug"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:104
+msgid "Disable log color output"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:106
+msgid "Disable log timestamp"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:70
+msgid "Enable"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:96
+msgid "Error"
+msgstr ""
+
+#: applications/luci-app-v2raya/root/usr/share/rpcd/acl.d/luci-app-v2raya.json:3
+msgid "Grant access to v2rayA configuration"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:77
+msgid "IPv6 support"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:94
+msgid "Info"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:73
+msgid "Listening address"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/log.js:28
+msgid "Loading…"
+msgstr ""
+
+#: applications/luci-app-v2raya/root/usr/share/luci/menu.d/luci-app-v2raya.json:21
+msgid "Log"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/log.js:48
+msgid "Log file does not exist."
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/log.js:39
+msgid "Log is clean."
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:91
+msgid "Log level"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:99
+msgid "Max log retention period"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:33
+msgid "NOT RUNNING"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:84
+msgid "Nftables support"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:81
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:88
+msgid "Off"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:80
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:87
+msgid "On"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:30
+msgid "Open Web Interface"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:31
+msgid "RUNNING"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/log.js:65
+msgid "Refresh every %d seconds."
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:85
+msgid "Requires nftables."
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:78
+msgid "Requires working IPv6 connectivity."
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:92
+msgid "Trace"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:100
+msgid "Unit: days."
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/log.js:52
+msgid "Unknown error: %s"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:95
+msgid "Warn"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:31
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:33
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:50
+#: applications/luci-app-v2raya/root/usr/share/luci/menu.d/luci-app-v2raya.json:3
+msgid "v2rayA"
+msgstr ""
+
+#: applications/luci-app-v2raya/htdocs/luci-static/resources/view/v2raya/config.js:51
+msgid ""
+"v2rayA is a V2Ray Linux client supporting global transparent proxy, "
+"compatible with SS, SSR, Trojan(trojan-go), PingTunnel protocols."
+msgstr ""
diff --git a/applications/luci-app-v2raya/root/usr/share/luci/menu.d/luci-app-v2raya.json b/applications/luci-app-v2raya/root/usr/share/luci/menu.d/luci-app-v2raya.json
new file mode 100644 (file)
index 0000000..564bcef
--- /dev/null
@@ -0,0 +1,28 @@
+{
+       "admin/services/v2raya": {
+               "title": "v2rayA",
+               "action": {
+                       "type": "firstchild"
+               },
+               "depends": {
+                       "acl": [ "luci-app-v2raya" ],
+                       "uci": { "v2raya": true }
+               }
+       },
+       "admin/services/v2raya/config": {
+               "title": "Configuration",
+               "order": 10,
+               "action": {
+                       "type": "view",
+                       "path": "v2raya/config"
+               }
+       },
+       "admin/services/v2raya/log": {
+               "title": "Log",
+               "order": 20,
+               "action": {
+                       "type": "view",
+                       "path": "v2raya/log"
+               }
+       }
+}
diff --git a/applications/luci-app-v2raya/root/usr/share/rpcd/acl.d/luci-app-v2raya.json b/applications/luci-app-v2raya/root/usr/share/rpcd/acl.d/luci-app-v2raya.json
new file mode 100644 (file)
index 0000000..173296e
--- /dev/null
@@ -0,0 +1,17 @@
+{
+       "luci-app-v2raya": {
+               "description": "Grant access to v2rayA configuration",
+               "read": {
+                       "file": {
+                               "/var/log/v2raya/v2raya.log": [ "read" ]
+                       },
+                       "ubus": {
+                               "service": [ "list" ]
+                       },
+                       "uci": [ "v2raya" ]
+               },
+               "write": {
+                       "uci": [ "v2raya" ]
+               }
+       }
+}