mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-31 03:35:13 +08:00
🐤 Sync 2026-06-24 03:33:40
This commit is contained in:
1 parent
c72c833af0
commit
34879cebcd
69 files changed
+3101
-8708
No files matched your search
@@ -33,7 +33,6 @@ const BACKENDS = {
|
||||
log: '/var/log/dae/dae.log',
|
||||
pkg: 'dae',
|
||||
config: '/etc/dae/config.dae',
|
||||
example: '/etc/dae/example.dae',
|
||||
hasWebUI: false,
|
||||
useNetns: false
|
||||
}
|
||||
|
||||
@@ -11,84 +11,62 @@
|
||||
/* dae 示例里的占位订阅特征 —— example.com、relative/path/to 或中文占位文本 */
|
||||
const RE_PLACEHOLDER_URL = /(['"])(?:(?:https?|https-file|file):\/\/[^'"]*?(?:example\.com|relative\/path\/to)[^'"]*|你的订阅链接或者机场链接)\1/;
|
||||
|
||||
/* 常用块片段 —— 给文本模式用户一键插入官方推荐结构 */
|
||||
const SNIPPETS = {
|
||||
subscription:
|
||||
'\nsubscription {\n' +
|
||||
' # 把下面这行换成你的机场订阅链接\n' +
|
||||
" my_sub: '你的订阅链接或者机场链接'\n" +
|
||||
'}\n',
|
||||
node:
|
||||
'\nnode {\n' +
|
||||
' # 单条节点链接(ss/vmess/trojan/vless 等 share link)\n' +
|
||||
" # 'ss://...'\n" +
|
||||
'}\n',
|
||||
group:
|
||||
'\ngroup {\n' +
|
||||
' my_group {\n' +
|
||||
' # 节点选择策略:min_moving_avg / min / random / fixed(0)\n' +
|
||||
' policy: min_moving_avg\n' +
|
||||
' }\n' +
|
||||
'}\n',
|
||||
routing:
|
||||
'\nrouting {\n' +
|
||||
' # 私网直连\n' +
|
||||
' dip(geoip:private) -> direct\n' +
|
||||
' # 国内直连\n' +
|
||||
' dip(geoip:cn) -> direct\n' +
|
||||
' domain(geosite:cn) -> direct\n' +
|
||||
' # 兜底:走代理组\n' +
|
||||
' fallback: my_group\n' +
|
||||
'}\n',
|
||||
dns:
|
||||
'\ndns {\n' +
|
||||
' upstream {\n' +
|
||||
" alidns: 'udp://dns.alidns.com:53'\n" +
|
||||
" googledns: 'tcp+udp://dns.google:53'\n" +
|
||||
' }\n' +
|
||||
' routing {\n' +
|
||||
' request {\n' +
|
||||
' qname(geosite:cn) -> alidns\n' +
|
||||
' fallback: googledns\n' +
|
||||
' }\n' +
|
||||
' }\n' +
|
||||
'}\n',
|
||||
include:
|
||||
'\ninclude {\n' +
|
||||
' # 相对路径:相对于 dae -c 指定的入口配置目录\n' +
|
||||
' config.d/*.dae\n' +
|
||||
'}\n'
|
||||
};
|
||||
|
||||
/* 把 example.dae 的 subscription 块瘦身成「只保留 1 行占位 + 中文引导注释」,
|
||||
降低用户认知负担。其他块(global/node/group/routing/dns)原样保留。 */
|
||||
function simplifySubscriptionBlock(text) {
|
||||
if (!text) return text;
|
||||
const lines = text.split('\n');
|
||||
let start = -1, end = -1, depth = 0;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (start < 0) {
|
||||
if (/^\s*subscription\s*\{/.test(lines[i])) {
|
||||
start = i;
|
||||
depth = 1;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
depth += (lines[i].match(/\{/g) || []).length;
|
||||
depth -= (lines[i].match(/\}/g) || []).length;
|
||||
if (depth <= 0) { end = i; break; }
|
||||
}
|
||||
}
|
||||
if (start < 0 || end < 0) return text;
|
||||
/* 整块替换:保留原有 block 缩进风格(4 空格) */
|
||||
const replacement = [
|
||||
'subscription {',
|
||||
' # ⚠ 把下面这行的 URL 换成你机场的订阅链接,然后保存。',
|
||||
" my_sub: '你的订阅链接或者机场链接'",
|
||||
'}'
|
||||
];
|
||||
return lines.slice(0, start).concat(replacement).concat(lines.slice(end + 1)).join('\n');
|
||||
}
|
||||
/* Minimal ready-to-run default: gen-dae-config.sh defaults + a placeholder subscription to swap. */
|
||||
const DEFAULT_TEMPLATE =
|
||||
'# luci-app-daede 默认配置:把 subscription 里的占位链接换成你的机场订阅,保存即可运行。\n' +
|
||||
'global {\n' +
|
||||
' tproxy_port: 12345\n' +
|
||||
' tproxy_port_protect: true\n' +
|
||||
' log_level: info\n' +
|
||||
' lan_interface: br-lan\n' +
|
||||
' wan_interface: auto\n' +
|
||||
' auto_config_kernel_parameter: true\n' +
|
||||
' dial_mode: domain\n' +
|
||||
" tcp_check_url: 'http://cp.cloudflare.com,1.1.1.1,2606:4700:4700::1111'\n" +
|
||||
" udp_check_dns: 'dns.google:53,8.8.8.8,2001:4860:4860::8888'\n" +
|
||||
' check_interval: 30s\n' +
|
||||
' check_tolerance: 50ms\n' +
|
||||
'}\n' +
|
||||
'\n' +
|
||||
'subscription {\n' +
|
||||
' # ⚠ 把下面这行的 URL 换成你机场的订阅链接,然后保存。\n' +
|
||||
" my_airport: 'https://example.com/sub'\n" +
|
||||
'}\n' +
|
||||
'\n' +
|
||||
'dns {\n' +
|
||||
' ipversion_prefer: 4\n' +
|
||||
' upstream {\n' +
|
||||
" cndns: 'udp://dns.alidns.com:53'\n" +
|
||||
" fallbackdns: 'tcp+udp://dns.google:53'\n" +
|
||||
' }\n' +
|
||||
' routing {\n' +
|
||||
' request {\n' +
|
||||
' qname(geosite:cn) -> cndns\n' +
|
||||
' fallback: fallbackdns\n' +
|
||||
' }\n' +
|
||||
' }\n' +
|
||||
'}\n' +
|
||||
'\n' +
|
||||
'group {\n' +
|
||||
' proxy {\n' +
|
||||
' filter: subtag(my_airport)\n' +
|
||||
' policy: min_moving_avg\n' +
|
||||
' }\n' +
|
||||
'}\n' +
|
||||
'\n' +
|
||||
'routing {\n' +
|
||||
' pname(NetworkManager) -> direct\n' +
|
||||
' dip(224.0.0.0/3) -> direct\n' +
|
||||
' dip(255.255.255.255/32) -> direct\n' +
|
||||
" dip('ff00::/8') -> direct\n" +
|
||||
' dip(geoip:private) -> direct\n' +
|
||||
' l4proto(udp) && dport(123) -> direct\n' +
|
||||
' domain(connectivitycheck.gstatic.com) -> direct\n' +
|
||||
' domain(msftconnecttest.com) -> direct\n' +
|
||||
' dip(geoip:cn) -> direct\n' +
|
||||
' domain(geosite:cn) -> direct\n' +
|
||||
' fallback: proxy\n' +
|
||||
'}\n';
|
||||
|
||||
function detectPlaceholders(text) {
|
||||
if (!text) return [];
|
||||
@@ -584,7 +562,7 @@ function renderDaeEditor() {
|
||||
const textarea = E('textarea', {
|
||||
'class': 'dd-editor dd-editor-hl',
|
||||
'spellcheck': 'false',
|
||||
'placeholder': _('dae config file is empty. Click "Initialize from example" to start, or paste your config here.')
|
||||
'placeholder': _('dae config file is empty. Click "Load default config" to start, or paste your config here.')
|
||||
}, '');
|
||||
const hlCode = E('code', {});
|
||||
const hlPre = E('pre', { 'class': 'dd-hl', 'aria-hidden': 'true' }, hlCode);
|
||||
@@ -595,7 +573,7 @@ function renderDaeEditor() {
|
||||
}
|
||||
textarea.addEventListener('scroll', function() { hlPre.scrollTop = textarea.scrollTop; });
|
||||
const save = E('button', { 'class': 'cbi-button cbi-button-positive' }, _('Save manual config'));
|
||||
const init = E('button', { 'class': 'cbi-button cbi-button-action' }, _('Initialize from example'));
|
||||
const init = E('button', { 'class': 'cbi-button cbi-button-action' }, _('Load default config'));
|
||||
const status = E('span', { 'class': 'dd-editor-status' }, '');
|
||||
|
||||
const phWarn = E('div', { 'class': 'dd-ph-warn' }, [
|
||||
@@ -605,40 +583,12 @@ function renderDaeEditor() {
|
||||
E('ul', { 'class': 'dd-ph-list' })
|
||||
]);
|
||||
|
||||
/* Insert Block 下拉:选完后自动复位到首项 */
|
||||
const insertSelect = E('select', { 'class': 'dd-insert-select', 'title': _('Insert config block at cursor') }, [
|
||||
E('option', { 'value': '' }, _('+ Insert Block…')),
|
||||
E('option', { 'value': 'subscription' }, 'subscription'),
|
||||
E('option', { 'value': 'node' }, 'node'),
|
||||
E('option', { 'value': 'group' }, 'group'),
|
||||
E('option', { 'value': 'routing' }, 'routing'),
|
||||
E('option', { 'value': 'dns' }, 'dns'),
|
||||
E('option', { 'value': 'include' }, 'include')
|
||||
]);
|
||||
|
||||
/* Footer:dae version · 行数 · 占位剩余 */
|
||||
const fbVer = E('span', { 'class': 'dd-fb-item' }, [ E('span', { 'class': 'dd-fb-key' }, 'dae'), E('span', {}, '—') ]);
|
||||
const fbLine = E('span', { 'class': 'dd-fb-item' }, [ E('span', { 'class': 'dd-fb-key' }, _('lines')), E('span', {}, '0') ]);
|
||||
const fbStat = E('span', { 'class': 'dd-fb-item dd-fb-ok' }, '✓ ready');
|
||||
const footer = E('div', { 'class': 'dd-editor-footer' }, [ fbVer, fbLine, fbStat ]);
|
||||
|
||||
function insertAtCursor(text) {
|
||||
const start = textarea.selectionStart;
|
||||
const end = textarea.selectionEnd;
|
||||
const v = textarea.value;
|
||||
textarea.value = v.slice(0, start) + text + v.slice(end);
|
||||
const pos = start + text.length;
|
||||
textarea.focus();
|
||||
textarea.setSelectionRange(pos, pos);
|
||||
refreshPlaceholders();
|
||||
}
|
||||
|
||||
insertSelect.addEventListener('change', function() {
|
||||
const key = insertSelect.value;
|
||||
insertSelect.value = '';
|
||||
if (key && SNIPPETS[key]) insertAtCursor(SNIPPETS[key]);
|
||||
});
|
||||
|
||||
let statusTimer = null;
|
||||
function flashStatus(text, kind, holdMs) {
|
||||
status.textContent = text;
|
||||
@@ -762,16 +712,9 @@ function renderDaeEditor() {
|
||||
ev.preventDefault();
|
||||
if (textarea.value.trim() && !confirm(_('This will replace your current config. Continue?')))
|
||||
return;
|
||||
init.disabled = true;
|
||||
flashStatus(_('Loading example…'));
|
||||
fs.read_direct(backend.BACKENDS.dae.example, 'text')
|
||||
.then(function(content) {
|
||||
textarea.value = simplifySubscriptionBlock(content || '');
|
||||
refreshPlaceholders();
|
||||
flashStatus(_('Example loaded — save to apply'), 'ok');
|
||||
})
|
||||
.catch(function(e) { flashStatus(_('Init failed: %s').format(e.message || e), 'err'); })
|
||||
.finally(function() { init.disabled = false; });
|
||||
textarea.value = DEFAULT_TEMPLATE;
|
||||
refreshPlaceholders();
|
||||
flashStatus(_('Default config loaded — replace the subscription link, then save'), 'ok');
|
||||
});
|
||||
|
||||
/* 一次性探测 dae --version;失败就显示 unknown,不影响主流程 */
|
||||
@@ -797,12 +740,12 @@ function renderDaeEditor() {
|
||||
E('div', { 'class': 'dd-editor-hint', style: 'margin:0 0 10px' }, [
|
||||
E('b', {}, _('Text-only mode.')),
|
||||
' ',
|
||||
_('Edit the dae DSL directly. Initialize loads a template. Use daed for a GUI.')
|
||||
_('Edit the dae DSL directly. "Load default config" gives a ready-to-run starting point. Use daed for a GUI.')
|
||||
]),
|
||||
phWarn,
|
||||
editWrap,
|
||||
footer,
|
||||
E('div', { 'class': 'dd-editor-actions' }, [ save, init, insertSelect, status ])
|
||||
E('div', { 'class': 'dd-editor-actions' }, [ save, init, status ])
|
||||
])
|
||||
]);
|
||||
adv.firstChild.addEventListener('click', function() { adv.classList.toggle('dd-closed'); });
|
||||
|
||||
@@ -107,8 +107,10 @@ const CSS = [
|
||||
'.dd-edit-wrap .dd-editor,.dd-edit-wrap .dd-hl{margin:0;padding:10px 12px;border-width:1px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono",monospace;font-size:12px;line-height:1.5;letter-spacing:0;tab-size:4;white-space:pre-wrap;word-break:break-word;box-sizing:border-box}',
|
||||
'.dd-edit-wrap .dd-hl{position:absolute;inset:0;margin:0;overflow:hidden;border:1px solid transparent;border-radius:6px 6px 0 0;pointer-events:none;background:#f6f8fa;color:#3b4252;z-index:1}',
|
||||
/* reset Argon's code{background:var(--lighter)} so the overlay inherits the
|
||||
pre background uniformly — otherwise a dark pre shows as side bars */
|
||||
'.dd-edit-wrap .dd-hl code{font:inherit;white-space:inherit;word-break:inherit;display:block;background:transparent !important}',
|
||||
pre background uniformly — otherwise a dark pre shows as side bars.
|
||||
color:inherit so plain text follows .dd-hl (light in dark mode) instead of
|
||||
the theme's own code{color}, which is dark and vanishes on a dark pre */
|
||||
'.dd-edit-wrap .dd-hl code{font:inherit;white-space:inherit;word-break:inherit;display:block;color:inherit !important;background:transparent !important}',
|
||||
'.dd-edit-wrap .dd-editor-hl{position:relative;z-index:2;color:transparent !important;background:transparent !important;caret-color:#2f7288;border:1px solid rgba(128,128,128,.28)}',
|
||||
'.dd-edit-wrap .dd-editor-hl::placeholder{color:rgba(128,128,128,.55)}',
|
||||
/* keep selected text transparent too — else the browser force-colors it and
|
||||
|
||||
Reference in new issue
Block a user