🤞 Sync 2026-09-29 21:07:16

This commit is contained in:
github-actions[bot] committed 2026-09-29 21:07:16 +08:00
1 parent 52b5905a85
commit 08343f89fa
231 files changed
+179633 -1348

No files matched your search

+38 -21
View File
@@ -14,12 +14,23 @@ function get_dashboard_dir(type) {
return DASHBOARD_DIRS[type] || DASHBOARD_DIRS.doona;
}
let cached_pid = null;
function get_honk_pid() {
if (cached_pid) {
let cmd = readfile("/proc/" + cached_pid + "/cmdline");
if (cmd && match(cmd, /honk-core/)) {
return cached_pid;
}
cached_pid = null;
}
let p = popen("pidof honk-core 2>/dev/null");
let pids = p ? p.read("all") : "";
if (p) p.close();
let m = match(pids, /([0-9]+)/);
return m ? m[1] : null;
cached_pid = m ? m[1] : null;
return cached_pid;
}
function parse_host_port(addr) {
@@ -166,13 +177,19 @@ function remove_bracket_block(content, header_regex) {
let trimmed = trim(line);
if (!in_block && match(trimmed, header_regex)) {
in_block = true;
depth = 1;
let open_cnt = length(split(trimmed, "{")) - 1;
let close_cnt = length(split(trimmed, "}")) - 1;
depth = open_cnt - close_cnt;
if (open_cnt > 0 && depth <= 0) {
in_block = false;
}
continue;
}
if (in_block) {
if (match(trimmed, /\{/)) depth++;
if (match(trimmed, /\}/)) depth--;
let open_cnt = length(split(trimmed, "{")) - 1;
let close_cnt = length(split(trimmed, "}")) - 1;
depth += (open_cnt - close_cnt);
if (depth <= 0) {
in_block = false;
}
@@ -184,6 +201,21 @@ function remove_bracket_block(content, header_regex) {
return join("\n", new_lines);
}
function get_uci_dashboard_type(u) {
if (!u) return null;
u.load("honk");
let requested_type = u.get("honk", "config", "dashboard");
if (!requested_type) {
u.foreach("honk", "honk", function(s) {
if (s.dashboard) {
requested_type = s.dashboard;
return false;
}
});
}
return requested_type;
}
function clean_legacy_api_from_config(config_file) {
let main_content = readfile(config_file);
if (!main_content) return;
@@ -206,13 +238,7 @@ function get_dashboard_info(req) {
let u = cursor();
let requested_type = (req && req.args) ? req.args.type : null;
if (!requested_type && u) {
u.load("honk");
requested_type = u.get("honk", "config", "dashboard");
if (!requested_type) {
u.foreach("honk", "honk", function(s) {
if (s.dashboard) requested_type = s.dashboard;
});
}
requested_type = get_uci_dashboard_type(u);
}
if (!requested_type) requested_type = "none";
@@ -274,16 +300,7 @@ function get_dashboard_info(req) {
function download_dashboard(req) {
let requested_type = (req && req.args) ? req.args.type : null;
if (!requested_type) {
let u = cursor();
if (u) {
u.load("honk");
requested_type = u.get("honk", "config", "dashboard");
if (!requested_type) {
u.foreach("honk", "honk", function(s) {
if (s.dashboard) requested_type = s.dashboard;
});
}
}
requested_type = get_uci_dashboard_type(cursor());
}
if (!requested_type || requested_type == "none") {
requested_type = "doona";
@@ -1,6 +1,7 @@
'use strict';
'require baseclass';
'require rpc';
'require uci';
'require fs';
'require ui';
'require poll';
@@ -88,13 +89,6 @@ var callHonkSwitchDashboardApi = rpc.declare({
expect: { }
});
var callUciGet = rpc.declare({
object: 'uci',
method: 'get',
params: [ 'config', 'section', 'option' ],
expect: { value: '' }
});
function readFile(path) {
if (fs.read_direct) {
return fs.read_direct(path).catch(function() {
@@ -140,6 +134,7 @@ function ensureEditorStyles() {
var style = document.createElement('style');
style.id = 'honk-editor-custom-style';
style.textContent = [
'.cbi-value.hidden { display: none !important; }',
'.honk-status-field { display: inline-flex !important; align-items: center !important; justify-content: flex-start !important; gap: 16px !important; flex-wrap: wrap !important; min-height: 32px !important; }',
'.honk-editor-toolbar { margin-bottom: 6px !important; margin-top: 0 !important; display: flex !important; align-items: center !important; justify-content: flex-start !important; }',
'.cm-format-btn { margin: 0 !important; cursor: pointer !important; }',
@@ -147,31 +142,22 @@ function ensureEditorStyles() {
'.cbi-value:has(.CodeMirror) > .cbi-value-title { padding-top: 5px !important; }',
'.cbi-value:has(.CodeMirror) .cbi-value-field { flex: 1 1 0% !important; min-width: 0 !important; width: auto !important; }',
'.CodeMirror {',
' border: 1px solid var(--hairline, var(--border-color-medium, #ccc)) !important;',
' border: 1px solid var(--border, var(--hairline, var(--border-color-medium, #ccc))) !important;',
' border-radius: var(--radius-base, 4px);',
' height: auto;',
' min-height: 480px;',
' font-family: var(--font-mono, monospace);',
' font-size: 13px;',
' background: var(--control-bg, var(--surface, #ffffff)) !important;',
' color: var(--text, inherit) !important;',
' background: var(--background, var(--control-bg, var(--background-color-high, #ffffff))) !important;',
' color: var(--foreground, var(--text, var(--text-color-highest, inherit))) !important;',
' box-shadow: none;',
'}',
'.CodeMirror-gutters {',
' border-right: 1px solid var(--hairline, var(--border-color-medium, #ccc)) !important;',
' background: var(--surface-sunken, var(--background-color-low, #f7f7f7)) !important;',
' border-right: 1px solid var(--border, var(--hairline, var(--border-color-medium, #ccc))) !important;',
' background: var(--surface-raised, var(--surface-sunken, var(--background-color-low, #f7f7f7))) !important;',
'}',
'.CodeMirror-linenumber { color: var(--text-muted, var(--text-color-low, #888888)) !important; }',
'.CodeMirror-cursor { border-left: 1px solid var(--text, currentColor) !important; }',
'[data-darkmode="true"] .CodeMirror, [data-theme="dark"] .CodeMirror, .dark .CodeMirror {',
' background: var(--control-bg, var(--surface, #141822)) !important;',
' color: var(--text, #f9fafb) !important;',
' border-color: var(--hairline, var(--border-color-medium, #334155)) !important;',
'}',
'[data-darkmode="true"] .CodeMirror-gutters, [data-theme="dark"] .CodeMirror-gutters, .dark .CodeMirror-gutters {',
' background: var(--surface-sunken, var(--background-color-low, #0a0e17)) !important;',
' border-right-color: var(--hairline, var(--border-color-medium, #334155)) !important;',
'}'
'.CodeMirror-linenumber { color: var(--muted-foreground, var(--text-muted, var(--text-color-low, #888888))) !important; }',
'.CodeMirror-cursor { border-left: 1px solid var(--foreground, var(--text, currentColor)) !important; }'
].join('\n');
document.head.appendChild(style);
}
@@ -213,6 +199,18 @@ function ensureCodeMirror() {
return _cmPromise;
}
function preloadCodeMirror() {
if (window.requestIdleCallback) {
requestIdleCallback(function() {
ensureCodeMirror().catch(function() {});
}, { timeout: 2000 });
} else {
setTimeout(function() {
ensureCodeMirror().catch(function() {});
}, 300);
}
}
function formatEditor(ed) {
ed.operation(function() {
var cursor = ed.getCursor();
@@ -315,17 +313,35 @@ function formatEditor(ed) {
function bindCodeMirrorToMap(m, onSaveCallback) {
if (!m || m._cmHooked) return;
m._cmHooked = true;
ensureEditorStyles();
var origRenderContents = m.renderContents;
m.renderContents = function() {
return origRenderContents.apply(this, arguments).then(function(mapNode) {
var target = mapNode || m.root || document.getElementById('cbi-' + m.config) || document;
target.querySelectorAll('textarea').forEach(function(ta) {
initCodeMirror(ta, onSaveCallback).then(function(editor) {
requestAnimationFrame(function() {
editor.refresh();
if (ta.dataset.cmInitialized === 'true' || ta._editor) return;
if (ta.offsetParent !== null) {
initCodeMirror(ta, onSaveCallback);
} else if (window.IntersectionObserver) {
var row = ta.closest('.cbi-value') || ta;
var io = new IntersectionObserver(function(entries) {
if (entries[0] && entries[0].isIntersecting) {
io.disconnect();
initCodeMirror(ta, onSaveCallback).then(function(editor) {
if (editor) {
requestAnimationFrame(function() {
editor.refresh();
});
}
});
}
});
});
io.observe(row);
} else {
initCodeMirror(ta, onSaveCallback);
}
});
return mapNode;
});
@@ -418,6 +434,10 @@ function initCodeMirror(textarea, onSaveCallback) {
function createConfigFileView(filePath, mapTitle, mapDesc, fieldTitle, successMsg, needRestart) {
return view.extend({
load: function() {
return uci.load('honk');
},
render: function() {
var m = new form.Map('honk', mapTitle, mapDesc);
@@ -457,41 +477,47 @@ function createConfigFileView(filePath, mapTitle, mapDesc, fieldTitle, successMs
}
function updateTabVisibilityFromSections(sections) {
var s = (sections && sections[0]) ? sections[0] : (uci.get_first('honk', 'honk') || {});
var isAdvanced = (s.advanced === '1');
var dashType = s.dashboard || '';
var hiddenTabs = [];
if (!isAdvanced) {
hiddenTabs.push('dns', 'node', 'route');
}
if (dashType === 'none') {
hiddenTabs.push('api');
}
applyTabCss(hiddenTabs);
var currentTab = (window.L && L.env && Array.isArray(L.env.dispatchpath)) ? L.env.dispatchpath[3] : '';
if (!currentTab) {
var m = window.location.pathname.match(/\/honk\/([a-z0-9_-]+)/);
if (m) currentTab = m[1];
}
if (currentTab && hiddenTabs.indexOf(currentTab) !== -1) {
window.location.href = L.url('admin/services/honk/global');
}
}
// Tab visibility control:
// - dns, node, route are hidden when advanced=0
// - api is hidden when dashboard=none
function applyTabVisibility() {
return Promise.all([
L.resolveDefault(callUciGet('honk', 'config', 'advanced'), '0'),
L.resolveDefault(callUciGet('honk', 'config', 'dashboard'), 'none')
]).then(function(res) {
var isAdvanced = (res[0] === '1');
var dashType = res[1] || 'none';
var sections = uci.sections('honk', 'honk');
if (sections && sections.length) {
updateTabVisibilityFromSections(sections);
return Promise.resolve();
}
var hiddenTabs = [];
if (!isAdvanced) {
hiddenTabs.push('dns', 'node', 'route');
}
if (dashType === 'none') {
hiddenTabs.push('api');
}
applyTabCss(hiddenTabs);
var currentTab = (window.L && L.env && Array.isArray(L.env.dispatchpath)) ? L.env.dispatchpath[3] : '';
if (!currentTab) {
var m = window.location.pathname.match(/\/honk\/([a-z0-9_-]+)/);
if (m) currentTab = m[1];
}
if (currentTab && hiddenTabs.indexOf(currentTab) !== -1) {
window.location.href = L.url('admin/services/honk/global');
}
return uci.load('honk').then(function() {
updateTabVisibilityFromSections(uci.sections('honk', 'honk'));
}).catch(function() {
});
}
var applyAdvancedTabVisibility = applyTabVisibility;
function applyTabCss(hiddenTabs) {
var styleId = 'honk-tab-visibility-style';
var existing = document.getElementById(styleId);
@@ -511,6 +537,14 @@ function applyTabCss(hiddenTabs) {
}
}
// Early synchronous check if UCI data is already loaded in memory
try {
var _earlySections = uci.sections('honk', 'honk');
if (_earlySections && _earlySections.length) {
updateTabVisibilityFromSections(_earlySections);
}
} catch (e) {}
function renderStatusHeader() {
var statusEl = E('span', { 'id': 'honk_status', 'style': 'font-weight: 500;' }, [
E('em', {}, _('Collecting data...'))
@@ -567,17 +601,17 @@ function renderStatusHeader() {
poll.add(function() {
return callHonkStatus().then(updateStatus);
}, 3);
}, 5);
applyTabVisibility();
return section;
}
preloadCodeMirror();
return baseclass.extend({
applyTabVisibility: applyTabVisibility,
applyAdvancedTabVisibility: applyTabVisibility,
callHonkStatus: callHonkStatus,
callHonkReload: callHonkReload,
callHonkRestart: callHonkRestart,
@@ -590,6 +624,7 @@ return baseclass.extend({
readFile: readFile,
writeFile: writeFile,
ensureCodeMirror: ensureCodeMirror,
preloadCodeMirror: preloadCodeMirror,
formatEditor: formatEditor,
initCodeMirror: initCodeMirror,
bindCodeMirrorToMap: bindCodeMirrorToMap,
@@ -17,9 +17,8 @@ return view.extend({
},
render: function() {
var applyTabs = (honk && (honk.applyTabVisibility || honk.applyAdvancedTabVisibility));
if (applyTabs) {
applyTabs();
if (honk && honk.applyTabVisibility) {
honk.applyTabVisibility();
}
var sec = (uci.sections('honk', 'honk')[0] || {});
var sid = sec['.name'] || 'config';
@@ -137,7 +136,7 @@ return view.extend({
' width: 100%;',
' height: calc(100vh - 210px);',
' min-height: 650px;',
' border: 1px solid var(--hairline, var(--border-color-medium, #ccc));',
' border: 1px solid var(--border, var(--hairline, var(--border-color-medium, #ccc)));',
' border-radius: var(--radius-base, 4px);',
' display: block;',
'}',
@@ -154,7 +153,7 @@ return view.extend({
'.dash-toolbar-actions > * { white-space: nowrap !important; flex-shrink: 0; }',
'@media (max-width: 768px) {',
' #dash_iframe { border-right: none; border-radius: var(--radius-base,4px) 0 0 var(--radius-base,4px); }',
' #dash_scroll_handle { display: flex; align-items: center; justify-content: center; width: 14px; flex-shrink: 0; touch-action: none; user-select: none; border: 1px solid var(--hairline,var(--border-color-medium,#ccc)); border-radius: 0 var(--radius-base,4px) var(--radius-base,4px) 0; }',
' #dash_scroll_handle { display: flex; align-items: center; justify-content: center; width: 14px; flex-shrink: 0; touch-action: none; user-select: none; border: 1px solid var(--border, var(--hairline, var(--border-color-medium, #ccc))); border-radius: 0 var(--radius-base,4px) var(--radius-base,4px) 0; }',
' .dash-toolbar-actions { width: 100%; display: flex; flex-wrap: wrap; gap: 6px; }',
' .dash-toolbar-actions select { flex: 1 1 auto; min-width: 90px; margin-right: 0 !important; }',
' .dash-toolbar-actions .btn, .dash-toolbar-actions .cbi-button { padding: 4px 8px !important; font-size: 12px !important; white-space: nowrap !important; flex: 0 0 auto; }',
@@ -162,8 +161,8 @@ return view.extend({
'.dash-log-box {',
' max-height: 150px; overflow-y: auto; font-family: var(--font-mono, monospace);',
' font-size: 12px; line-height: 1.4; padding: 8px; margin: 8px 0;',
' background: var(--surface-sunken, var(--background-color-low, transparent));',
' border: 1px solid var(--hairline, var(--border-color-medium, #ccc));',
' background: var(--surface-raised, var(--surface-sunken, var(--background-color-low, transparent)));',
' border: 1px solid var(--border, var(--hairline, var(--border-color-medium, #ccc)));',
' border-radius: var(--radius-base, 3px); white-space: pre-wrap; word-break: break-all;',
'}'
].join('\n'));
@@ -644,9 +643,11 @@ return view.extend({
httpsAlert.style.display = 'none';
}
var normalizedSrc = (iframe.src || '').replace(/\/$/, '');
var normalizedFullUrl = (fullUrl || '').replace(/\/$/, '');
if (forceReload) {
reloadIframe(true);
} else if (!iframeLoaded || iframe.src !== fullUrl) {
} else if (!iframeLoaded || normalizedSrc !== normalizedFullUrl) {
iframe.src = fullUrl;
iframeLoaded = true;
}
@@ -660,6 +661,9 @@ return view.extend({
loadInfo();
poll.add(function() {
if (document.hidden) {
return Promise.resolve();
}
if (currentInfo && currentInfo.configured && currentInfo.has_ui) {
return honk.callHonkStatus().then(function(res) {
var isRunning = (res && res.running);
@@ -1,18 +1,22 @@
'use strict';
'require view';
'require ui';
'require uci';
'require poll';
'require honk.common as honk';
return view.extend({
load: function() {
return uci.load('honk');
},
handleSaveApply: null,
handleSave: null,
handleReset: null,
render: function() {
var applyTabs = (honk && (honk.applyTabVisibility || honk.applyAdvancedTabVisibility));
if (applyTabs) {
applyTabs();
if (honk && honk.applyTabVisibility) {
honk.applyTabVisibility();
}
var scrolled = false;
@@ -47,8 +51,9 @@ return view.extend({
function updateLog() {
return honk.callHonkGetLog().then(function(data) {
var content = (data && data.log) ? data.log : '';
var atBottom = !scrolled || (logTextarea.scrollHeight - logTextarea.scrollTop - logTextarea.clientHeight < 50);
logTextarea.value = content;
if (!scrolled && content) {
if (atBottom && content) {
logTextarea.scrollTop = logTextarea.scrollHeight;
scrolled = true;
}
@@ -58,9 +63,18 @@ return view.extend({
updateLog();
poll.add(function() {
if (document.hidden) {
return Promise.resolve();
}
return updateLog();
}, 3);
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
updateLog();
}
});
return E('fieldset', { 'class': 'cbi-section', 'id': '_log_fieldset' }, [
E('legend', {}, _('Logs')),
E('div', { 'style': 'margin-bottom: 10px;' }, [ btnClear ]),