mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-03 07:03:56 +08:00
🤞 Sync 2026-09-29 21:07:16
This commit is contained in:
1 parent
52b5905a85
commit
08343f89fa
231 files changed
+179633
-1348
No files matched your search
@@ -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 ]),
|
||||
|
||||
Reference in new issue
Block a user