mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-02 13:13:56 +08:00
🍓 Sync 2026-09-15 02:13:06
This commit is contained in:
1 parent
0e5393594c
commit
1dd96ac2bb
146 files changed
+26541
-508
No files matched your search
@@ -0,0 +1,24 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-ssserver-next
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_MAINTAINER:=Dante <dante@example.com>
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
LUCI_TITLE:=LuCI support for Shadowsocks-rust Server (Next)
|
||||
LUCI_DEPENDS:=+shadowsocks-rust-ssserver-next +luci-base +ucode +ucode-mod-fs +ucode-mod-ubus
|
||||
|
||||
define Package/luci-app-ssserver/postinst
|
||||
#!/bin/sh
|
||||
[ -n "$${IPKG_INSTROOT}" ] || {
|
||||
chmod +x /etc/init.d/ssserver
|
||||
/etc/init.d/rpcd restart
|
||||
}
|
||||
exit 0
|
||||
endef
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
@@ -0,0 +1,106 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require rpc';
|
||||
|
||||
var callGetCiphers = rpc.declare({
|
||||
object: 'luci.ssserver',
|
||||
method: 'get_ciphers',
|
||||
expect: {}
|
||||
});
|
||||
|
||||
var callGetTFOStatus = rpc.declare({
|
||||
object: 'luci.ssserver',
|
||||
method: 'get_tfo_status',
|
||||
expect: {}
|
||||
});
|
||||
|
||||
var callGetLanIP = rpc.declare({
|
||||
object: 'luci.ssserver',
|
||||
method: 'get_lan_ip',
|
||||
expect: {}
|
||||
});
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
callGetCiphers(),
|
||||
callGetTFOStatus(),
|
||||
callGetLanIP()
|
||||
]);
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var m, s, o;
|
||||
var ciphers = data[0].ciphers || [];
|
||||
var tfo_val = data[1].value || 0;
|
||||
var lan_ip = data[2].ip || '192.168.1.1';
|
||||
|
||||
m = new form.Map('ssserver', _('Shadowsocks Server Title') + ' - ' + _('Settings'),
|
||||
_('A fast and secure tunnel proxy that helps you securely access your local network. ' +
|
||||
'This version uses shadowsocks-rust.'));
|
||||
|
||||
s = m.section(form.TypedSection, 'ssserver', _('Settings'));
|
||||
s.anonymous = true;
|
||||
s.addremove = false;
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('Enable'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'server', _('Server Address'),
|
||||
_('The address the server will bind to. Use 0.0.0.0 for all interfaces.'));
|
||||
o.datatype = 'ipaddr';
|
||||
o.placeholder = '0.0.0.0';
|
||||
|
||||
o = s.option(form.Value, 'server_port', _('Server Port'),
|
||||
_('The port the server will listen on.'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '8388';
|
||||
|
||||
o = s.option(form.Value, 'password', _('Password'));
|
||||
o.password = true;
|
||||
|
||||
o = s.option(form.ListValue, 'method', _('Encryption Method'));
|
||||
for (var i = 0; i < ciphers.length; i++) {
|
||||
o.value(ciphers[i]);
|
||||
}
|
||||
if (ciphers.indexOf('chacha20-ietf-poly1305') === -1 && ciphers.length === 0) {
|
||||
o.value('chacha20-ietf-poly1305');
|
||||
o.value('aes-256-gcm');
|
||||
o.value('aes-128-gcm');
|
||||
}
|
||||
|
||||
o = s.option(form.Value, 'timeout', _('ssserver timeout'), _('Connection timeout in seconds.'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = '300';
|
||||
|
||||
o = s.option(form.ListValue, 'mode', _('Network Mode'));
|
||||
o.value('tcp_and_udp', _('TCP and UDP'));
|
||||
o.value('tcp_only', _('TCP only'));
|
||||
o.value('udp_only', _('UDP only'));
|
||||
o.default = 'tcp_and_udp';
|
||||
|
||||
o = s.option(form.Value, 'dns_resolver', _('DNS Resolver'),
|
||||
_('DNS server for the shadowsocks server to resolve addresses.'));
|
||||
o.datatype = 'ipaddr';
|
||||
o.placeholder = lan_ip;
|
||||
o.default = lan_ip;
|
||||
|
||||
o = s.option(form.Flag, 'open_firewall', _('Open Firewall'),
|
||||
_('Automatically open the server port in the firewall (WAN).'));
|
||||
|
||||
var tfo_status_msg = '';
|
||||
if (tfo_val === 0) {
|
||||
tfo_status_msg = '<span style="color:red"> (' + _('Not supported or disabled in kernel') + ')</span>';
|
||||
} else if (tfo_val === 1) {
|
||||
tfo_status_msg = '<span style="color:orange"> (' + _('Enabled for client only') + ')</span>';
|
||||
} else if (tfo_val === 2 || tfo_val === 3) {
|
||||
tfo_status_msg = '<span style="color:green"> (' + _('Enabled and supported') + ')</span>';
|
||||
}
|
||||
|
||||
o = s.option(form.Flag, 'fast_open', _('TCP Fast Open'),
|
||||
_('Enable TCP Fast Open (requires kernel support).') + tfo_status_msg);
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require poll';
|
||||
|
||||
var callGetLog = rpc.declare({
|
||||
object: 'luci.ssserver',
|
||||
method: 'get_log',
|
||||
expect: {}
|
||||
});
|
||||
|
||||
return view.extend({
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null,
|
||||
|
||||
render: function() {
|
||||
var log_view = E('textarea', {
|
||||
id: 'ssserver_log_content',
|
||||
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: 'readonly'
|
||||
}, _('Loading logs...'));
|
||||
|
||||
poll.add(function() {
|
||||
return callGetLog().then(function(res) {
|
||||
var area = document.getElementById('ssserver_log_content');
|
||||
if (area) {
|
||||
if (res && res.log) {
|
||||
area.value = res.log;
|
||||
area.scrollTop = area.scrollHeight;
|
||||
} else {
|
||||
area.value = _('No log entries found.');
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 5);
|
||||
|
||||
return E('div', { 'class': 'cbi-map' }, [
|
||||
E('h2', {}, _('Shadowsocks Server Title') + ' - ' + _('Log')),
|
||||
E('div', { 'class': 'cbi-map-descr' }, _('Logs are updated every 5 seconds.')),
|
||||
E('fieldset', { 'class': 'cbi-section' }, [
|
||||
E('legend', {}, _('Log')),
|
||||
E('div', { 'class': 'cbi-section-node' }, log_view)
|
||||
])
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,189 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require poll';
|
||||
'require ui';
|
||||
'require uci';
|
||||
|
||||
const callSsserverStatus = rpc.declare({
|
||||
object: 'luci.ssserver',
|
||||
method: 'get_status'
|
||||
});
|
||||
|
||||
const callServiceAction = rpc.declare({
|
||||
object: 'luci.ssserver',
|
||||
method: 'service_action',
|
||||
params: [ 'action' ]
|
||||
});
|
||||
|
||||
const callSsserverLanIp = rpc.declare({
|
||||
object: 'luci.ssserver',
|
||||
method: 'get_lan_ip'
|
||||
});
|
||||
|
||||
function renderState(status) {
|
||||
let text = _('Stopped');
|
||||
let color = 'red';
|
||||
|
||||
if (status.running) {
|
||||
text = _('Running');
|
||||
if (status.pid) {
|
||||
text += ' [PID: ' + status.pid + ']';
|
||||
}
|
||||
color = 'green';
|
||||
}
|
||||
|
||||
return { text, color };
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null,
|
||||
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
callSsserverStatus(),
|
||||
uci.load('ssserver'),
|
||||
callSsserverLanIp().catch(function() { return { ip: '' }; })
|
||||
]);
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
const status = data && data[0] ? data[0] : {};
|
||||
const stateInfo = renderState(status);
|
||||
const lanIpInfo = data && data[2] ? data[2] : { ip: '' };
|
||||
const lanIp = lanIpInfo.ip || '8.8.8.8';
|
||||
|
||||
// Read configurations from UCI
|
||||
const ssserverConfig = uci.get('ssserver', 'main') || {};
|
||||
const server = ssserverConfig.server || '0.0.0.0';
|
||||
const port = ssserverConfig.server_port || '8388';
|
||||
const method = ssserverConfig.method || 'chacha20-ietf-poly1305';
|
||||
const timeout = ssserverConfig.timeout || '300';
|
||||
|
||||
let modeText = _('TCP and UDP');
|
||||
if (ssserverConfig.mode === 'tcp_only') modeText = _('TCP only');
|
||||
if (ssserverConfig.mode === 'udp_only') modeText = _('UDP only');
|
||||
|
||||
const dns = ssserverConfig.dns_resolver || lanIp;
|
||||
const tfo = (ssserverConfig.fast_open === '1') ? _('Enabled') : _('Disabled');
|
||||
const firewall = (ssserverConfig.open_firewall === '1') ? _('Enabled') : _('Disabled');
|
||||
|
||||
const container = E('div', { 'class': 'cbi-map' }, [
|
||||
E('h2', {}, _('Shadowsocks Server Title') + ' - ' + _('Overview')),
|
||||
E('div', { 'class': 'cbi-map-descr' }, _('Overview of ssserver running status and actual runtime configurations.'))
|
||||
]);
|
||||
|
||||
// Service Status Section (Aligned with lucky style)
|
||||
const statusSection = E('fieldset', { 'class': 'cbi-section' }, [
|
||||
E('legend', {}, _('Service Status')),
|
||||
E('div', { 'class': 'cbi-section-node' }, [
|
||||
E('table', { 'class': 'table cbi-section-table' }, [
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left', 'width': '33%' }, _('Current Status')),
|
||||
E('td', { 'class': 'td left' }, [
|
||||
E('span', {
|
||||
id: 'ssserver-status-text',
|
||||
'style': 'font-weight:bold; color:' + stateInfo.color + '; margin-right:15px;'
|
||||
}, stateInfo.text),
|
||||
E('button', {
|
||||
id: 'ssserver-restart-btn',
|
||||
'class': 'btn cbi-button cbi-button-apply',
|
||||
'style': 'display:' + (status.running ? 'inline-block' : 'none') + ';',
|
||||
click: ui.createHandlerFn(this, function(ev) {
|
||||
if (!ev || !ev.currentTarget) return;
|
||||
const btn = ev.currentTarget;
|
||||
btn.disabled = true;
|
||||
|
||||
ui.addNotification(null, E('p', _('Restarting Shadowsocks Server, please wait...')), 'info');
|
||||
|
||||
return callServiceAction('restart').then(function(res) {
|
||||
btn.disabled = false;
|
||||
if (res && res.success) {
|
||||
ui.addNotification(null, E('p', _('Service restarted successfully.')), 'info');
|
||||
} else {
|
||||
ui.addNotification(null, E('p', _('Failed to restart service.')), 'error');
|
||||
}
|
||||
}).catch(function(err) {
|
||||
btn.disabled = false;
|
||||
ui.addNotification(null, E('p', _('An error occurred: ') + err.message), 'error');
|
||||
});
|
||||
})
|
||||
}, _('Restart'))
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
container.appendChild(statusSection);
|
||||
|
||||
// Runtime Configuration Section (Aligned with lucky style)
|
||||
const configSection = E('fieldset', { 'class': 'cbi-section' }, [
|
||||
E('legend', {}, _('Runtime Configurations')),
|
||||
E('div', { 'class': 'cbi-section-node' }, [
|
||||
E('table', { 'class': 'table cbi-section-table' }, [
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left', 'width': '33%' }, _('Server Address')),
|
||||
E('td', { 'class': 'td left' }, E('code', {}, server))
|
||||
]),
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, _('Server Port')),
|
||||
E('td', { 'class': 'td left' }, E('code', {}, port))
|
||||
]),
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, _('Encryption Method')),
|
||||
E('td', { 'class': 'td left' }, E('code', {}, method))
|
||||
]),
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, _('Network Mode')),
|
||||
E('td', { 'class': 'td left' }, modeText)
|
||||
]),
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, _('ssserver timeout')),
|
||||
E('td', { 'class': 'td left' }, timeout + ' ' + _('seconds'))
|
||||
]),
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, _('DNS Resolver')),
|
||||
E('td', { 'class': 'td left' }, E('code', {}, dns))
|
||||
]),
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, _('TCP Fast Open')),
|
||||
E('td', { 'class': 'td left' }, tfo)
|
||||
]),
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, _('Open Firewall')),
|
||||
E('td', { 'class': 'td left' }, firewall)
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
container.appendChild(configSection);
|
||||
|
||||
// Add live status polling
|
||||
poll.add(function() {
|
||||
return callSsserverStatus().then(function(status) {
|
||||
const textNode = document.getElementById('ssserver-status-text');
|
||||
const restartBtn = document.getElementById('ssserver-restart-btn');
|
||||
|
||||
if (textNode && restartBtn) {
|
||||
const stateInfo = renderState(status || {});
|
||||
|
||||
textNode.textContent = stateInfo.text;
|
||||
textNode.style.color = stateInfo.color;
|
||||
|
||||
// Toggle button visibility based on runtime state
|
||||
if (status.running) {
|
||||
restartBtn.style.display = 'inline-block';
|
||||
} else {
|
||||
restartBtn.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 5);
|
||||
|
||||
return container;
|
||||
}
|
||||
});
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
zh_Hans
|
||||
@@ -0,0 +1,146 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
msgid "Shadowsocks Server"
|
||||
msgstr "Shadowsocks服务端"
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "概览"
|
||||
|
||||
msgid "Service Status"
|
||||
msgstr "服务状态"
|
||||
|
||||
msgid "Current Status"
|
||||
msgstr "当前状态"
|
||||
|
||||
msgid "Running"
|
||||
msgstr "运行中"
|
||||
|
||||
msgid "Stopped"
|
||||
msgstr "已停止"
|
||||
|
||||
msgid "Restart"
|
||||
msgstr "重启"
|
||||
|
||||
msgid "Unknown"
|
||||
msgstr "未知"
|
||||
|
||||
msgid "A fast and secure tunnel proxy that helps you securely access your local network. This version uses shadowsocks-rust."
|
||||
msgstr "一个快速、安全的隧道代理,可以帮助您安全地访问局域网。此版本使用 shadowsocks-rust。"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "设置"
|
||||
|
||||
msgid "Log"
|
||||
msgstr "日志"
|
||||
|
||||
msgid "Enable"
|
||||
msgstr "启用"
|
||||
|
||||
msgid "Server Address"
|
||||
msgstr "服务器地址"
|
||||
|
||||
msgid "The address the server will bind to. Use 0.0.0.0 for all interfaces."
|
||||
msgstr "服务器绑定的地址。使用 0.0.0.0 以监听所有接口。"
|
||||
|
||||
msgid "Server Port"
|
||||
msgstr "服务器端口"
|
||||
|
||||
msgid "The port the server will listen on."
|
||||
msgstr "服务器监听的端口。"
|
||||
|
||||
msgid "Password"
|
||||
msgstr "密码"
|
||||
|
||||
msgid "Encryption Method"
|
||||
msgstr "加密方式"
|
||||
|
||||
msgid "ssserver timeout"
|
||||
msgstr "超时时间"
|
||||
|
||||
msgid "Connection timeout in seconds."
|
||||
msgstr "连接超时时间(秒)。"
|
||||
|
||||
msgid "Network Mode"
|
||||
msgstr "网络模式"
|
||||
|
||||
msgid "TCP and UDP"
|
||||
msgstr "TCP 和 UDP"
|
||||
|
||||
msgid "TCP only"
|
||||
msgstr "仅 TCP"
|
||||
|
||||
msgid "UDP only"
|
||||
msgstr "仅 UDP"
|
||||
|
||||
msgid "TCP Fast Open"
|
||||
msgstr "TCP 快速打开"
|
||||
|
||||
msgid "Enable TCP Fast Open (requires kernel support)."
|
||||
msgstr "启用 TCP 快速打开(需要内核支持)。"
|
||||
|
||||
msgid "Not supported or disabled in kernel"
|
||||
msgstr "内核不支持或已禁用"
|
||||
|
||||
msgid "Enabled for client only"
|
||||
msgstr "仅对客户端开启"
|
||||
|
||||
msgid "Enabled and supported"
|
||||
msgstr "已开启并支持"
|
||||
|
||||
msgid "DNS Resolver"
|
||||
msgstr "DNS 解析服务器"
|
||||
|
||||
msgid "DNS server for the shadowsocks server to resolve addresses."
|
||||
msgstr "Shadowsocks 服务端用于解析地址的 DNS 服务器。"
|
||||
|
||||
msgid "Open Firewall"
|
||||
msgstr "自动开放防火墙"
|
||||
|
||||
msgid "Automatically open the server port in the firewall (WAN)."
|
||||
msgstr "自动在防火墙 (WAN) 中开放服务器端口。"
|
||||
|
||||
msgid "Shadowsocks Server Log"
|
||||
msgstr "Shadowsocks服务端日志"
|
||||
|
||||
msgid "Logs are updated every 5 seconds."
|
||||
msgstr "日志每 5 秒更新一次。"
|
||||
|
||||
msgid "No log entries found."
|
||||
msgstr "未找到日志条目。"
|
||||
|
||||
msgid "Default"
|
||||
msgstr "默认"
|
||||
|
||||
msgid "Enabled"
|
||||
msgstr "已启用"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "已禁用"
|
||||
|
||||
msgid "Overview of ssserver running status and actual runtime configurations."
|
||||
msgstr "Shadowsocks服务端运行状态及当前运行配置概览。"
|
||||
|
||||
msgid "Restarting Shadowsocks Server, please wait..."
|
||||
msgstr "正在重启Shadowsocks服务端,请稍候..."
|
||||
|
||||
msgid "Service restarted successfully."
|
||||
msgstr "服务重启成功。"
|
||||
|
||||
msgid "Failed to restart service."
|
||||
msgstr "服务重启失败。"
|
||||
|
||||
msgid "An error occurred: "
|
||||
msgstr "发生错误:"
|
||||
|
||||
msgid "Runtime Configurations"
|
||||
msgstr "运行配置"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "秒"
|
||||
|
||||
msgid "Loading logs..."
|
||||
msgstr "正在加载日志..."
|
||||
|
||||
msgid "Shadowsocks Server Title"
|
||||
msgstr "Shadowsocks 服务端"
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
config ssserver 'main'
|
||||
option enabled '0'
|
||||
option server '0.0.0.0'
|
||||
option server_port '8388'
|
||||
option password 'password'
|
||||
option method 'chacha20-ietf-poly1305'
|
||||
option timeout '300'
|
||||
option mode 'tcp_and_udp'
|
||||
option fast_open '0'
|
||||
option open_firewall '1'
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
USE_PROCD=1
|
||||
START=95
|
||||
|
||||
CONF_FILE="/var/etc/ssserver.json"
|
||||
|
||||
ss_server_generate_json() {
|
||||
local server server_port password method timeout fast_open dns_resolver lan_ip mode
|
||||
config_get server "$1" server "0.0.0.0"
|
||||
config_get server_port "$1" server_port "8388"
|
||||
config_get password "$1" password "password"
|
||||
config_get method "$1" method "chacha20-ietf-poly1305"
|
||||
config_get timeout "$1" timeout "300"
|
||||
config_get mode "$1" mode "tcp_and_udp"
|
||||
config_get_bool fast_open "$1" fast_open 0
|
||||
config_get dns_resolver "$1" dns_resolver
|
||||
|
||||
# If DNS is empty, try to get LAN IP
|
||||
if [ -z "$dns_resolver" ]; then
|
||||
lan_ip=$(ubus call network.interface.lan status | jsonfilter -e '@["ipv4-address"][0].address')
|
||||
dns_resolver="${lan_ip:-8.8.8.8}"
|
||||
fi
|
||||
|
||||
mkdir -p /var/etc
|
||||
|
||||
cat > "$CONF_FILE" <<EOF
|
||||
{
|
||||
"server": "$server",
|
||||
"server_port": $server_port,
|
||||
"password": "$password",
|
||||
"method": "$method",
|
||||
"timeout": $timeout,
|
||||
"fast_open": $([ "$fast_open" = "1" ] && echo "true" || echo "false"),
|
||||
"dns": "$dns_resolver",
|
||||
"mode": "$mode"
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
handle_firewall() {
|
||||
local enabled port mode proto
|
||||
config_get_bool enabled "$1" open_firewall 1
|
||||
config_get port "$1" server_port 8388
|
||||
config_get mode "$1" mode "tcp_and_udp"
|
||||
|
||||
case "$mode" in
|
||||
tcp_only) proto="tcp" ;;
|
||||
udp_only) proto="udp" ;;
|
||||
*) proto="tcp udp" ;;
|
||||
esac
|
||||
|
||||
# Remove existing rule if any
|
||||
uci -q delete firewall.ssserver_wan
|
||||
|
||||
if [ "$enabled" -eq 1 ] && [ "$2" = "start" ]; then
|
||||
uci -q batch <<EOF
|
||||
set firewall.ssserver_wan=rule
|
||||
set firewall.ssserver_wan.name='Allow-Shadowsocks-WAN'
|
||||
set firewall.ssserver_wan.src='wan'
|
||||
set firewall.ssserver_wan.dest_port="$port"
|
||||
set firewall.ssserver_wan.proto="$proto"
|
||||
set firewall.ssserver_wan.target='ACCEPT'
|
||||
commit firewall
|
||||
EOF
|
||||
/etc/init.d/firewall reload
|
||||
else
|
||||
uci -q commit firewall
|
||||
/etc/init.d/firewall reload
|
||||
fi
|
||||
}
|
||||
|
||||
start_service() {
|
||||
config_load ssserver
|
||||
local enabled
|
||||
config_get_bool enabled main enabled 0
|
||||
|
||||
[ "$enabled" -eq 0 ] && return
|
||||
|
||||
ss_server_generate_json main
|
||||
handle_firewall main start
|
||||
|
||||
procd_open_instance
|
||||
procd_set_param command /usr/bin/ssserver -c "$CONF_FILE"
|
||||
procd_set_param respawn
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_set_param user root
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
config_load ssserver
|
||||
handle_firewall main stop
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "ssserver"
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"admin/services/ssserver": {
|
||||
"title": "Shadowsocks Server",
|
||||
"action": {
|
||||
"type": "firstchild"
|
||||
},
|
||||
"order": 50
|
||||
},
|
||||
"admin/services/ssserver/overview": {
|
||||
"title": "Overview",
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ssserver/overview"
|
||||
},
|
||||
"order": 10
|
||||
},
|
||||
"admin/services/ssserver/config": {
|
||||
"title": "Settings",
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ssserver/config"
|
||||
},
|
||||
"order": 20
|
||||
},
|
||||
"admin/services/ssserver/log": {
|
||||
"title": "Log",
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ssserver/log"
|
||||
},
|
||||
"order": 30
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"luci-app-ssserver": {
|
||||
"description": "Grant access to ssserver configuration and status",
|
||||
"read": {
|
||||
"ubus": {
|
||||
"luci.ssserver": [ "get_log", "get_ciphers", "get_tfo_status", "get_lan_ip", "get_status" ]
|
||||
},
|
||||
"uci": [ "ssserver", "firewall" ]
|
||||
},
|
||||
"write": {
|
||||
"ubus": {
|
||||
"luci.ssserver": [ "service_action" ]
|
||||
},
|
||||
"uci": [ "ssserver", "firewall" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
'use strict';
|
||||
|
||||
import { connect } from 'ubus';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const methods = {
|
||||
get_log: {
|
||||
call: function() {
|
||||
let p = fs.popen('logread -e ssserver');
|
||||
let log = '';
|
||||
if (p) {
|
||||
log = p.read('all');
|
||||
p.close();
|
||||
}
|
||||
return { log: log };
|
||||
}
|
||||
},
|
||||
|
||||
get_ciphers: {
|
||||
call: function() {
|
||||
let ciphers = [];
|
||||
let p = fs.popen('/usr/bin/ssserver --help 2>&1');
|
||||
if (p) {
|
||||
let output = p.read('all');
|
||||
p.close();
|
||||
let start = index(output, '--encrypt-method');
|
||||
if (start >= 0) {
|
||||
let sub = substr(output, start);
|
||||
let res = match(sub, /possible values: (([^\]\r\n]|[\r\n])+)/);
|
||||
if (res) {
|
||||
let list = split(res[1], /,\s*/);
|
||||
for (let c in list) {
|
||||
let val = rtrim(ltrim(c));
|
||||
// Clean up possible newlines and extra spaces
|
||||
val = replace(val, /\s+/g, "");
|
||||
if (val) push(ciphers, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback if parsing fails or returns nothing
|
||||
if (length(ciphers) == 0) {
|
||||
ciphers = [
|
||||
'chacha20-ietf-poly1305',
|
||||
'aes-256-gcm',
|
||||
'aes-128-gcm',
|
||||
'2022-blake3-aes-128-gcm',
|
||||
'2022-blake3-aes-256-gcm',
|
||||
'2022-blake3-chacha20-poly1305'
|
||||
];
|
||||
}
|
||||
return { ciphers: ciphers };
|
||||
}
|
||||
},
|
||||
|
||||
get_tfo_status: {
|
||||
call: function() {
|
||||
let val = rtrim(fs.readfile('/proc/sys/net/ipv4/tcp_fastopen') || '0');
|
||||
return { value: +val };
|
||||
}
|
||||
},
|
||||
|
||||
get_lan_ip: {
|
||||
call: function() {
|
||||
let u = connect();
|
||||
let ip = '';
|
||||
if (u) {
|
||||
let status = u.call('network.interface.lan', 'status');
|
||||
if (status && type(status['ipv4-address']) === 'array' && length(status['ipv4-address']) > 0) {
|
||||
ip = status['ipv4-address'][0].address;
|
||||
}
|
||||
u.disconnect();
|
||||
}
|
||||
return { ip: ip };
|
||||
}
|
||||
},
|
||||
|
||||
get_status: {
|
||||
call: function() {
|
||||
let u = connect();
|
||||
let running = false;
|
||||
let pid = null;
|
||||
if (u) {
|
||||
let res = u.call('service', 'list', { name: 'ssserver' });
|
||||
if (res && res.ssserver && res.ssserver.instances) {
|
||||
let inst = res.ssserver.instances.instance1;
|
||||
if (inst && inst.running) {
|
||||
running = true;
|
||||
pid = inst.pid;
|
||||
}
|
||||
}
|
||||
u.disconnect();
|
||||
}
|
||||
return { running: running, pid: pid };
|
||||
}
|
||||
},
|
||||
|
||||
service_action: {
|
||||
args: { action: '' },
|
||||
call: function(req) {
|
||||
let action = req.args?.action ?? '';
|
||||
if (action != 'start' && action != 'stop' && action != 'restart' && action != 'reload')
|
||||
return { success: false, error: 'invalid action' };
|
||||
let rc = system('/etc/init.d/ssserver ' + action + ' >/dev/null 2>&1');
|
||||
return { success: (rc === 0), applied: (rc === 0) };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return { 'luci.ssserver': methods };
|
||||
Reference in new issue
Block a user