diff --git a/luci-app-alist/Makefile b/luci-app-alist/Makefile index e23ae803..9edcd81d 100644 --- a/luci-app-alist/Makefile +++ b/luci-app-alist/Makefile @@ -6,11 +6,19 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-alist -PKG_VERSION:=1.1.0 +PKG_VERSION:=1.0.13 PKG_RELEASE:=1 LUCI_TITLE:=LuCI support for alist -LUCI_DEPENDS:=+alist +LUCI_DEPENDS:=+alist +luci-compat + +define Package/$(PKG_NAME)/postinst +#!/bin/sh +[ -n "${IPKG_INSTROOT}" ] || { + ( . /etc/uci-defaults/50-luci-alist ) && rm -f /etc/uci-defaults/50-luci-alist + exit 0 +} +endef include $(TOPDIR)/feeds/luci/luci.mk diff --git a/luci-app-alist/htdocs/luci-static/resources/view/alist/basic.js b/luci-app-alist/htdocs/luci-static/resources/view/alist/basic.js deleted file mode 100644 index bb40c41c..00000000 --- a/luci-app-alist/htdocs/luci-static/resources/view/alist/basic.js +++ /dev/null @@ -1,199 +0,0 @@ -'use strict'; -'require form'; -'require fs'; -'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('alist'), {}).then(function (res) { - var isRunning = false; - try { - isRunning = res['alist']['instances']['alist']['running']; - } catch (e) { } - return isRunning; - }); -} - -function renderStatus(isRunning, protocol, webport) { - var spanTemp = '%s %s'; - var renderHTML; - if (isRunning) { - var button = String.format('', - _('Open Web Interface'), protocol, window.location.hostname, webport); - renderHTML = spanTemp.format('green', 'Alist', _('RUNNING')) + button; - } else { - renderHTML = spanTemp.format('red', 'Alist', _('NOT RUNNING')); - } - - return renderHTML; -} - -return view.extend({ - load: function () { - return Promise.all([ - uci.load('alist') - ]); - }, - - handleResetPassword: async function (data) { - var data_dir = uci.get(data[0], '@alist[0]', 'data_dir') || '/etc/alist'; - try { - var newpassword = await fs.exec('/usr/bin/alist', ['admin', 'random', '--data', data_dir]); - var new_password = newpassword.stderr.match(/password:\s*(\S+)/)[1]; - const textArea = document.createElement('textarea'); - textArea.value = new_password; - document.body.appendChild(textArea); - textArea.select(); - document.execCommand('copy'); - document.body.removeChild(textArea); - alert(_('Username:') + 'admin\n' + _('New Password:') + new_password + '\n\n' + _('New password has been copied to clipboard.')); - } catch (error) { - console.error('Failed to reset password: ', error); - } - }, - - render: function (data) { - var m, s, o; - var webport = uci.get(data[0], '@alist[0]', 'port') || '5244'; - var ssl = uci.get(data[0], '@alist[0]', 'ssl') || '0'; - var protocol; - if (ssl === '0') { - protocol = 'http:'; - } else if (ssl === '1') { - protocol = 'https:'; - } - - m = new form.Map('alist', _('Alist'), - _('A file list program that supports multiple storage.') + - '
' + - _('User Manual') + - ''); - - s = m.section(form.TypedSection); - s.anonymous = true; - s.addremove = false; - - s.render = function () { - poll.add(function () { - return L.resolveDefault(getServiceStatus()).then(function (res) { - var view = document.getElementById('service_status'); - view.innerHTML = renderStatus(res, protocol, webport); - }); - }); - - return E('div', { class: 'cbi-section', id: 'status_bar' }, [ - E('p', { id: 'service_status' }, _('Collecting data...')) - ]); - } - - s = m.section(form.NamedSection, '@alist[0]', 'alist'); - - o = s.option(form.Flag, 'enabled', _('Enabled')); - o.default = o.disabled; - o.rmempty = false; - - o = s.option(form.Value, 'port', _('Port')); - o.datatype = 'and(port,min(1))'; - o.default = '5244'; - o.rmempty = false; - - o = s.option(form.Flag, 'log', _('Enable Logs')); - o.default = 1; - o.rmempty = false; - - o = s.option(form.Flag, 'ssl', _('Enable SSL')); - o.rmempty = false; - - o = s.option(form.Value, 'ssl_cert', _('SSL cert'), - _('SSL certificate file path')); - o.rmempty = false; - o.depends('ssl', '1'); - - o = s.option(form.Value, 'ssl_key', _('SSL key'), - _('SSL key file path')); - o.rmempty = false; - o.depends('ssl', '1'); - - o = s.option(form.Flag, 'mysql', _('Enable Database')); - o.rmempty = false; - - o = s.option(form.ListValue, 'mysql_type', _('Database Type')); - o.default = 'mysql'; - o.depends('mysql', '1'); - o.value('mysql', _('MySQL')); - o.value('postgres', _('PostgreSQL')); - - o = s.option(form.Value, 'mysql_host', _('Database Host')); - o.depends('mysql', '1'); - - o = s.option(form.Value, 'mysql_port', _('Database Port')); - o.datatype = 'port'; - o.default = '3306'; - o.depends('mysql', '1'); - - o = s.option(form.Value, 'mysql_username', _('Database Username')); - o.depends('mysql', '1'); - - o = s.option(form.Value, 'mysql_password', _('Database Password')); - o.depends('mysql', '1'); - - o = s.option(form.Value, 'mysql_database', _('Database Name')); - o.depends('mysql', '1'); - - o = s.option(form.Value, 'mysql_table_prefix', _('Database Table Prefix')); - o.default = 'x_'; - o.depends('mysql', '1'); - - o = s.option(form.Value, 'mysql_ssl_mode', _('Database SSL Mode')); - o.depends('mysql', '1'); - - o = s.option(form.Value, 'mysql_dsn', _('Database DSN')); - o.depends('mysql', '1'); - - o = s.option(form.Flag, 'allow_wan', _('Allow Access From Internet')); - o.rmempty = false; - - o = s.option(form.Value, 'site_url', _('Site URL'), - _('When the web is reverse proxied to a subdirectory, this option must be filled out to ensure proper functioning of the web. Do not include \'/\' at the end of the URL')); - - o = s.option(form.Value, 'max_connections', _('Max Connections'), - _('0 is unlimited, It is recommend to set a low number of concurrency (10-20) for poor performance device')); - o.default = '0'; - o.datatype = 'uinteger'; - o.rmempty = false; - - o = s.option(form.Value, 'token_expires_in', _('Login Validity Period (hours)')); - o.datatype = 'uinteger'; - o.default = '48'; - o.rmempty = false; - - o = s.option(form.Value, 'delayed_start', _('Delayed Start (seconds)')); - o.datatype = 'uinteger'; - o.default = '0'; - o.rmempty = false; - - o = s.option(form.Value, 'data_dir', _('Data directory')); - o.default = '/etc/alist'; - - o = s.option(form.Value, 'temp_dir', _('Cache directory')); - o.default = '/tmp/alist'; - o.rmempty = false; - - o = s.option(form.Button, '_newpassword', _('Reset Password'), - _('Generate a new random password.')); - o.inputtitle = _('Reset Password'); - o.inputstyle = 'apply'; - o.onclick = L.bind(this.handleResetPassword, this, data); - - return m.render(); - } -}); diff --git a/luci-app-alist/htdocs/luci-static/resources/view/alist/logs.js b/luci-app-alist/htdocs/luci-static/resources/view/alist/logs.js deleted file mode 100644 index 6077c3c3..00000000 --- a/luci-app-alist/htdocs/luci-static/resources/view/alist/logs.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict'; -'require dom'; -'require fs'; -'require poll'; -'require view'; - -function pollLog(e) { - return Promise.all([ - fs.read_direct('/var/log/alist.log', 'text').then(function (res) { - return res.trim().split(/\n/).join('\n').replace(/\u001b\[33mWARN\u001b\[0m/g, '').replace(/\u001b\[36mINFO\u001b\[0m/g, ''); - }), - ]).then(function (data) { - var logTextarea = E('textarea', { 'class': 'cbi-input-textarea', 'wrap': 'off', 'readonly': 'readonly', 'style': 'width: calc(100% - 20px);height: 500px;margin: 10px;overflow-y: scroll;' }, [ - data[0] || _('No log data.') - ]); - - // Store the current scroll position - var storedScrollTop = e.querySelector('textarea') ? e.querySelector('textarea').scrollTop : null; - - dom.content(e, logTextarea); - - // If the storedScrollTop is not null, it means we have a previous scroll position - if (storedScrollTop !== null) { - logTextarea.scrollTop = storedScrollTop; - } - - // Add event listener to save the scroll position when scrolling stops - var timer; - logTextarea.addEventListener('scroll', function () { - clearTimeout(timer); - timer = setTimeout(function () { - storeScrollPosition(logTextarea.scrollTop); - }, 150); - }); - - function storeScrollPosition(scrollPos) { - localStorage.setItem("scrollPosition", JSON.stringify({ "log": scrollPos })); - } - - }); -}; - -return view.extend({ - handleCleanLogs: function () { - return fs.write('/var/log/alist.log', '') - .catch(function (e) { ui.addNotification(null, E('p', e.message)) }); - }, - - render: function () { - var log_textarea = E('div', { 'id': 'log_textarea' }, - E('img', { - 'src': L.resource(['icons/loading.gif']), - 'alt': _('Loading'), - 'style': 'vertical-align:middle' - }, _('Collecting data...')) - ); - - poll.add(pollLog.bind(this, log_textarea)); - var clear_logs_button = E('input', { 'class': 'btn cbi-button-action', 'type': 'button', 'style': 'margin-left: 10px; margin-top: 10px;', 'value': _('Clear logs') }); - clear_logs_button.addEventListener('click', this.handleCleanLogs.bind(this)); - return E([ - E('div', { 'class': 'cbi-map' }, [ - E('div', { 'class': 'cbi-section' }, [ - clear_logs_button, - log_textarea, - E('div', { 'style': 'text-align:right' }, - E('small', {}, _('Refresh every %s seconds.').format(L.env.pollinterval)) - ) - ])]) - ]); - }, - - handleSave: null, - handleSaveApply: null, - handleReset: null -}); diff --git a/luci-app-alist/luasrc/controller/alist.lua b/luci-app-alist/luasrc/controller/alist.lua new file mode 100644 index 00000000..e664683c --- /dev/null +++ b/luci-app-alist/luasrc/controller/alist.lua @@ -0,0 +1,50 @@ +module("luci.controller.alist", package.seeall) + +function index() + if not nixio.fs.access("/etc/config/alist") then + return + end + + local page = entry({"admin", "nas", "alist"}, alias("admin", "nas", "alist", "basic"), _("Alist"), 20) + page.dependent = true + page.acl_depends = { "luci-app-alist" } + + entry({"admin", "nas"}, firstchild(), "NAS", 44).dependent = false + entry({"admin", "nas", "alist", "basic"}, cbi("alist/basic"), _("Basic Setting"), 1).leaf = true + entry({"admin", "nas", "alist", "log"}, cbi("alist/log"), _("Logs"), 2).leaf = true + entry({"admin", "nas", "alist", "alist_status"}, call("alist_status")).leaf = true + entry({"admin", "nas", "alist", "get_log"}, call("get_log")).leaf = true + entry({"admin", "nas", "alist", "clear_log"}, call("clear_log")).leaf = true + entry({"admin", "nas", "alist", "admin_info"}, call("admin_info")).leaf = true +end + +function alist_status() + local sys = require "luci.sys" + local uci = require "luci.model.uci".cursor() + local port = tonumber(uci:get_first("alist", "alist", "port")) + + local status = { + running = (sys.call("pidof alist >/dev/null") == 0), + port = (port or 5244) + } + + luci.http.prepare_content("application/json") + luci.http.write_json(status) +end + +function get_log() + luci.http.write(luci.sys.exec("cat /var/log/alist.log")) +end + +function clear_log() + luci.sys.call("cat /dev/null > /var/log/alist.log") +end + +function admin_info() + local random = luci.sys.exec("/usr/bin/alist --data $(uci -q get alist.@alist[0].data_dir) admin random 2>&1") + local username = string.match(random, "username: (%S+)") + local password = string.match(random, "password: (%S+)") + + luci.http.prepare_content("application/json") + luci.http.write_json({username = username, password = password}) +end diff --git a/luci-app-alist/luasrc/model/cbi/alist/basic.lua b/luci-app-alist/luasrc/model/cbi/alist/basic.lua new file mode 100644 index 00000000..ec5587a8 --- /dev/null +++ b/luci-app-alist/luasrc/model/cbi/alist/basic.lua @@ -0,0 +1,113 @@ +local m, s + +m = Map("alist", translate("Alist"), translate("A file list program that supports multiple storage.") .. "
" .. [[]] .. translate("User Manual") .. [[]]) + +m:section(SimpleSection).template = "alist/alist_status" + +s = m:section(TypedSection, "alist") +s.addremove = false +s.anonymous = true + +o = s:option(Flag, "enabled", translate("Enabled")) +o.rmempty = false + +o = s:option(Value, "port", translate("Port")) +o.datatype = "and(port,min(1))" +o.rmempty = false +o.default = "5244" + +o = s:option(Flag, "log", translate("Enable Logs")) +o.default = 1 +o.rmempty = false + +o = s:option(Flag, "ssl", translate("Enable SSL")) +o.rmempty=false + +o = s:option(Value,"ssl_cert", translate("SSL cert"), translate("SSL certificate file path")) +o.datatype = "file" +o:depends("ssl", "1") + +o = s:option(Value,"ssl_key", translate("SSL key"), translate("SSL key file path")) +o.datatype = "file" +o:depends("ssl", "1") + +o = s:option(Flag, "mysql", translate("Enable Database")) +o.rmempty=false + +o = s:option(ListValue, "mysql_type", translate("Database Type")) +o.datatype = "string" +o:value("mysql", translate("MySQL")) +o:value("postgres", translate("PostgreSQL")) +o.default = "mysql" +o:depends("mysql", "1") + +o = s:option(Value,"mysql_host", translate("Database Host")) +o.datatype = "string" +o:depends("mysql", "1") + +o = s:option(Value,"mysql_port", translate("Database Port")) +o.datatype = "and(port,min(1))" +o.default = "3306" +o:depends("mysql", "1") + +o = s:option(Value,"mysql_username", translate("Database Username")) +o.datatype = "string" +o:depends("mysql", "1") + +o = s:option(Value,"mysql_password", translate("Database Password")) +o.datatype = "string" +o.password = true +o:depends("mysql", "1") + +o = s:option(Value,"mysql_database", translate("Database Name")) +o.datatype = "string" +o:depends("mysql", "1") + +o = s:option(Value,"mysql_table_prefix", translate("Database Table Prefix")) +o.datatype = "string" +o.default = "x_" +o:depends("mysql", "1") + +o = s:option(Value,"mysql_ssl_mode", translate("Database SSL Mode")) +o.datatype = "string" +o:depends("mysql", "1") + +o = s:option(Value,"mysql_dsn", translate("Database DSN")) +o.datatype = "string" +o:depends("mysql", "1") + +o = s:option(Flag, "allow_wan", translate("Allow Access From Internet")) +o.rmempty = false + +o = s:option(Value, "site_url", translate("Site URL"), translate("When the web is reverse proxied to a subdirectory, this option must be filled out to ensure proper functioning of the web. Do not include '/' at the end of the URL")) +o.datatype = "string" + +o = s:option(Value, "max_connections", translate("Max Connections"), translate("0 is unlimited, It is recommend to set a low number of concurrency (10-20) for poor performance device")) +o.datatype = "and(uinteger,min(0))" +o.default = "0" +o.rmempty = false + +o = s:option(Value, "token_expires_in", translate("Login Validity Period (hours)")) +o.datatype = "and(uinteger,min(1))" +o.default = "48" +o.rmempty = false + +o = s:option(Value, "delayed_start", translate("Delayed Start (seconds)")) +o.datatype = "and(uinteger,min(0))" +o.default = "0" +o.rmempty = false + +o = s:option(Value, "data_dir", translate("Data directory")) +o.datatype = "string" +o.default = "/etc/alist" + +o = s:option(Value, "temp_dir", translate("Cache directory")) +o.datatype = "string" +o.default = "/tmp/alist" +o.rmempty = false + +o = s:option(Button, "admin_info", translate("Reset Password")) +o.rawhtml = true +o.template = "alist/admin_info" + +return m diff --git a/luci-app-alist/luasrc/model/cbi/alist/log.lua b/luci-app-alist/luasrc/model/cbi/alist/log.lua new file mode 100644 index 00000000..4b6c36f8 --- /dev/null +++ b/luci-app-alist/luasrc/model/cbi/alist/log.lua @@ -0,0 +1,5 @@ +m = Map("alist") + +m:append(Template("alist/alist_log")) + +return m diff --git a/luci-app-alist/luasrc/view/alist/admin_info.htm b/luci-app-alist/luasrc/view/alist/admin_info.htm new file mode 100644 index 00000000..ba34e91b --- /dev/null +++ b/luci-app-alist/luasrc/view/alist/admin_info.htm @@ -0,0 +1,26 @@ +<%+cbi/valueheader%> + + +<%=self.value%> +<%+cbi/valuefooter%> \ No newline at end of file diff --git a/luci-app-alist/luasrc/view/alist/alist_log.htm b/luci-app-alist/luasrc/view/alist/alist_log.htm new file mode 100644 index 00000000..5ec4a78b --- /dev/null +++ b/luci-app-alist/luasrc/view/alist/alist_log.htm @@ -0,0 +1,35 @@ + +
+ + +
diff --git a/luci-app-alist/luasrc/view/alist/alist_status.htm b/luci-app-alist/luasrc/view/alist/alist_status.htm new file mode 100644 index 00000000..6be654ea --- /dev/null +++ b/luci-app-alist/luasrc/view/alist/alist_status.htm @@ -0,0 +1,36 @@ +<% + local uci = require 'luci.model.uci'.cursor() + ssl = uci:get_first('alist', 'alist', 'ssl') + if ssl == '1' then + protocol="https://" + else + protocol="http://" + end +%> + + + + +
+

