mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-03 01:23:56 +08:00
💋 Sync 2026-10-16 23:55:30
This commit is contained in:
1 parent
76f10c2a74
commit
1849cc6156
254 files changed
+25484
-8840
No files matched your search
@@ -59,6 +59,50 @@ function yn(v, t, f) {
|
||||
|
||||
/* ------------------------------------------------------------------ hosts */
|
||||
|
||||
/* LuCI on the LAN is plain http, where the async clipboard API is withheld
|
||||
* (secure-context only): fall back to select + execCommand. The API path
|
||||
* also degrades to the fallback when the browser denies the permission. */
|
||||
function copyText(t) {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
navigator.clipboard.writeText(t).catch(function() { copyTextFallback(t); });
|
||||
return;
|
||||
}
|
||||
copyTextFallback(t);
|
||||
}
|
||||
function copyTextFallback(t) {
|
||||
var ta = E('textarea', { 'style': 'position:fixed; top:-100px; left:-100px;' });
|
||||
ta.value = t;
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
try { document.execCommand('copy'); } catch (e) {}
|
||||
document.body.removeChild(ta);
|
||||
}
|
||||
|
||||
/* Shown after a save that generated a fresh key pair: the private half never
|
||||
* crossed the browser, and the operator adds the public half to the remote's
|
||||
* authorized_keys -- which they can do because they already have their own
|
||||
* access to that host. */
|
||||
function pubkeyModal(pub, name) {
|
||||
var cmd = "echo '" + pub.replace(/\s+$/, '') + "' >> ~/.ssh/authorized_keys";
|
||||
var cmdI = E('input', { 'type': 'text', 'readonly': 'readonly', 'value': cmd,
|
||||
'style': 'width:100%; box-sizing:border-box; font-family:monospace; font-size:12px; padding:6px;' });
|
||||
var msg = E('div', { 'style': 'color:green; min-height:18px; margin-top:4px;' });
|
||||
var copyBtn = E('button', { 'class': 'cbi-button cbi-button-action' }, [ _('Copy command') ]);
|
||||
copyBtn.addEventListener('click', function() {
|
||||
copyText(cmd);
|
||||
msg.textContent = _('Copied. Run it on the remote host.');
|
||||
});
|
||||
var doneBtn = E('button', { 'class': 'cbi-button', 'click': function() { ui.hideModal(); } }, [ _('Done') ]);
|
||||
ui.showModal(_('Authorize this router on the remote host'), E('div', { 'style': 'max-width:560px;' }, [
|
||||
E('p', {}, [ _('A dedicated key pair was generated on this router for host "%s". The private half never leaves this router. You already have access to the remote host, so run this command there to authorize the connection:').replace('%s', name) ]),
|
||||
cmdI,
|
||||
msg,
|
||||
E('div', { 'style': 'margin-top:10px; display:flex; gap:8px; justify-content:flex-end;' }, [ copyBtn, doneBtn ])
|
||||
]));
|
||||
cmdI.focus();
|
||||
cmdI.select();
|
||||
}
|
||||
|
||||
function hostEditor(section, host, onSaved) {
|
||||
var isNew = !section;
|
||||
var f = section || '';
|
||||
@@ -72,13 +116,11 @@ function hostEditor(section, host, onSaved) {
|
||||
modeSel.value = (host && host.mode) || 'ssh';
|
||||
var portI = E('input', { 'type': 'number', 'id': 'zt_r_port', 'value': (host && host.port) || '22', 'min': '1', 'max': '65535' });
|
||||
var userI = E('input', { 'type': 'text', 'id': 'zt_r_user', 'value': (host && host.user) || 'root' });
|
||||
var cportI = E('input', { 'type': 'number', 'id': 'zt_r_cport', 'value': (host && host.controller_port) || '27893', 'min': '1', 'max': '65535' });
|
||||
var keyI = E('textarea', {
|
||||
'id': 'zt_r_key', 'rows': '6', 'style': 'width:100%; font-family:monospace; font-size:12px;',
|
||||
'placeholder': isNew
|
||||
? _('Required: paste the PRIVATE key of a keypair whose public half is in the remote ~/.ssh/authorized_keys')
|
||||
: _('Leave blank to keep the stored key')
|
||||
});
|
||||
var cportI = E('input', { 'type': 'number', 'id': 'zt_r_cport', 'value': (host && host.controller_port) || '9993', 'min': '1', 'max': '65535' });
|
||||
var pubI = (host && host.pubkey)
|
||||
? E('input', { 'type': 'text', 'readonly': 'readonly', 'value': host.pubkey,
|
||||
'style': 'width:100%; box-sizing:border-box; font-family:monospace; font-size:12px;' })
|
||||
: null;
|
||||
var apiI = E('input', {
|
||||
'type': 'password', 'style': 'width:100%; font-family:monospace;',
|
||||
'placeholder': isNew
|
||||
@@ -95,15 +137,20 @@ function hostEditor(section, host, onSaved) {
|
||||
saveBtn.disabled = true;
|
||||
var sectName = f || ('zt' + Date.now().toString(36));
|
||||
var m = modeSel.value;
|
||||
/* private_key is always '' now: a dedicated key pair is generated on
|
||||
* the router when the first SSH-mode save finds none stored. The
|
||||
* field survives in the RPC contract for API callers only. */
|
||||
L.resolveDefault(rpcHostSet(
|
||||
sectName, nameI.value, hostI.value, portI.value,
|
||||
userI.value, cportI.value,
|
||||
(m === 'ssh') ? keyI.value : '',
|
||||
userI.value, (m === 'direct') ? cportI.value : '',
|
||||
'',
|
||||
m, (m === 'direct') ? apiI.value : ''), {})
|
||||
.then(function(res) {
|
||||
if (res && res.code === 0) {
|
||||
hostEditor.close();
|
||||
onSaved();
|
||||
if (m === 'ssh' && res.key_generated && res.pubkey)
|
||||
pubkeyModal(res.pubkey, nameI.value || sectName);
|
||||
} else {
|
||||
msg.textContent = errText(res, _('Could not save the host'));
|
||||
saveBtn.disabled = false;
|
||||
@@ -117,7 +164,22 @@ function hostEditor(section, host, onSaved) {
|
||||
var modeRow = E('div', { 'class': 'cbi-value' }, [ E('label', {}, [_('Access mode')]), E('div', {}, [ modeSel ]) ]);
|
||||
var portRow = E('div', { 'class': 'cbi-value' }, [ E('label', {}, [_('SSH port')]), E('div', {}, [ portI ]) ]);
|
||||
var userRow = E('div', { 'class': 'cbi-value' }, [ E('label', {}, [_('SSH user')]), E('div', {}, [ userI ]) ]);
|
||||
var keyRow = E('div', { 'class': 'cbi-value' }, [ E('label', {}, [_('SSH private key')]), E('div', {}, [ keyI ]) ]);
|
||||
var keyRow = E('div', { 'class': 'cbi-value' }, [ E('label', {}, [_('SSH public key')]), E('div', {}, pubI ? [
|
||||
pubI,
|
||||
E('div', { 'class': 'cbi-value-description' }, [
|
||||
_("The public half of this router's dedicated key pair. Add it to the remote host's ~/.ssh/authorized_keys to authorize this router.")
|
||||
])
|
||||
] : [
|
||||
E('div', { 'class': 'cbi-value-description' }, [
|
||||
_('No private key needed: when you save, this router generates a dedicated key pair and shows you the public key and the exact command to run on the remote host.')
|
||||
])
|
||||
]) ]);
|
||||
var cportRow = E('div', { 'class': 'cbi-value' }, [ E('label', {}, [_('Controller port')]), E('div', {}, [
|
||||
cportI,
|
||||
E('div', { 'class': 'cbi-value-description' }, [
|
||||
_('Direct mode only. Over SSH the port is discovered from the remote daemon and remembered, so it never has to be typed.')
|
||||
])
|
||||
]) ]);
|
||||
var apiRow = E('div', { 'class': 'cbi-value' }, [ E('label', {}, [_('API key')]), E('div', {}, [ apiI ]) ]);
|
||||
|
||||
hostEditor.body = E('div', { 'class': 'cbi-section' }, [
|
||||
@@ -127,7 +189,7 @@ function hostEditor(section, host, onSaved) {
|
||||
E('div', { 'class': 'cbi-value' }, [ E('label', {}, [_('Public IP or host')]), E('div', {}, [ hostI ]) ]),
|
||||
portRow,
|
||||
userRow,
|
||||
E('div', { 'class': 'cbi-value' }, [ E('label', {}, [_('Controller port')]), E('div', {}, [ cportI, E('div', { 'class': 'cbi-value-description' }, [_('The port the controller API listens on, e.g. 27893.')]) ]) ]),
|
||||
cportRow,
|
||||
keyRow,
|
||||
apiRow,
|
||||
msg,
|
||||
@@ -137,6 +199,7 @@ function hostEditor(section, host, onSaved) {
|
||||
var m = modeSel.value;
|
||||
portRow.style.display = (m === 'direct') ? 'none' : '';
|
||||
userRow.style.display = (m === 'direct') ? 'none' : '';
|
||||
cportRow.style.display = (m === 'direct') ? '' : 'none';
|
||||
keyRow.style.display = (m === 'direct') ? 'none' : '';
|
||||
apiRow.style.display = (m === 'direct') ? '' : 'none';
|
||||
hostI.placeholder = (m === 'direct') ? '192.168.192.247' : '203.0.113.10';
|
||||
@@ -1355,19 +1418,26 @@ return view.extend({
|
||||
L.resolveDefault(rpcHostDel(h.section), {}).then(function() { reload(); });
|
||||
});
|
||||
|
||||
listBox.appendChild(E('div', { 'class': 'cbi-section' }, [
|
||||
/* The name borrows the page-title scale: argon styles h2 at
|
||||
* 1.25rem, which is exactly the "ZeroTier - Remote Controller"
|
||||
* heading. Only the h2's card chrome (padding/white
|
||||
* background/shadow) is neutralised -- typography stays the
|
||||
* theme's, so a theme resize moves both together. */
|
||||
listBox.appendChild(E('div', { 'class': 'cbi-section', 'style': 'margin-bottom:1rem;' }, [
|
||||
E('div', { 'style': 'display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:8px;' }, [
|
||||
E('div', {}, [
|
||||
E('a', { 'href': '#', 'style': 'color:inherit; cursor:pointer; text-decoration:none;', 'title': _('Manage'), 'click': function(ev) { ev.preventDefault(); toggleDetail(); } }, [
|
||||
E('strong', {}, [ h.name ])
|
||||
E('h2', { 'style': 'margin:0; padding:0; background:none; box-shadow:none;' }, [
|
||||
E('a', { 'href': '#', 'style': 'color:inherit; cursor:pointer; text-decoration:none;', 'title': _('Manage'), 'click': function(ev) { ev.preventDefault(); toggleDetail(); } }, [ h.name ])
|
||||
]),
|
||||
E('div', { 'style': 'font-family:monospace; font-size:12px; color:#666;' }, [
|
||||
(h.mode === 'direct' || h.direct)
|
||||
? E('span', { 'style': 'color:green; font-weight:bold; margin-right:6px;' }, [ '[' + _('Direct') + ']' ])
|
||||
: E('span', { 'style': 'color:#888; margin-right:6px;' }, [ '[SSH]' ]),
|
||||
(h.mode === 'direct')
|
||||
? (h.host + ':' + h.controller_port + _(' (ZeroTier direct)'))
|
||||
: (h.user + '@' + h.host + ':' + h.port + _(' (controller port ') + h.controller_port + ')')
|
||||
(h.mode === 'direct')
|
||||
? (h.host + ':' + h.controller_port + _(' (ZeroTier direct)'))
|
||||
: (h.user + '@' + h.host + ':' + h.port + (h.controller_port
|
||||
? _(' (controller port ') + h.controller_port + ')'
|
||||
: _(' (controller port auto-discovered)')))
|
||||
]),
|
||||
h.key_present || h.mode === 'direct'
|
||||
? (h.mode === 'direct' ? E('div', { 'style': 'font-size:12px; color:green;' }, [_('API token stored')]) : E('div', { 'style': 'font-size:12px; color:green;' }, [_('SSH key installed')]))
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
* Keys are sfh(trimws(msgid)) in hex, exactly what cbi.js _() looks up.
|
||||
* This firmware ships no window.TR bridge of its own. */
|
||||
window.TR = window.TR || {};
|
||||
Object.assign(window.TR, {"7ba57fa5":"(控制器端口 ","005fb0ba":" —— 成员将不会从此地址池获得地址。","928f10ca":"节点地址必须为 10 位十六进制数字","4f25ffed":"地址池需要一个覆盖相同子网的路由,否则成员将获取不到地址。","623124cd":"ZeroTier 控制器或 Moon 根节点必须运行在具有固定公网 IP 的服务器上,可经 SSH 访问,且该用户具有免密 sudo 权限。控制器 API 不支持 TLS 且拒绝所有非本机回环的调用方,因此本页面从不直接访问它:而是从本路由器向远程回环接口建立 SSH 隧道,经隧道发送 API 调用。控制器令牌按需经 SSH 读取,从不存储在本机。","fe5585ed":"接入方式","038a5486":"操作","e733b525":"主动桥接","300fe5c8":"添加","3698d6ed":"添加远程主机","5598797e":"加入 Moon","d252deef":"添加地址池","e5b147b7":"添加远程主机","ea8fae46":"添加路由","71677946":"地址","a4a2e2e7":"高级设置","6ee64833":"允许默认路由","d24721ab":"允许 DNS","51aaeef3":"允许全局","e575f233":"允许管理","d6d5a047":"允许 Zerotier 的拨入客户端访问路由器 LAN 资源(需要在 Zerotier 管理页面设定到 LAN 网段的路由表)","4e247ba0":"API 密钥","1e4876a9":"API 令牌已存储","0b830d67":"正在分配地址…","2e4746c8":"授权","18bbde5a":"授权失败","ececebcc":"已授权","e3f664db":"自动允许客户端 NAT","2014c910":"自动 NAT: 已禁用","228abc80":"自动 NAT: 已启用","f2ffec55":"正在备份...","bdabf869":"备份失败","5eca6072":"立即备份","379bb677":"备份请求失败","f9fb6815":"基本设置","6b3b39f7":"广播","fcf89e62":"构建中…","ae5deb3a":"取消","08f23f6b":"无法连接到 ZeroTier 服务","4bd163d1":"无法读取 moon(ZeroTier 服务是否在运行?)","26d000c2":"关闭","c06f3259":"正在收集数据...","8462484d":"正在获取地址...","cd2f6c29":"运行配置目录","8f48fe99":"配置","6e72b101":"已配置的主机","f1d119dc":"控制器端口","5d359cf2":"控制器可经隧道访问","41e439d8":"控制器状态存在","00a35b8a":"运行配置目录保护","bd7030be":"使用拷贝而不是链接,来保护原目录内容(一般用于预先配置的控制器模式)","88720483":"无法构建方案","a1a69329":"无法获取成员列表","c53244dc":"无法连接控制器","2c150360":"无法读取此网络的任何成员","c1dfeb1a":"以下网络无法读取,因此未显示:","f2248b2d":"无法刷新成员列表;表格保持不变","cbb3b741":"无法保存主机","56370eb4":"创建并签署 Moon","e3a2eb47":"创建失败","eb9bb863":"创建网络","6c94b9b0":"点击跳转到 Zerotier 官网管理平台,新建或者管理网络,并允许客户端接入访问你私人网路(新接入的节点默认不允许访问)","9a74d36d":"删除","b3b872fe":"删除失败","bbfce216":"网络详情","51d36b3d":"删除网络 ","f89b7324":"退出失败","88643bc2":"退出请求失败","be021833":"设备","a0c974c7":"诊断","6469d71b":"诊断失败","7a0e4df2":"直连","00c6b479":"将此文件分发给成员,然后在每个节点上使用「接口信息 → Moon → 添加 Moon」。签署密钥从未离开远程主机。","799c81a7":"下载 .moon","000e380f":"编辑","2f2c0439":"编辑远程主机","814db5a8":"留空 = 直连","78c2105b":"启用","17074f70":"端点","037e2ea1":"错误","be7c6b14":"加载 moon 列表出错","ef0771c3":"加载网络信息失败","eef2dd18":"加载节点信息失败","a285946d":"读取 moon 列表出错","b4b44266":"服务重载失败,请手动执行: /etc/init.d/luci-zerotier reload","d0e77240":"按名称、地址或 IP 筛选","9865b44d":"起始 IP","48150b7e":"网关","a62c536c":"常规设置","764447db":"全局设置","8686381d":"隐藏","24553b14":"远程管理的工作原理","42933f28":"接口信息","d08681fe":"IP 地址","da686b34":"IP 地址","ad69d4ef":"IP 分配","fbb92dcf":"IP 地址池","23603ee1":"IP 分配","cccbf9e4":"IP 必须为 IPv4 地址","7e11b65e":"标签","f2929f6f":"结束 IP","c6ce32d3":"延迟","22038f1d":"退出","4e72d9f1":"留空则保留已存储的密钥","8da4a1f4":"留空则保留已存储的令牌","2b1fb9a6":"正在退出 moon...","dee10879":"已退出 Moon","9c0e750d":"链路","10e6bd0b":"加载中...","c2b71057":"本地配置文件","b259d7d8":"管理","949429e8":"托管 IP 必须为 IPv4 地址,以逗号分隔","a31cd521":"托管路由","58695b7a":"成员详情","cdd88b7c":"成员 ID","cdc6d88f":"成员名称","16916c1d":"成员","9c03b236":"个成员","a0ddf226":"Moon","b7ca31c5":"Moon 已创建:","c2e07f85":"Moon 创建失败","bbf90d15":"Moon ID","1af43404":"Moon ID 必须是 10 位十六进制字符(例如 123456abcd)","119eb928":"Moon 种子节点","793bcbfa":"Moon 种子必须是该 moon 某个根节点的 10 位地址","2e8cf7ed":"本节点并未加入该 moon","9a9a1e82":"主机上的 moon.json","7ab90fdf":"Moon","ff21d2cf":"MTU","a59f3806":"多播限制","8Line truncated
|
||||
Object.assign(window.TR, {"7ba57fa5":"(控制器端口 ","005fb0ba":" —— 成员将不会从此地址池获得地址。","928f10ca":"节点地址必须为 10 位十六进制数字","4f25ffed":"地址池需要一个覆盖相同子网的路由,否则成员将获取不到地址。","623124cd":"ZeroTier 控制器或 Moon 根节点必须运行在具有固定公网 IP 的服务器上,可经 SSH 访问,且该用户具有免密 sudo 权限。控制器 API 不支持 TLS 且拒绝所有非本机回环的调用方,因此本页面从不直接访问它:而是从本路由器向远程回环接口建立 SSH 隧道,经隧道发送 API 调用。控制器令牌按需经 SSH 读取,从不存储在本机。","fe5585ed":"接入方式","038a5486":"操作","e733b525":"主动桥接","300fe5c8":"添加","3698d6ed":"添加远程主机","5598797e":"加入 Moon","d252deef":"添加地址池","e5b147b7":"添加远程主机","ea8fae46":"添加路由","71677946":"地址","a4a2e2e7":"高级设置","6ee64833":"允许默认路由","d24721ab":"允许 DNS","51aaeef3":"允许全局","e575f233":"允许管理","d6d5a047":"允许 Zerotier 的拨入客户端访问路由器 LAN 资源(需要在 Zerotier 管理页面设定到 LAN 网段的路由表)","4e247ba0":"API 密钥","1e4876a9":"API 令牌已存储","0b830d67":"正在分配地址…","2e4746c8":"授权","18bbde5a":"授权失败","ececebcc":"已授权","e3f664db":"自动允许客户端 NAT","2014c910":"自动 NAT: 已禁用","228abc80":"自动 NAT: 已启用","f2ffec55":"正在备份...","bdabf869":"备份失败","5eca6072":"立即备份","379bb677":"备份请求失败","f9fb6815":"基本设置","6b3b39f7":"广播","fcf89e62":"构建中…","ae5deb3a":"取消","08f23f6b":"无法连接到 ZeroTier 服务","4bd163d1":"无法读取 moon(ZeroTier 服务是否在运行?)","26d000c2":"关闭","c06f3259":"正在收集数据...","8462484d":"正在获取地址...","cd2f6c29":"运行配置目录","8f48fe99":"配置","6e72b101":"已配置的主机","f1d119dc":"控制器端口","5d359cf2":"控制器可经隧道访问","41e439d8":"控制器状态存在","00a35b8a":"运行配置目录保护","bd7030be":"使用拷贝而不是链接,来保护原目录内容(一般用于预先配置的控制器模式)","88720483":"无法构建方案","a1a69329":"无法获取成员列表","c53244dc":"无法连接控制器","2c150360":"无法读取此网络的任何成员","c1dfeb1a":"以下网络无法读取,因此未显示:","f2248b2d":"无法刷新成员列表;表格保持不变","cbb3b741":"无法保存主机","56370eb4":"创建并签署 Moon","e3a2eb47":"创建失败","eb9bb863":"创建网络","6c94b9b0":"点击跳转到 Zerotier 官网管理平台,新建或者管理网络,并允许客户端接入访问你私人网路(新接入的节点默认不允许访问)","9a74d36d":"删除","b3b872fe":"删除失败","bbfce216":"网络详情","51d36b3d":"删除网络 ","f89b7324":"退出失败","88643bc2":"退出请求失败","be021833":"设备","a0c974c7":"诊断","6469d71b":"诊断失败","7a0e4df2":"直连","00c6b479":"将此文件分发给成员,然后在每个节点上使用「接口信息 → Moon → 添加 Moon」。签署密钥从未离开远程主机。","799c81a7":"下载 .moon","000e380f":"编辑","2f2c0439":"编辑远程主机","814db5a8":"留空 = 直连","78c2105b":"启用","17074f70":"端点","037e2ea1":"错误","be7c6b14":"加载 moon 列表出错","ef0771c3":"加载网络信息失败","eef2dd18":"加载节点信息失败","a285946d":"读取 moon 列表出错","b4b44266":"服务重载失败,请手动执行: /etc/init.d/luci-zerotier reload","d0e77240":"按名称、地址或 IP 筛选","9865b44d":"起始 IP","48150b7e":"网关","a62c536c":"常规设置","764447db":"全局设置","8686381d":"隐藏","24553b14":"远程管理的工作原理","42933f28":"接口信息","d08681fe":"IP 地址","da686b34":"IP 地址","ad69d4ef":"IP 分配","fbb92dcf":"IP 地址池","23603ee1":"IP 分配","cccbf9e4":"IP 必须为 IPv4 地址","7e11b65e":"标签","f2929f6f":"结束 IP","c6ce32d3":"延迟","22038f1d":"退出","8da4a1f4":"留空则保留已存储的令牌","2b1fb9a6":"正在退出 moon...","dee10879":"已退出 Moon","9c0e750d":"链路","10e6bd0b":"加载中...","c2b71057":"本地配置文件","b259d7d8":"管理","949429e8":"托管 IP 必须为 IPv4 地址,以逗号分隔","a31cd521":"托管路由","58695b7a":"成员详情","cdd88b7c":"成员 ID","cdc6d88f":"成员名称","16916c1d":"成员","9c03b236":"个成员","a0ddf226":"Moon","b7ca31c5":"Moon 已创建:","c2e07f85":"Moon 创建失败","bbf90d15":"Moon ID","1af43404":"Moon ID 必须是 10 位十六进制字符(例如 123456abcd)","119eb928":"Moon 种子节点","793bcbfa":"Moon 种子必须是该 moon 某个根节点的 10 位地址","2e8cf7ed":"本节点并未加入该 moon","9a9a1e82":"主机上的 moon.json","7ab90fdf":"Moon","ff21d2cf":"MTU","a59f3806":"多播限制","8d8bb35f":"我的控制器","d785d68e":"名称",Line truncated
|
||||
Reference in new issue
Block a user