mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-26 20:42:52 +08:00
This commit is contained in:
1 parent
f6683ec9ac
commit
7b7c6e31cd
18 files changed
+976
-544
No files matched your search
+10
-4
@@ -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;
|
||||
}
|
||||
});
|
||||
@@ -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
@@ -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
|
||||
Regular → Executable
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" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Regular → Executable
+276
-81
@@ -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
@@ -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)))
|
||||
@@ -1,5 +0,0 @@
|
||||
config lucky 'lucky'
|
||||
option logger '1'
|
||||
option port '16601'
|
||||
option configdir '/etc/lucky'
|
||||
option enabled '0'
|
||||
+23
-58
@@ -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() {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
config lucky
|
||||
option enabled '0'
|
||||
option logger '1'
|
||||
option configdir '/etc/config/lucky.daji'
|
||||
+2
-2
@@ -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
|
||||
|
||||
Reference in new issue
Block a user