+ <%:Collecting data...%> +

+
diff --git a/luci-app-alist/po/zh-cn b/luci-app-alist/po/zh-cn deleted file mode 120000 index 8d69574d..00000000 --- a/luci-app-alist/po/zh-cn +++ /dev/null @@ -1 +0,0 @@ -zh_Hans \ No newline at end of file diff --git a/luci-app-alist/po/zh_Hans/alist.po b/luci-app-alist/po/zh-cn/alist.po similarity index 91% rename from luci-app-alist/po/zh_Hans/alist.po rename to luci-app-alist/po/zh-cn/alist.po index c01cf1ba..8f7ea132 100644 --- a/luci-app-alist/po/zh_Hans/alist.po +++ b/luci-app-alist/po/zh-cn/alist.po @@ -67,17 +67,14 @@ msgstr "清空日志" msgid "Reset Password" msgstr "重置密码" -msgid "Generate a new random password." -msgstr "随机生成一个新密码。" +msgid "Reset" +msgstr "重置" msgid "Username:" msgstr "用户名:" -msgid "New Password:" -msgstr "新密码:" - -msgid "New password has been copied to clipboard." -msgstr "新密码已复制到剪贴板。" +msgid "Password:" +msgstr "密码:" msgid "Login Validity Period (hours)" msgstr "登录有效期(小时)" diff --git a/luci-app-alist/po/zh_Hans b/luci-app-alist/po/zh_Hans new file mode 120000 index 00000000..41451e4a --- /dev/null +++ b/luci-app-alist/po/zh_Hans @@ -0,0 +1 @@ +zh-cn \ No newline at end of file diff --git a/luci-app-alist/root/etc/uci-defaults/luci-app-alist b/luci-app-alist/root/etc/uci-defaults/50-luci-alist similarity index 100% rename from luci-app-alist/root/etc/uci-defaults/luci-app-alist rename to luci-app-alist/root/etc/uci-defaults/50-luci-alist diff --git a/luci-app-alist/root/usr/share/luci/menu.d/luci-app-alist.json b/luci-app-alist/root/usr/share/luci/menu.d/luci-app-alist.json deleted file mode 100644 index 295910a9..00000000 --- a/luci-app-alist/root/usr/share/luci/menu.d/luci-app-alist.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "admin/nas": { - "title": "NAS", - "order": 44, - "action": { - "type": "firstchild" - } - }, - "admin/nas/alist": { - "title": "Alist", - "order": 20, - "action": { - "type": "firstchild" - }, - "depends": { - "acl": [ "luci-app-alist" ], - "uci": { "alist": true } - } - }, - "admin/nas/alist/basic": { - "title": "Basic Setting", - "order": 30, - "action": { - "type": "view", - "path": "alist/basic" - } - }, - "admin/nas/alist/logs": { - "title": "Logs", - "order": 40, - "action": { - "type": "view", - "path": "alist/logs" - } - } -} diff --git a/luci-app-alist/root/usr/share/rpcd/acl.d/luci-app-alist.json b/luci-app-alist/root/usr/share/rpcd/acl.d/luci-app-alist.json index 46d12af5..98ba9422 100644 --- a/luci-app-alist/root/usr/share/rpcd/acl.d/luci-app-alist.json +++ b/luci-app-alist/root/usr/share/rpcd/acl.d/luci-app-alist.json @@ -2,19 +2,9 @@ "luci-app-alist": { "description": "Grant UCI access for luci-app-alist", "read": { - "file": { - "/usr/bin/alist": [ "exec" ], - "/var/log/alist.log": [ "read" ] - }, - "ubus": { - "service": [ "list" ] - }, "uci": [ "alist" ] }, "write": { - "file": { - "/var/log/alist.log": [ "write" ] - }, "uci": [ "alist" ] } }