🛸 Sync 2026-09-15 02:19:07
Merge-upstream / merge (push) Canceled after 0s

This commit is contained in:
github-actions[bot] committed 2026-09-15 02:19:07 +08:00
1 parent f6683ec9ac
commit 7b7c6e31cd
18 files changed
+976 -544

No files matched your search

+10 -4
View File
@@ -1,19 +1,25 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# Copyright (C) 2021-2026 sirpdboy <herboy2008@gmail.com> 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
@@ -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(
'<em><span style="color:%s">%s <strong>%s %s - %s</strong></span></em>',
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(
'<input class="cbi-button cbi-button-reload" type="button" style="margin-left: 20px" value="%s" onclick="window.open(\'%s\')">',
_('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();
}
});
@@ -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
});
@@ -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
});
@@ -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();
}
});
@@ -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;
}
});
+231 -41
View File
@@ -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服务未启用"
+7
View File
@@ -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
@@ -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
View File
File renamed without changes.
@@ -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"
}
}
}
@@ -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" ]
}
}
}
}
+276 -81
View File
@@ -1,94 +1,289 @@
#!/usr/bin/ucode
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (C) 2021-2026 sirpdboy <herboy2008@gmail.com> 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 };
return { 'luci.lucky': methods };
+16 -13
View File
@@ -1,17 +1,14 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# Copyright (C) 2021-2025 sirpdboy <herboy2008@gmail.com>
#
# 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)))
-5
View File
@@ -1,5 +0,0 @@
config lucky 'lucky'
option logger '1'
option port '16601'
option configdir '/etc/lucky'
option enabled '0'
+23 -58
View File
@@ -1,83 +1,48 @@
#!/bin/sh /etc/rc.common
#
# Copyright (C) 2021-2025 sirpdboy <herboy2008@gmail.com> 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 <https://github.com/gdy666/lucky>
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() {
+4
View File
@@ -0,0 +1,4 @@
config lucky
option enabled '0'
option logger '1'
option configdir '/etc/config/lucky.daji'
+2 -2
View File
@@ -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