From 7b7c6e31cd7f07696a81caad7fb20561ff90f3fa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 15 Sep 2026 02:19:07 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=B8=20Sync=202026-09-15=2002:19:07?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luci-app-lucky/Makefile | 14 +- .../resources/view/lucky/config.js | 192 ---------- .../luci-static/resources/view/lucky/log.js | 44 +++ .../luci-static/resources/view/lucky/lucky.js | 78 ---- .../resources/view/lucky/settings.js | 120 ++++++ .../resources/view/lucky/status.js | 193 ++++++++++ luci-app-lucky/po/zh_Hans/lucky.po | 272 +++++++++++-- .../root/etc/uci-defaults/66_luci-lucky | 7 + .../root/etc/uci-defaults/luci-app-lucky | 21 -- .../root/usr/bin/luckyarch | 0 .../usr/share/luci/menu.d/luci-app-lucky.json | 49 +-- .../usr/share/rpcd/acl.d/luci-app-lucky.json | 50 ++- .../root/usr/share/rpcd/ucode/luci.lucky | 357 ++++++++++++++---- lucky/Makefile | 29 +- lucky/files/lucky.config | 5 - lucky/files/lucky.init | 81 ++-- lucky/files/luckyuci | 4 + wwand/Makefile | 4 +- 18 files changed, 976 insertions(+), 544 deletions(-) delete mode 100644 luci-app-lucky/htdocs/luci-static/resources/view/lucky/config.js create mode 100644 luci-app-lucky/htdocs/luci-static/resources/view/lucky/log.js delete mode 100644 luci-app-lucky/htdocs/luci-static/resources/view/lucky/lucky.js create mode 100644 luci-app-lucky/htdocs/luci-static/resources/view/lucky/settings.js create mode 100644 luci-app-lucky/htdocs/luci-static/resources/view/lucky/status.js create mode 100755 luci-app-lucky/root/etc/uci-defaults/66_luci-lucky delete mode 100644 luci-app-lucky/root/etc/uci-defaults/luci-app-lucky rename lucky/files/luckyarch.bin => luci-app-lucky/root/usr/bin/luckyarch (100%) mode change 100644 => 100755 mode change 100644 => 100755 luci-app-lucky/root/usr/share/rpcd/ucode/luci.lucky delete mode 100644 lucky/files/lucky.config create mode 100644 lucky/files/luckyuci diff --git a/luci-app-lucky/Makefile b/luci-app-lucky/Makefile index 1c5cec4af..be3473c9d 100644 --- a/luci-app-lucky/Makefile +++ b/luci-app-lucky/Makefile @@ -1,19 +1,25 @@ # SPDX-License-Identifier: GPL-3.0-only # -# Copyright (C) 2021-2026 sirpdboy https://github.com/sirpdboy/luci-app-lucky +# Copyright (C) 2022-2023 gdy # # This is free software, licensed under the Apache License, Version 2.0 . # include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-lucky -PKG_VERSION:=3.0.3 -PKG_RELEASE:=1 +PKG_VERSION:=1.0.4 +PKG_RELEASE:=2 -LUCI_TITLE:=LuCI Support for Dynamic lucky Client +LUCI_TITLE:=LuCI Support for lucky LUCI_DEPENDS:=+lucky LUCI_PKGARCH:=all +define Package/$(PKG_NAME)/prerm +#!/bin/sh +rm -f /tmp/lucky_info_cache 2>/dev/null +exit 0 +endef + include $(TOPDIR)/feeds/luci/luci.mk # call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-lucky/htdocs/luci-static/resources/view/lucky/config.js b/luci-app-lucky/htdocs/luci-static/resources/view/lucky/config.js deleted file mode 100644 index f4ec602d6..000000000 --- a/luci-app-lucky/htdocs/luci-static/resources/view/lucky/config.js +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright (C) 2021-2026 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-lucky - -'use strict'; -'require form'; -'require poll'; -'require rpc'; -'require uci'; -'require ui'; -'require view'; -'require fs'; - -async function checkProcess() { - try { - const pidofRes = await fs.exec('/bin/pidof', ['lucky']); - if (pidofRes.code === 0) { - return { - running: true, - pid: pidofRes.stdout.trim() - }; - } - } catch (err) { - } - try { - const psRes = await fs.exec('/bin/ps', ['-C', 'lucky', '-o', 'pid=']); - const pid = psRes.stdout.trim(); - return { - running: pid !== '', - pid: pid || null - }; - } catch (err) { - return { running: false, pid: null }; - } -} - -const getLuckyConfig = rpc.declare({ - object: 'luci.lucky', - method: 'get_Info', - expect: { 'Info': {} } -}); - -function getServiceStatus() { - return L.resolveDefault(checkProcess(), {}).then(function(res) { - let isRunning = false; - try { - if (res && res.running) { - isRunning = true; - } - } catch (e) { - console.error('Service status error:', e); - } - return isRunning; - }).catch(function(error) { - console.error('Service status check failed:', error); - return false; - }); -} - -function loadLuckyVer() { - return L.resolveDefault(getLuckyConfig(), {}).then(function(result) { - // console.debug('loadLuckyVer'); - return result.Version || 'Unknown'; - }); -} - -function renderStatus(isRunning, webport, safe_url, protocol, version) { - let statusText = isRunning ? _('RUNNING') : _('NOT RUNNING'); - let color = isRunning ? 'green' : 'red'; - let icon = isRunning ? '✓' : '✗'; - let html = String.format( - '%s %s %s - %s', - color, icon, _('Lucky'), version, statusText - ); - - if (isRunning) { - let buttonUrl = String.format('%s//%s:%s/', protocol, window.location.hostname, webport); - - if (safe_url && safe_url.trim() !== '') { - buttonUrl = String.format('%s%s/', buttonUrl, safe_url); - } - - html += String.format( - '', - _('Open Web Interface'), - buttonUrl - ); - } - - return html; -} - -return view.extend({ - load: function() { - return Promise.all([ - uci.load('lucky') - ]); - }, - - handleResetUser: async function () { - try { - // 检查文件权限 - const stat = await fs.stat('/usr/bin/lucky'); - const result = await fs.exec('/usr/bin/lucky', ['-rResetUser', '-cd', '/etc/lucky']); - if (result.code === 0) { - alert(_('SUCCESS:') + '\n' + _('Username and password reset successfully to 666')); - } - } catch (error) { } - }, - render: function(data) { - let m, s, o; - let webport = uci.get('lucky', 'lucky', 'port') || '16601'; - let safeurl = uci.get('lucky', 'lucky', 'safe') || ''; - let uci_ssl = uci.get('lucky', 'lucky', 'ssl') || '0'; - let protocol = uci_ssl === '1' ? 'https:' : 'http:'; - - m = new form.Map('lucky', _('Lucky'), - _('ipv4/ipv6 portforward,ddns,reverseproxy proxy,wake on lan,IOT and more,Default username and password 666')); - - // 状态显示部分 - s = m.section(form.TypedSection); - s.anonymous = true; - s.addremove = false; - - s.render = function() { - poll.add(function() { - return Promise.all([ - L.resolveDefault(getServiceStatus()), - L.resolveDefault(loadLuckyVer()) - ]).then(function(results) { - const [isRunning, version] = results; - var view = document.getElementById('service_status'); - if (view) { - view.innerHTML = renderStatus(isRunning, webport, safeurl, protocol, version); - } - }).catch(function(error) { - console.error('Poll error:', error); - }); - }, 5); // 添加轮询间隔5秒 - - return E('div', { class: 'cbi-section', id: 'status_bar' }, [ - E('div', { id: 'service_status' }, - E('p', {}, _('Collecting data...')) - ), - E('div', { 'style': 'text-align: right; font-style: italic;' }, [ - E('span', {}, [ - _('© github '), - E('a', { - 'href': 'https://github.com/sirpdboy', - 'target': '_blank', - 'style': 'text-decoration: none;' - }, 'by sirpdboy') - ]) - ]) - ]); - }; - - s = m.section(form.NamedSection, 'lucky', 'lucky'); - - o = s.option(form.Flag, 'enabled', _('Enable')); - o.default = o.disabled; - o.rmempty = false; - - o = s.option(form.Value, 'port', _('Set the Lucky access port')); - o.default = '16601'; - o.rmempty = false; - o.datatype = 'port'; - o.validate = function(section_id, value) { - if (value < 1 || value > 65535) { - return _('Port must be between 1 and 65535'); - } - return true; - }; - - o = s.option(form.Value, 'safe', _('Safe entrance'),_('Set an installation access path, eg:sirpdboy')); - o.default = ''; - o.datatype = 'string'; - - o = s.option(form.Flag, 'ssl', _('Enable SSL'),_('Encrypt access using HTTPS')); - o.default = '0'; - o.rmempty = false; - - o = s.option(form.Value, 'delay', _('Delayed Start (seconds)')); - o.default = '60'; - - o = s.option(form.Button, '_newpassword', _('ResetUser'), - _('Reset account and password to initial values')); - o.inputtitle = _('ResetUser'); - o.inputstyle = 'apply'; - o.onclick = L.bind(this.handleResetUser, this, data); - - return m.render(); - } -}); \ No newline at end of file diff --git a/luci-app-lucky/htdocs/luci-static/resources/view/lucky/log.js b/luci-app-lucky/htdocs/luci-static/resources/view/lucky/log.js new file mode 100644 index 000000000..d6a0073b3 --- /dev/null +++ b/luci-app-lucky/htdocs/luci-static/resources/view/lucky/log.js @@ -0,0 +1,44 @@ +'use strict'; +'require view'; +'require rpc'; +'require poll'; +'require dom'; +'require ui'; + +var callGetLog = rpc.declare({ object: 'luci.lucky', method: 'get_log', expect: { } }); + +return view.extend({ + render: function() { + var logView = E('textarea', { + id: 'logView', + style: 'width:100%; height:500px; background:#f4f4f4; color:#333; padding:10px; border:1px solid #ccc; border-radius:3px; font-family:monospace; font-size:12px; overflow-y:auto; white-space:pre-wrap; word-break:break-all; margin-top:10px;', + readonly: true + }, [ _('Loading logs...') ]); + + var container = E('div', { class: 'cbi-map' }, [ + E('h2', {}, _('Lucky') + ' - ' + _('Log')), + E('div', { class: 'cbi-map-descr' }, _('Real-time query of the Lucky running logs to diagnose DDNS updates, port forwarding actions, and routing issues.')), + E('fieldset', { class: 'cbi-section' }, [ + E('legend', {}, _('Logs')), + E('div', { class: 'cbi-section-node' }, logView) + ]) + ]); + + poll.add(function() { + return callGetLog().then(function(res) { + var textarea = document.getElementById('logView'); + if (textarea && res && typeof(res.log) === 'string') { + var isAtBottom = (textarea.scrollHeight - textarea.clientHeight <= textarea.scrollTop + 1); + textarea.value = res.log || _('No log available.'); + if (isAtBottom) textarea.scrollTop = textarea.scrollHeight; + } + }); + }, 5); + + return container; + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}); diff --git a/luci-app-lucky/htdocs/luci-static/resources/view/lucky/lucky.js b/luci-app-lucky/htdocs/luci-static/resources/view/lucky/lucky.js deleted file mode 100644 index 39b0fa931..000000000 --- a/luci-app-lucky/htdocs/luci-static/resources/view/lucky/lucky.js +++ /dev/null @@ -1,78 +0,0 @@ -// 版权 Copyright (C) 2021-2026 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-lucky - -'use strict'; -'require view'; -'require fs'; -'require ui'; -'require uci'; -'require form'; -'require poll'; - -return view.extend({ - load: function() { - return uci.load('lucky'); - }, - - checkRunning: function() { - return fs.exec('/bin/pidof', ['lucky']).then(function(pidRes) { - if (pidRes.code === 0) return { isRunning: true }; - return fs.exec('/bin/ash', ['-c', 'ps | grep -q "[l]ucky"']).then(function(grepRes) { - return { isRunning: grepRes.code === 0 }; - }); - }); - }, - - render: function() { - var self = this; - - return this.checkRunning().then(function(checkResult) { - var isRunning = checkResult.isRunning; - let webport = uci.get('lucky', 'lucky', 'port') || '16601'; - let safe_url = uci.get('lucky', 'lucky', 'safe') || ''; - let uci_ssl = uci.get('lucky', 'lucky', 'ssl') || '0'; - let protocol = uci_ssl === '1' ? 'https:' : 'http:'; - let buttonUrl = String.format('%s//%s:%s/', protocol, window.location.hostname, webport); - if (safe_url && safe_url.trim() !== '') { - buttonUrl = String.format('%s%s/', buttonUrl, safe_url); - } - var container = E('div'); - if (!isRunning ) { - var message = _('Lucky Service Not Running'); - - - container.appendChild(E('div', { - style: 'text-align: center; padding: 2em;' - }, [ - E('img', { - src: 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMjQiIGhlaWdodD0iMTAyNCIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCI+PHBhdGggZmlsbD0iI2RmMDAwMCIgZD0iTTk0Mi40MjEgMjM0LjYyNGw4MC44MTEtODAuODExLTE1My4wNDUtMTUzLjA0NS04MC44MTEgODAuODExYy03OS45NTctNTEuNjI3LTE3NS4xNDctODEuNTc5LTI3Ny4zNzYtODEuNTc5LTI4Mi43NTIgMC01MTIgMjI5LjI0OC01MTIgNTEyIDAgMTAyLjIyOSAyOS45NTIgMTk3LjQxOSA4MS41NzkgMjc3LjM3NmwtODAuODExIDgwLjgxMSAxNTMuMDQ1IDE1My4wNDUgODAuODExLTgwLjgxMWM3OS45NTcgNTEuNjI3IDE3NS4xNDcgODEuNTc5IDI3Ny4zNzYgODEuNTc5IDI4Mi43NTIgMCA1MTItMjI5LjI0OCA1MTItNTEyIDAtMTAyLjIyOS0yOS45NTItMTk3LjQxOS04MS41NzktMjc3LjM3NnpNMTk0Ljk0NCA1MTJjMC0xNzUuMTA0IDE0MS45NTItMzE3LjA1NiAzMTcuMDU2LTMxNy4wNTYgNDggMCA5My40ODMgMTAuNjY3IDEzNC4yMjkgMjkuNzgxbC00MjEuNTQ3IDQyMS41NDdjLTE5LjA3Mi00MC43ODktMjkuNzM5LTg2LjI3Mi0yOS43MzktMTM0LjI3MnpNNTEyIDgyOS4wNTZjLTQ4IDAtOTMuNDgzLTEwLjY2Ny0xMzQuMjI5LTI5Ljc4MWw0MjEuNTQ3LTQyMS41NDdjMTkuMDcyIDQwLjc4OSAyOS43ODEgODYuMjcyIDI5Ljc4MSAxMzQuMjI5LTAuMDQzIDE3NS4xNDctMTQxLjk5NSAzMTcuMDk5LTMxNy4wOTkgMzE3LjA5OXoiLz48L3N2Zz4=', - style: 'width: 100px; height: 100px; margin-bottom: 1em;' - }), - E('h2', {}, message) - ])); - } else { - var iframe = E('iframe', { - src: buttonUrl, - style: 'width: 100%; min-height: 100vh; border: none;' - }); - container.appendChild(iframe); - } - - poll.add(function() { - return self.checkRunning().then(function(checkResult) { - var newStatus = checkResult.isRunning; - if (newStatus !== isRunning) { - window.location.reload(); - } - }); - }, 5); - - poll.start(); - - return container; - }); - }, - - handleSaveApply: null, - handleSave: null, - handleReset: null -}); diff --git a/luci-app-lucky/htdocs/luci-static/resources/view/lucky/settings.js b/luci-app-lucky/htdocs/luci-static/resources/view/lucky/settings.js new file mode 100644 index 000000000..3b4a2f6b2 --- /dev/null +++ b/luci-app-lucky/htdocs/luci-static/resources/view/lucky/settings.js @@ -0,0 +1,120 @@ +'use strict'; +'require view'; +'require rpc'; +'require uci'; +'require ui'; +'require form'; + +var callGetInfo = rpc.declare({ object: 'luci.lucky', method: 'get_info', expect: { } }); +var callSetConfig = rpc.declare({ object: 'luci.lucky', method: 'set_config', params: ['key', 'value'], expect: { } }); +var callService = rpc.declare({ object: 'luci.lucky', method: 'service', params: ['action'], expect: { } }); + +return view.extend({ + load: function() { + return Promise.all([ + uci.load('lucky'), + callGetInfo() + ]); + }, + + handleUpdate: function(key, value) { + ui.showModal(null, [ E('p', { class: 'spinning' }, _('Updating configuration and restarting service...')) ]); + + return callSetConfig(key, value).then(function(res) { + if (res && res.ret === 0) { + return callService('restart').then(function() { + // Wait a bit for restart + setTimeout(function() { + ui.hideModal(); + ui.addNotification(null, E('p', _('Configuration updated successfully')), 'info'); + window.location.reload(); + }, 2000); + }); + } else { + ui.hideModal(); + ui.addNotification(null, E('p', _('Update failed')), 'error'); + } + }); + }, + + render: function(results) { + var info = results[1] || {}; + var baseConf = { + AdminWebListenPort: String(info.adminPort || '16601'), + SafeURL: info.adminSafeUrl || '', + AllowInternetaccess: info.adminAllowInternet || false + }; + + var m, s, o; + var self = this; + + m = new form.Map('lucky', _('Lucky') + ' - ' + _('Settings'), + _('Configure basic settings for the Lucky high-performance routing service, including master enable switch and administrative ports.')); + + s = m.section(form.TypedSection, 'lucky', _('General Settings')); + s.anonymous = true; + + // Enable service + o = s.option(form.Flag, 'enabled', _('Enable')); + o.rmempty = false; + + // Config Directory (UCI) + o = s.option(form.Value, 'configdir', _('Config Directory Path')); + o.rmempty = false; + o.placeholder = '/etc/config/lucky.daji'; + + // Admin Panel Settings - 使用同一个 TypedSection 追加虚拟选项 + s = m.section(form.TypedSection, 'lucky', _('Admin Panel Settings')); + s.anonymous = true; + + // Admin Port + o = s.option(form.Value, 'admin_port', _('Admin Web Listen Port')); + o.datatype = 'port'; + o.load = function(section_id) { return baseConf.AdminWebListenPort || "16601"; }; + o.write = function(section_id, value) { + callSetConfig('admin_http_port', value); + return this.super('write', [section_id, value]); + }; + o.remove = function(section_id) { + return this.super('remove', [section_id]); + }; + + // Admin Safe URL + o = s.option(form.Value, 'admin_safe_url', _('Admin Safe URL')); + o.load = function(section_id) { return baseConf.SafeURL || ""; }; + o.write = function(section_id, value) { + callSetConfig('admin_safe_url', value); + return this.super('write', [section_id, value]); + }; + o.remove = function(section_id) { + callSetConfig('admin_safe_url', ''); + return this.super('remove', [section_id]); + }; + + // Allow Internet Access + o = s.option(form.Flag, 'admin_internet', _('Allow Internet Access')); + o.load = function(section_id) { + return baseConf.AllowInternetaccess ? '1' : '0'; + }; + o.write = function(section_id, value) { + callSetConfig('switch_Internetaccess', (value === '1')); + return this.super('write', [section_id, value]); + }; + o.remove = function(section_id) { + callSetConfig('switch_Internetaccess', false); + return this.super('remove', [section_id]); + }; + + // Reset Auth + o = s.option(form.Button, '_reset_auth', _('Reset Account and Password')); + o.inputtitle = _('Reset to Default (666:666)'); + o.inputstyle = 'negative'; + o.onclick = ui.createHandlerFn(self, function() { + if (confirm(_('Are you sure you want to reset the admin account and password to default (666:666)?'))) { + return self.handleUpdate('reset_auth_info', ''); + } + }); + + return m.render(); + } +}); diff --git a/luci-app-lucky/htdocs/luci-static/resources/view/lucky/status.js b/luci-app-lucky/htdocs/luci-static/resources/view/lucky/status.js new file mode 100644 index 000000000..fef4fa72b --- /dev/null +++ b/luci-app-lucky/htdocs/luci-static/resources/view/lucky/status.js @@ -0,0 +1,193 @@ +'use strict'; +'require view'; +'require rpc'; +'require ui'; +'require dom'; +'require poll'; + +var callGetInfo = rpc.declare({ object: 'luci.lucky', method: 'get_info', expect: { } }); +var callGetStatus = rpc.declare({ object: 'luci.lucky', method: 'get_status', expect: { } }); +var callService = rpc.declare({ object: 'luci.lucky', method: 'service', params: ['action'], expect: { } }); + +return view.extend({ + load: function() { + return callGetInfo(); + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null, + + handleServiceAction: function(action) { + ui.showModal(null, [ E('p', { class: 'spinning' }, _('Executing service action...')) ]); + var self = this; + var startTime = Date.now(); + var shouldBeRunning = (action === 'start' || action === 'restart'); + + return callService(action).then(function() { + function waitForReady() { + if (Date.now() - startTime > 20000) { + ui.hideModal(); + ui.addNotification(null, E('p', _('Operation timeout, please refresh the page')), 'error'); + return; + } + callGetStatus().then(function(res) { + var isRunning = res && res.state && res.state !== 'stopped'; + if (shouldBeRunning ? isRunning : !isRunning) { + ui.hideModal(); + ui.addNotification(null, E('p', _('Service action executed successfully')), 'info'); + self.updateStatus(res); + // 服务刚启动/重启后,异步刷新完整信息 + if (isRunning) { + callGetInfo().then(function(info) { + self.updateInfo(info); + }); + } + } else { + setTimeout(waitForReady, 1000); + } + }); + } + waitForReady(); + }); + }, + + updateStatus: function(status) { + var statusEl = document.getElementById('_status'); + var state = status ? status.state : 'stopped'; + var pid = status ? status.pid : null; + var running = (state !== 'stopped'); + + if (statusEl) { + var statusText = _('Stopped'); + var statusColor = 'red'; + var isManaged = (state === 'managed'); + var isUnmanaged = (state === 'unmanaged'); + + if (isManaged) { + statusText = _('Running'); + statusColor = 'green'; + } else if (isUnmanaged) { + statusText = _('Running (Unmanaged)'); + statusColor = 'orange'; + } + + if (pid) { + statusText += ' [PID: ' + pid + ']'; + } + + var statusNode = [ + E('b', { style: 'color:' + statusColor }, statusText) + ]; + + if (running) { + statusNode.push(E('input', { + type: 'button', id: '_btnRestart', class: 'btn cbi-button cbi-button-reload', value: _('Restart'), + style: 'margin-left: 20px;', + click: ui.createHandlerFn(this, 'handleServiceAction', 'restart') + })); + } + + dom.content(statusEl, statusNode); + } + + var adminLinkEl = document.getElementById('_luckyAdminLink'); + if (adminLinkEl && !running) { + dom.content(adminLinkEl, E('em', {}, _('Service not running'))); + } + }, + + updateInfo: function(info) { + var luckyInfo = {}; + try { luckyInfo = JSON.parse(info.luckyInfo || '{}'); } catch(e) {} + + var el; + el = document.getElementById('_luckyVersion'); + if (el) dom.content(el, luckyInfo.Version || '-'); + + el = document.getElementById('_luckyArch'); + if (el) dom.content(el, info.luckyArch || '-'); + + el = document.getElementById('_luckyDate'); + if (el) dom.content(el, luckyInfo.Date || '-'); + + if (info.running && info.adminPort) { + var url = 'http://' + window.location.hostname + ':' + info.adminPort + (info.adminSafeUrl || ''); + el = document.getElementById('_luckyAdminLink'); + if (el) dom.content(el, E('a', { href: url, target: '_blank', style: 'font-weight:bold; color:blue;' }, url)); + } + }, + + render: function(info) { + var running = info.running; + + var container = E('div', { class: 'cbi-map' }, [ + E('h2', {}, _('Lucky') + ' - ' + _('Overview')), + E('div', { class: 'cbi-map-descr' }, _('Overview of your Lucky routing service status, real-time statistics, and control settings.')) + ]); + + // 解析程序信息 + var luckyInfo = {}; + try { luckyInfo = JSON.parse(info.luckyInfo || '{}'); } catch(e) {} + + // 管理面板链接 + var adminLinkContent; + if (running && info.adminPort) { + var url = 'http://' + window.location.hostname + ':' + info.adminPort + (info.adminSafeUrl || ''); + adminLinkContent = E('a', { href: url, target: '_blank', style: 'font-weight:bold; color:blue;' }, url); + } else if (running) { + adminLinkContent = E('em', {}, _('Loading...')); + } else { + adminLinkContent = E('em', {}, _('Service not running')); + } + + container.appendChild( + E('fieldset', { class: 'cbi-section' }, [ + E('legend', {}, _('Service Status')), + E('div', { class: 'cbi-section-node' }, [ + E('table', { class: 'table' }, [ + E('tr', { class: 'tr' }, [ + E('td', { class: 'td left', width: '33%' }, _('Current Status')), + E('td', { class: 'td left', id: '_status' }, E('em', {}, _('Loading...'))) + ]) + ]) + ]) + ]) + ); + + var infoSection = E('fieldset', { class: 'cbi-section' }, [ + E('legend', {}, _('Application Information')), + E('div', { class: 'cbi-section-node' }, [ + E('table', { class: 'table cbi-section-table' }, [ + E('tr', { class: 'tr' }, [ + E('td', { class: 'td left', width: '33%' }, _('Version')), + E('td', { class: 'td left', id: '_luckyVersion' }, luckyInfo.Version || '-') + ]), + E('tr', { class: 'tr' }, [ + E('td', { class: 'td left' }, _('Architecture')), + E('td', { class: 'td left', id: '_luckyArch' }, info.luckyArch || '-') + ]), + E('tr', { class: 'tr' }, [ + E('td', { class: 'td left' }, _('Compile Time')), + E('td', { class: 'td left', id: '_luckyDate' }, luckyInfo.Date || '-') + ]), + E('tr', { class: 'tr' }, [ + E('td', { class: 'td left' }, _('Admin Panel')), + E('td', { class: 'td left', id: '_luckyAdminLink' }, adminLinkContent) + ]) + ]) + ]) + ]); + container.appendChild(infoSection); + this.updateStatus(info); + + var self = this; + poll.add(function() { + return callGetStatus().then(function(res) { + self.updateStatus(res); + }); + }, 5); + + return container; + } +}); diff --git a/luci-app-lucky/po/zh_Hans/lucky.po b/luci-app-lucky/po/zh_Hans/lucky.po index b26a244d7..6d78ff431 100644 --- a/luci-app-lucky/po/zh_Hans/lucky.po +++ b/luci-app-lucky/po/zh_Hans/lucky.po @@ -2,62 +2,252 @@ msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" msgid "Lucky" -msgstr "Lucky大吉" +msgstr "Lucky" -msgid "Lucky Control panel" -msgstr "Lucky操作台" +msgid "IPv4/IPv6 port forwarding, DDNS, HTTP/HTTPS reverse proxy, WOL and more." +msgstr "IPv4/IPv6端口转发, 动态域名服务, HTTP/HTTPS反向代理, 网络唤醒以及更多功能。" -msgid "ipv4/ipv6 portforward,ddns,reverseproxy proxy,wake on lan,IOT and more,Default username and password 666" -msgstr "IPv4/IPv6端口转发,动态域名服务,http/https反向代理,默认用户名密码666.." +msgid "Overview" +msgstr "概览" -msgid "Enable SSL" -msgstr "开启SSL" +msgid "Settings" +msgstr "设置" + +msgid "Log" +msgstr "日志" + +msgid "Service Status" +msgstr "服务状态" + +msgid "Service Control" +msgstr "服务控制" + +msgid "Main Program Information" +msgstr "主程序信息" + +msgid "Admin Panel Information" +msgstr "管理面板信息" + +msgid "Enable" +msgstr "启用" + +msgid "Basic Settings" +msgstr "基础设置" + +msgid "Running Logs" +msgstr "运行日志" + +msgid "Installation Status" +msgstr "安装状态" + +msgid "Lucky Status" +msgstr "Lucky 运行状态" + +msgid "Lucky Arch" +msgstr "程序架构" + +msgid "Compilation Time" +msgstr "编译时间" + +msgid "Lucky Version" +msgstr "程序版本" msgid "Collecting data..." -msgstr "收集数据..." +msgstr "正在收集数据..." -msgid "Set the Lucky access port" -msgstr "设置访问端口" - -msgid "Base Setting" -msgstr "基本设置" - -msgid "Safe entrance" -msgstr "安全入口" - -msgid "Port must be between 1 and 65535" -msgstr "端口范围1-65535之间" - -msgid "Open Web Interface" -msgstr "打开WEB界面" - -msgid "RUNNING" +msgid "Running" msgstr "运行中" -msgid "NOT RUNNING" +msgid "Not running" msgstr "未运行" -msgid "Delayed Start (seconds)" -msgstr "开机延时启动(秒)" +msgid "Not installed" +msgstr "未安装" -msgid "Username and password reset successfully to 666" -msgstr "重置用户名和密码为666" +msgid "Installed" +msgstr "已安装" -msgid "SUCCESS:" -msgstr "执行完成:" +msgid "Start" +msgstr "启动" -msgid "ResetUser" -msgstr "重置用户密码" +msgid "Stop" +msgstr "停止" -msgid "Reset account and password to initial values" -msgstr "重置帐号和密码为初始值" +msgid "Restart" +msgstr "重启" -msgid "Encrypt access using HTTPS" -msgstr "使用https加密访问" +msgid "Processing..." +msgstr "处理中..." -msgid "Set an installation access path, eg:sirpdboy" -msgstr "设置一个安装访问路径,如:sirpdboy" +msgid "Admin Panel" +msgstr "Lucky 后台管理" + +msgid "Admin Panel Login Info" +msgstr "管理后台登录信息" + +msgid "Lucky Admin Http Port" +msgstr "Lucky 管理端口" + +msgid "Admin Safe URL" +msgstr "管理安全入口" + +msgid "Allow Internet access" +msgstr "允许外网访问管理界面" + +msgid "Service not running, Admin Panel is unavailable" +msgstr "服务未运行,管理后台不可用" + +msgid "DefaultAuth" +msgstr "默认登录账号密码" + +msgid "Reset Account and Password" +msgstr "重置帐号密码" + +msgid "update success" +msgstr "更新成功" + +msgid "update failed" +msgstr "更新失败" + +msgid "Updating configuration..." +msgstr "正在更新配置..." + +msgid "Change" +msgstr "修改" + +msgid "allow" +msgstr "允许" + +msgid "not allow" +msgstr "禁止" + +msgid "Are you sure Enalbe Internetaccess?" +msgstr "确定要启用外网访问吗?" + +msgid "Are you sure Disable Internetaccess?" +msgstr "确定要禁用外网访问吗?" + +msgid "Config dir path" +msgstr "配置文件夹路径" + +msgid "The path to store the config file" +msgstr "存储 Lucky 配置文件的目录路径" + +msgid "get latest version" +msgstr "获取最新版本" + +msgid "portValueError" +msgstr "端口值无效" + +msgid "No log available." +msgstr "暂无可用日志。" + +msgid "Fetching logs..." +msgstr "正在拉取日志..." + +msgid "Executing service action..." +msgstr "正在执行服务操作..." + +msgid "Operation timeout, please refresh the page" +msgstr "操作超时,请刷新页面" + +msgid "Service action executed successfully" +msgstr "服务操作执行成功" + +msgid "Stopped" +msgstr "已停止" + +msgid "Service not running" +msgstr "服务未运行" + +msgid "Lucky Overview" +msgstr "Lucky 概览" + +msgid "Monitor and control the Lucky service." +msgstr "监控并控制 Lucky 服务的运行状态。" + +msgid "Loading..." +msgstr "加载中..." + +msgid "Current Status" +msgstr "当前状态" + +msgid "Actions" +msgstr "操作" + +msgid "Application Information" +msgstr "应用程序信息" + +msgid "Version" +msgstr "版本" + +msgid "Architecture" +msgstr "系统架构" + +msgid "Compile Time" +msgstr "编译时间" + +msgid "Updating configuration and restarting service..." +msgstr "正在更新配置并重启服务..." + +msgid "Configuration updated successfully" +msgstr "配置更新成功" + +msgid "Update failed" +msgstr "更新失败" + +msgid "Lucky Settings" +msgstr "Lucky 设置" + +msgid "Configure basic settings for the Lucky service." +msgstr "配置 Lucky 服务的基础设置。" + +msgid "General Settings" +msgstr "常规设置" + +msgid "Config Directory Path" +msgstr "配置文件路径" + +msgid "Admin Panel Settings" +msgstr "管理面板设置" + +msgid "Admin Web Listen Port" +msgstr "后台管理端口" + +msgid "Allow Internet Access" +msgstr "允许外网访问" + +msgid "Reset to Default (666:666)" +msgstr "恢复默认 (666:666)" + +msgid "Are you sure you want to reset the admin account and password to default (666:666)?" +msgstr "确定要将管理员账号和密码恢复为默认值 (666:666) 吗?" + +msgid "Loading logs..." +msgstr "正在加载日志..." + +msgid "Lucky Running Logs" +msgstr "Lucky 运行日志" + +msgid "View the real-time execution logs of the Lucky service." +msgstr "查看 Lucky 服务的实时执行日志。" + +msgid "Logs" +msgstr "日志" + +msgid "Overview of your Lucky routing service status, real-time statistics, and control settings." +msgstr "展示 Lucky 服务当前的后台运行状态、系统架构、接口监听端口以及便捷的管理面板快捷登录链接。" + +msgid "Configure basic settings for the Lucky high-performance routing service, including master enable switch and administrative ports." +msgstr "配置 Lucky 高性能分流与网络转发服务的核心基础参数,控制服务总开关、前台管理端口以及底层二进制运行策略。" + +msgid "Real-time query of the Lucky running logs to diagnose DDNS updates, port forwarding actions, and routing issues." +msgstr "实时查看 Lucky 的 system 运行日志,以快速排查动态域名 DDNS 更新状态、端口转发链路以及路由策略异常。" + +msgid "Running" +msgstr "运行中" + +msgid "Running (Unmanaged)" +msgstr "运行中 (非托管)" -msgid "Lucky Service Not Running" -msgstr "Lucky服务未启用" diff --git a/luci-app-lucky/root/etc/uci-defaults/66_luci-lucky b/luci-app-lucky/root/etc/uci-defaults/66_luci-lucky new file mode 100755 index 000000000..31e942208 --- /dev/null +++ b/luci-app-lucky/root/etc/uci-defaults/66_luci-lucky @@ -0,0 +1,7 @@ +#!/bin/sh + +chmod +x /usr/bin/luckyarch +rm -f /tmp/luci-indexcache* 2>/dev/null +rm -f /tmp/luci-modulecache/* 2>/dev/null +rm -f /tmp/lucky_info_cache 2>/dev/null +return 0 diff --git a/luci-app-lucky/root/etc/uci-defaults/luci-app-lucky b/luci-app-lucky/root/etc/uci-defaults/luci-app-lucky deleted file mode 100644 index 39825835c..000000000 --- a/luci-app-lucky/root/etc/uci-defaults/luci-app-lucky +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -[ ! -f "/usr/share/ucitrack/luci-app-lucky.json" ] && { - cat > /usr/share/ucitrack/luci-app-lucky.json << EEOF -{ - "config": "lucky", - "init": "lucky" -} -EEOF -} - -[ -f "/etc/config/ucitrack" ] && { -uci -q batch <<-EOF >/dev/null - delete ucitrack.@lucky[-1] - add ucitrack lucky - set ucitrack.@lucky[-1].init=lucky - commit ucitrack -EOF -} - -rm -rf /tmp/luci-* -exit 0 \ No newline at end of file diff --git a/lucky/files/luckyarch.bin b/luci-app-lucky/root/usr/bin/luckyarch old mode 100644 new mode 100755 similarity index 100% rename from lucky/files/luckyarch.bin rename to luci-app-lucky/root/usr/bin/luckyarch diff --git a/luci-app-lucky/root/usr/share/luci/menu.d/luci-app-lucky.json b/luci-app-lucky/root/usr/share/luci/menu.d/luci-app-lucky.json index 23a18223b..f6f77645c 100644 --- a/luci-app-lucky/root/usr/share/luci/menu.d/luci-app-lucky.json +++ b/luci-app-lucky/root/usr/share/luci/menu.d/luci-app-lucky.json @@ -1,30 +1,37 @@ { - "admin/services/lucky": { + "admin/services/lucky": { "title": "Lucky", - "order": 59, + "order": 60, "action": { - "type": "firstchild" + "type": "firstchild" }, "depends": { "acl": [ "luci-app-lucky" ], "uci": { "lucky": true } } - }, - "admin/services/lucky/lucky": { - "title": "Lucky Control panel", - "order": 10, - "action": { - "type": "view", - "path": "lucky/lucky" - } - }, - "admin/services/lucky/config": { - "title": "Base Setting", - "order": 20, - "action": { - "type": "view", - "path": "lucky/config" - } - } - + }, + "admin/services/lucky/status": { + "title": "Overview", + "order": 10, + "action": { + "type": "view", + "path": "lucky/status" + } + }, + "admin/services/lucky/settings": { + "title": "Settings", + "order": 20, + "action": { + "type": "view", + "path": "lucky/settings" + } + }, + "admin/services/lucky/log": { + "title": "Log", + "order": 30, + "action": { + "type": "view", + "path": "lucky/log" + } + } } diff --git a/luci-app-lucky/root/usr/share/rpcd/acl.d/luci-app-lucky.json b/luci-app-lucky/root/usr/share/rpcd/acl.d/luci-app-lucky.json index 69e53d277..3cb0d24ba 100644 --- a/luci-app-lucky/root/usr/share/rpcd/acl.d/luci-app-lucky.json +++ b/luci-app-lucky/root/usr/share/rpcd/acl.d/luci-app-lucky.json @@ -1,29 +1,23 @@ { - "luci-app-lucky": { - "description": "Grant UCI access for luci-app-lucky", - "read": { - "uci": [ "lucky" ], - "file": { - "/etc/init.d/lucky": [ "exec" ], - "/usr/bin/lucky": [ "exec" ], - "/bin/pidof": [ "exec" ], - "/bin/ps": [ "exec" ], - "/bin/ash": [ "exec" ] - }, - "ubus": { - "rc": [ "*" ], - "service": ["list"], - "luci.lucky": [ "*" ] - } - }, - "write": { - "ubus": { - "luci.lucky": [ "*" ] - }, - "file": { - "/etc/lucky/*.lkcf": ["write"] - }, - "uci": ["lucky"] - } - } -} + "luci-app-lucky": { + "description": "Grant UCI and UBUS access for luci-app-lucky", + "read": { + "ubus": { + "luci.lucky": [ "get_info", "get_status", "get_log" ] + }, + "uci": [ "lucky" ], + "file": { + "/etc/init.d/lucky": [ "exec" ] + } + }, + "write": { + "ubus": { + "luci.lucky": [ "set_config", "service" ] + }, + "uci": [ "lucky" ], + "file": { + "/tmp/lucky_info_cache": [ "write" ] + } + } + } +} \ No newline at end of file diff --git a/luci-app-lucky/root/usr/share/rpcd/ucode/luci.lucky b/luci-app-lucky/root/usr/share/rpcd/ucode/luci.lucky old mode 100644 new mode 100755 index 87ed9574a..7530349e6 --- a/luci-app-lucky/root/usr/share/rpcd/ucode/luci.lucky +++ b/luci-app-lucky/root/usr/share/rpcd/ucode/luci.lucky @@ -1,94 +1,289 @@ #!/usr/bin/ucode -/* - * SPDX-License-Identifier: GPL-2.0-only - * - * Copyright (C) 2021-2026 sirpdboy https://github.com/sirpdboy/luci-app-lucky - */ - 'use strict'; +import { connect } from 'ubus'; +import { popen, readfile, writefile, unlink } from 'fs'; +import { cursor } from 'uci'; -import { access, error, lstat, popen, readfile, writefile } from 'fs'; - -/* Kanged from ucode/luci */ -function shellquote(s) { - return `'${replace(s, "'", "'\\''")}'`; +function trim(s) { + if (s) return replace(s, /^\s+|\s+$/g, ''); + return s; } + +function shellquote(s) { + if (!s) return "''"; + return "'" + replace(s, "'", "'\\''") + "'"; +} + +function get_configdir() { + let c = cursor(); + c.load('lucky'); + let dir = c.get('lucky', '@lucky[0]', 'configdir'); + return dir ? trim(dir) : ''; +} + +function get_procd_status() { + let u = connect(); + if (!u) + return { running: false }; + + let res = u.call('service', 'list', { name: 'lucky' }); + if (res && res['lucky'] && res['lucky'].instances) { + let instances = res['lucky'].instances; + for (let name in instances) { + let inst = instances[name]; + if (inst && inst.running) + return { running: true, pid: inst.pid }; + } + } + + return { running: false }; +} + +function get_rogue_pid(managed_pid) { + let p = popen('pidof /usr/bin/lucky', 'r'); + if (!p) + return null; + + let pid_str = p.read('all'); + p.close(); + + if (!pid_str) + return null; + + let pids = split(trim(pid_str), /\s+/); + for (let i = 0; i < length(pids); i++) { + let pid = pids[i]; + if (pid != '' && (managed_pid == null || pid != (managed_pid + ''))) + return pid; + } + + return null; +} + +function get_pid() { + let procd_st = get_procd_status(); + let rogue_pid = get_rogue_pid(procd_st.pid); + return (procd_st.pid || rogue_pid || "") + ""; +} + + +const CACHE_FILE = '/tmp/lucky_info_cache'; + +// 读取缓存,仅在 PID 未变(包括停止状态 pid=="")时返回有效数据 +function get_cache() { + try { + let content = readfile(CACHE_FILE); + if (content) { + let cache = json(content); + let currentPid = get_pid(); + if (cache && cache.pid === currentPid && cache.data) { + return cache.data; + } + } + } catch(err) {} + return null; +} + +// 写入缓存,绑定当前 PID +function set_cache(data) { + let pid = get_pid(); + if (data) { + writefile(CACHE_FILE, sprintf("%J", { pid: pid, data: data })); + } +} + +// 清除缓存 +function clear_cache() { + try { unlink(CACHE_FILE); } catch(err) {} +} + const methods = { - set_ConfInfo: { - args: { key: 'key', value: 'value' }, - call: function(req) { - if (!access('/usr/bin/lucky')) - return { status: 255, error: 'lucky not found' }; + // 合并接口:一次返回运行状态 + 程序信息 + 管理信息 + get_info: { + call: function() { + let configPath = get_configdir(); - if (!req.args?.key) - return { status: 255, error: 'missing key parameter' }; + // 1. 检测运行状态 + let procd_st = get_procd_status(); + let rogue_pid = get_rogue_pid(procd_st.pid); - const key = shellquote(req.args.key); - const configPath = '/etc/lucky'; - let exit_code = 255; - - if (req.args.value !== null && req.args.value !== undefined) { - const value = shellquote(req.args.value); - exit_code = system(`/usr/bin/lucky -setconf -key ${key} -value ${value} -cd ${configPath}`); - } else { - // Èç¹ûûÓÐÌṩvalue£¬ֻÉèÖÃkey - exit_code = system(`/usr/bin/lucky -setconf -key ${key} -cd ${configPath}`); - } + let state = 'stopped'; + if (procd_st.running) + state = 'managed'; + else if (rogue_pid) + state = 'unmanaged'; - return { status: exit_code }; - } - }, + let running = (state !== 'stopped'); + let pid = procd_st.pid || rogue_pid; - get_baseInfo: { - call: function() { - if (!access('/usr/bin/lucky')) - return { ConfInfo: {}, error: 'lucky not found' }; + // 2. 程序信息(从缓存或实时获取) + let luckyInfo = ""; + let luckyArch = ""; + let adminPort = ""; + let adminSafeUrl = ""; + let adminAllowInternet = false; - const configPath = '/etc/lucky'; - const fd = popen('/usr/bin/lucky -baseConfInfo -cd /etc/lucky'); - if (fd) { - let config = fd.read('all'); - fd.close(); - - if (!config || length(config) === 0) - return { ConfInfo: {}, error: 'empty response' }; - - try { - config = json(config); - return { ConfInfo: config }; - } catch(e) { - return { ConfInfo: {}, error: 'JSON parse error: ' + e }; - } - } else { - return { ConfInfo: {}, error: 'failed to execute lucky command' }; - } - } - }, + let cached = get_cache(); + if (cached) { + luckyInfo = cached.luckyInfo || ""; + luckyArch = cached.luckyArch || ""; + adminPort = cached.adminPort || ""; + adminSafeUrl = cached.adminSafeUrl || ""; + adminAllowInternet = cached.adminAllowInternet || false; + } else { + let fd = popen(configPath ? + `/usr/bin/lucky -info -cd ${shellquote(configPath)}` : + "/usr/bin/lucky -info"); + if (fd) { luckyInfo = trim(fd.read('all')); fd.close(); } - get_Info: { - call: function() { - if (!access('/usr/bin/lucky')) - return { Info: {}, error: 'lucky not found' }; - const configPath = '/etc/lucky'; - const fd = popen('/usr/bin/lucky -info -cd /etc/lucky'); - if (fd) { - let config = fd.read('all'); - fd.close(); - - if (!config || length(config) === 0) - return { Info: {}, error: 'empty response' }; - - try { - config = json(config); - return { Info: config }; - } catch(e) { - return { Info: {}, error: 'JSON parse error: ' + e }; - } - } else { - return { Info: {}, error: 'failed to execute lucky command' }; - } - } - } + fd = popen("/usr/bin/luckyarch"); + if (fd) { luckyArch = trim(fd.read('all')); fd.close(); } + + let cmd = configPath ? + `/usr/bin/lucky -baseConfInfo -cd ${shellquote(configPath)}` : + `/usr/bin/lucky -baseConfInfo`; + fd = popen(cmd); + if (fd) { + let confStr = trim(fd.read('all')); + fd.close(); + if (confStr) { + try { + let obj = json(confStr); + if (obj && obj.BaseConfigure) { + adminPort = obj.BaseConfigure.AdminWebListenPort || ""; + adminSafeUrl = obj.BaseConfigure.SafeURL || ""; + adminAllowInternet = obj.BaseConfigure.AllowInternetaccess || false; + } + } catch(err) {} + } + } + + set_cache({ + luckyInfo: luckyInfo, + luckyArch: luckyArch, + adminPort: adminPort, + adminSafeUrl: adminSafeUrl, + adminAllowInternet: adminAllowInternet + }); + } + + return { + running: running, + state: state, + pid: pid, + luckyInfo: luckyInfo, + luckyArch: luckyArch, + adminPort: adminPort, + adminSafeUrl: adminSafeUrl, + adminAllowInternet: adminAllowInternet + }; + } + }, + get_status: { + call: function() { + let procd_st = get_procd_status(); + let rogue_pid = get_rogue_pid(procd_st.pid); + + let state = 'stopped'; + if (procd_st.running) + state = 'managed'; + else if (rogue_pid) + state = 'unmanaged'; + + let running = (state !== 'stopped'); + + return { + running: running, + state: state, + pid: procd_st.pid || rogue_pid + }; + } + }, + + get_log: { + call: function() { + let fd = popen("logread -e lucky | tail -n 100"); + let log = fd ? fd.read('all') : ""; + if (fd) fd.close(); + return { log: log }; + } + }, + set_config: { + args: { key: 'key', value: 'value' }, + call: function(req) { + if (!req.args || !req.args.key) { + return { ret: 1 }; + } + let key = req.args.key; + let value = req.args.value || ""; + let ret = 1; + let configPath = get_configdir(); + + if (key === "admin_http_port" || key === "admin_safe_url" || key === "switch_Internetaccess") { + let realKey = key; + let cmd = ""; + + if (key === "admin_http_port") realKey = "AdminWebListenPort"; + else if (key === "admin_safe_url") realKey = "SafeURL"; + else if (key === "switch_Internetaccess") realKey = "AllowInternetaccess"; + + if (key === "admin_safe_url" && value === "") { + cmd = `/usr/bin/lucky -rCancelSafeURL`; + } else { + cmd = `/usr/bin/lucky -setconf -key ${shellquote(realKey)} -value ${shellquote(value)}`; + } + + if (configPath !== "") { + cmd += ` -cd ${shellquote(configPath)}`; + } + system(cmd); + ret = 0; + } else if (key === "configdir") { + let c = cursor(); + c.load('lucky'); + c.set('lucky', '@lucky[0]', 'configdir', value); + c.commit('lucky'); + ret = 0; + } else if (key === "reset_auth_info") { + let cmd1 = `/usr/bin/lucky -setconf -key AdminAccount -value 666`; + let cmd2 = `/usr/bin/lucky -setconf -key AdminPassword -value 666`; + if (configPath !== "") { + cmd1 += ` -cd ${shellquote(configPath)}`; + cmd2 += ` -cd ${shellquote(configPath)}`; + } + system(cmd1); + system(cmd2); + ret = 0; + } + + return { ret: ret }; + } + }, + service: { + args: { action: 'action' }, + call: function(req) { + if (!req.args || !req.args.action) { + return { success: false }; + } + let action = req.args.action; + let applied = true; + if (action === "start" || action === "restart" || action === "stop") { + // 停止时清除缓存 + if (action === "stop") { + clear_cache(); + } + if (action === "restart") { + // 重启前,强制强杀所有可能残留的 lucky 野进程,确保重新由 procd 唯一受控拉起 + let p = popen('killall -9 lucky', 'r'); + if (p) p.close(); + } + let rc = system(`/etc/init.d/lucky ${action} >/dev/null 2>&1`); + applied = (rc === 0); + system("sleep 2"); + } + return { success: true, applied: applied }; + } + } }; -return { 'luci.lucky': methods }; \ No newline at end of file +return { 'luci.lucky': methods }; diff --git a/lucky/Makefile b/lucky/Makefile index 4d92dbc95..0a20501b3 100644 --- a/lucky/Makefile +++ b/lucky/Makefile @@ -1,17 +1,14 @@ # SPDX-License-Identifier: GPL-3.0-only # -# Copyright (C) 2021-2025 sirpdboy -# -# This is free software, licensed under the Apache License, Version 2.0 . -# -# include $(TOPDIR)/rules.mk PKG_NAME:=lucky PKG_VERSION:=2.27.2 -PKG_RELEASE:=2 +PKG_RELEASE:=3 PKGARCH:=all +LUCKY_CONF_DIR := /etc/config/lucky.daji +LUCKY_TMP_BACKUP := /tmp/luckyconfdir ifeq ($(ARCH),mipsel) LUCKY_ARCH:=mipsle_softfloat @@ -29,8 +26,8 @@ ifeq ($(ARCH),arm) ifeq ($(BOARD),bcm53xx) LUCKY_ARCH:=armv6 else - LUCKY_ARCH:=armv7 -endif + LUCKY_ARCH:=armv7 + endif endif ifeq ($(BOARD),bcm53xx) LUCKY_ARCH:=armv6 @@ -66,15 +63,20 @@ define Package/$(PKG_NAME)/description Main functions of Lucky: ipv4/ipv6 portforward,ddns,IOT wake on lan ,reverse proxy and more... endef + + + define Build/Prepare [ ! -f $(PKG_BUILD_DIR)/$(PKG_NAME)_$(PKG_VERSION)_Linux_$(LUCKY_ARCH).tar.gz ] && wget https://github.com/gdy666/lucky/releases/download/v$(PKG_VERSION)/$(PKG_NAME)_$(PKG_VERSION)_Linux_$(LUCKY_ARCH).tar.gz -O $(PKG_BUILD_DIR)/$(PKG_NAME)_$(PKG_VERSION)_Linux_$(LUCKY_ARCH).tar.gz - tar -xzvf $(PKG_BUILD_DIR)/$(PKG_NAME)_$(PKG_VERSION)_Linux_$(LUCKY_ARCH).tar.gz -C $(PKG_BUILD_DIR) || exit 1 + tar -xzvf $(PKG_BUILD_DIR)/$(PKG_NAME)_$(PKG_VERSION)_Linux_$(LUCKY_ARCH).tar.gz -C $(PKG_BUILD_DIR) endef + + define Package/$(PKG_NAME)/conffiles /etc/config/lucky -/etc/lucky/* endef + define Build/Compile endef @@ -83,9 +85,10 @@ define Package/$(PKG_NAME)/install $(INSTALL_DIR) $(1)/etc/init.d/ $(INSTALL_DIR) $(1)/etc/config/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/lucky $(1)/usr/bin/lucky - $(INSTALL_BIN) $(CURDIR)/files/luckyarch.bin $(1)/usr/bin/luckyarch $(INSTALL_BIN) ./files/lucky.init $(1)/etc/init.d/lucky - $(INSTALL_CONF) $(CURDIR)/files/lucky.config $(1)/etc/config/lucky + $(INSTALL_CONF) $(CURDIR)/files/luckyuci $(1)/etc/config/lucky endef -$(eval $(call BuildPackage,$(PKG_NAME))) + + +$(eval $(call BuildPackage,$(PKG_NAME))) \ No newline at end of file diff --git a/lucky/files/lucky.config b/lucky/files/lucky.config deleted file mode 100644 index 03c96239c..000000000 --- a/lucky/files/lucky.config +++ /dev/null @@ -1,5 +0,0 @@ -config lucky 'lucky' - option logger '1' - option port '16601' - option configdir '/etc/lucky' - option enabled '0' diff --git a/lucky/files/lucky.init b/lucky/files/lucky.init index f2cb05863..8cffa6dcb 100644 --- a/lucky/files/lucky.init +++ b/lucky/files/lucky.init @@ -1,83 +1,48 @@ #!/bin/sh /etc/rc.common -# -# Copyright (C) 2021-2025 sirpdboy https://github.com/sirpdboy/luci-app-lucky - -# This file is part of lucky . -# -# This is free software, licensed under the Apache License, Version 2.0 . +# Copyright (C) 2021-2024 daji START=99 -STOP=15 USE_PROCD=1 -CONF=lucky PROG=/usr/bin/lucky -CONFDIR=/etc/lucky +DEFAULT_PRO_CONF='/etc/config/lucky.daji/' -get_config() { - config_get_bool enabled $1 enabled 0 - config_get_bool logger $1 logger 1 - config_get port $1 port 16601 - config_get SafeURL $1 safe - config_get delay $1 delay 0 +lucky_instance() { + local cfg="$1" + local enabled configdir logger -} + config_get_bool enabled "$cfg" enabled 0 + config_get configdir "$cfg" configdir "$DEFAULT_PRO_CONF" + config_get_bool logger "$cfg" logger 1 -init_config(){ - config_load "$CONF" - config_foreach get_config "$CONF" -} + [ "$enabled" -eq 1 ] || return 0 -init_confdir(){ - [ -d $CONFDIR ] || mkdir -p $CONFDIR 2>/dev/null -} + [ -d "$configdir" ] || mkdir -p "$configdir" 2>/dev/null -LOG(){ - echo "$1" - logger -t lucky -p warn "$1" -} - -start_instance() { - enabled=$(uci -q get $CONF.$CONF.enabled ) || enabled="0" - logger=$(uci -q get $CONF.$CONF.logger ) || logger="1" - port=$(uci -q get $CONF.$CONF.port ) || port="16601" - SafeURL=$(uci -q get $CONF.$CONF.safe ) || SafeURL=" " - delay=$(uci -q get $CONF.$CONF.delay ) || delay="5" - SafeURL="${SafeURL##*( )}" - SafeURL="${SafeURL%%*( )}" - - init_confdir - [ x$enabled = x1 ] || return 1 - [ $(awk -F. '{print $1}' /proc/uptime) -lt "120" ] && sleep $delay - $(which lucky) -setconf -key AdminWebListenPort -value $port -cd $CONFDIR - if [ -z "$SafeURL" ] ; then - $(which lucky) -rCancelSafeURL - else - $(which lucky) -setconf -key SafeURL -value "$SafeURL" -cd $CONFDIR - fi - - procd_open_instance - procd_set_param command $PROG - procd_append_param command -cd $CONFDIR + procd_open_instance "$cfg" + procd_set_param command "$PROG" -cd "$configdir" procd_set_param respawn - procd_set_param stderr 1 + procd_set_param stdout "$logger" + procd_set_param stderr "$logger" procd_close_instance - LOG "lucky is start." - } start_service() { - pgrep -f $PROG | xargs kill -9 >/dev/null 2>&1 - start_instance + # 强制清理可能脱离 procd 管控的独立进程(如通过 Lucky 面板自行更新/重启产生的孤儿进程) + local pids=$(pidof /usr/bin/lucky) + [ -n "$pids" ] && kill -9 $pids >/dev/null 2>&1 || true + + config_load lucky + config_foreach lucky_instance lucky } stop_service() { - pgrep -f $PROG | xargs kill -9 >/dev/null 2>&1 - LOG "lucky is stop." + local pids=$(pidof /usr/bin/lucky) + [ -n "$pids" ] && kill -9 $pids >/dev/null 2>&1 || true } service_triggers() { - procd_add_reload_trigger lucky + procd_add_reload_trigger "lucky" } reload_service() { diff --git a/lucky/files/luckyuci b/lucky/files/luckyuci new file mode 100644 index 000000000..e34436500 --- /dev/null +++ b/lucky/files/luckyuci @@ -0,0 +1,4 @@ +config lucky + option enabled '0' + option logger '1' + option configdir '/etc/config/lucky.daji' \ No newline at end of file diff --git a/wwand/Makefile b/wwand/Makefile index a36cc5088..2cd3e9d2e 100644 --- a/wwand/Makefile +++ b/wwand/Makefile @@ -50,11 +50,11 @@ include $(TOPDIR)/rules.mk PKG_NAME:=wwand PKG_VERSION:=1.6.6_p28 -PKG_RELEASE:=38 +PKG_RELEASE:=39 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/ddimension/wwand.git -PKG_SOURCE_VERSION:=df322691e8567b60cf2c46fe2352c3ae3160f093 +PKG_SOURCE_VERSION:=55d1cf6ab38562ba40a64c123274b99d4a60a96d PKG_MIRROR_HASH:=skip PKG_LICENSE:=GPL-2.0-only