perf(luci): eliminate excessive router CPU load during LuCI session
- Optimize get_ui_state backend by reading /proc natively instead of spawning pidof - Avoid spawning readlink and redundant ubus queries in state.uc - Cache positive tachyon_runtime_network_configured checks for 5s - Back off idle UI state polling interval from 3s to 10s (60s when tab is hidden) - Back off TachyonLogWatcher interval to 30s - Pause Dashboard and Monitoring polling when browser tab is inactive (Page Visibility API) - Fix cleanTabId to ensure inactive tab pollers and timers unmount properly
This commit is contained in:
@@ -5,7 +5,7 @@ export function isActiveLuciTab(tabId: string) {
|
|||||||
|
|
||||||
return Boolean(
|
return Boolean(
|
||||||
document.querySelector(
|
document.querySelector(
|
||||||
`.cbi-tab[data-tab="${tabId}"]:not(.cbi-tab-disabled)`,
|
`.cbi-tab[data-tab="${tabId}"]:not(.cbi-tab-disabled), .cbi-tab[data-tab="cbi-tachyon-${tabId}"]:not(.cbi-tab-disabled)`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ type CoreServiceOptions = {
|
|||||||
logWatcherStartDelayMs?: number;
|
logWatcherStartDelayMs?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const LOG_WATCHER_INTERVAL_MS = 10000;
|
const LOG_WATCHER_INTERVAL_MS = 30000;
|
||||||
const LOG_WATCHER_START_DELAY_MS = 5000;
|
const LOG_WATCHER_START_DELAY_MS = 5000;
|
||||||
|
|
||||||
function componentDisplayName(component: string) {
|
function componentDisplayName(component: string) {
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import { logger } from './logger.service';
|
|||||||
import { applyUiStateToStore } from './uiState.service';
|
import { applyUiStateToStore } from './uiState.service';
|
||||||
|
|
||||||
const RUNTIME_UI_STATE_REFRESH_MIN_INTERVAL_MS = 500;
|
const RUNTIME_UI_STATE_REFRESH_MIN_INTERVAL_MS = 500;
|
||||||
const RUNTIME_UI_STATE_IDLE_POLL_INTERVAL_MS = 3000;
|
const RUNTIME_UI_STATE_IDLE_POLL_INTERVAL_MS = 10_000;
|
||||||
const RUNTIME_UI_STATE_ACTIVE_POLL_INTERVAL_MS = 500;
|
const RUNTIME_UI_STATE_ACTIVE_POLL_INTERVAL_MS = 500;
|
||||||
// A hidden tab must not keep spawning shell commands on the router every
|
// A hidden tab must not keep spawning shell commands on the router:
|
||||||
// second: back off to one request per 30s while hidden (a force refresh on
|
// back off to one request per 60s while hidden (a force refresh on
|
||||||
// visibilitychange covers the moment the tab comes back).
|
// visibilitychange covers the moment the tab comes back).
|
||||||
const RUNTIME_UI_STATE_HIDDEN_POLL_INTERVAL_MS = 30_000;
|
const RUNTIME_UI_STATE_HIDDEN_POLL_INTERVAL_MS = 60_000;
|
||||||
type RuntimeUiStateListener = (uiState: Tachyon.UiState) => void;
|
type RuntimeUiStateListener = (uiState: Tachyon.UiState) => void;
|
||||||
|
|
||||||
let runtimeUiStateRefreshPromise: Promise<Tachyon.UiState | undefined> | null =
|
let runtimeUiStateRefreshPromise: Promise<Tachyon.UiState | undefined> | null =
|
||||||
|
|||||||
@@ -39,13 +39,18 @@ class TabService {
|
|||||||
this.notify();
|
this.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private cleanTabId(tab: string | null | undefined): string | null {
|
||||||
|
if (!tab) return null;
|
||||||
|
return tab.replace(/^(?:cbi-tachyon-|tab-)/, '');
|
||||||
|
}
|
||||||
|
|
||||||
private getTabsInfo(): TabInfo[] {
|
private getTabsInfo(): TabInfo[] {
|
||||||
const tabs = Array.from(
|
const tabs = Array.from(
|
||||||
document.querySelectorAll<HTMLElement>('.cbi-tab, .cbi-tab-disabled'),
|
document.querySelectorAll<HTMLElement>('.cbi-tab, .cbi-tab-disabled'),
|
||||||
);
|
);
|
||||||
return tabs.map((el) => ({
|
return tabs.map((el) => ({
|
||||||
el,
|
el,
|
||||||
id: el.dataset.tab || '',
|
id: this.cleanTabId(el.dataset.tab) || '',
|
||||||
active:
|
active:
|
||||||
el.classList.contains('cbi-tab') &&
|
el.classList.contains('cbi-tab') &&
|
||||||
!el.classList.contains('cbi-tab-disabled'),
|
!el.classList.contains('cbi-tab-disabled'),
|
||||||
@@ -56,7 +61,7 @@ class TabService {
|
|||||||
const active = document.querySelector<HTMLElement>(
|
const active = document.querySelector<HTMLElement>(
|
||||||
'.cbi-tab:not(.cbi-tab-disabled)',
|
'.cbi-tab:not(.cbi-tab-disabled)',
|
||||||
);
|
);
|
||||||
return active?.dataset.tab || null;
|
return this.cleanTabId(active?.dataset.tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
private notify() {
|
private notify() {
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ import { shouldShowLoadingForRestoredAction } from '../../helpers/restoredAction
|
|||||||
import { getServiceAvailability } from '../../helpers/serviceAvailability';
|
import { getServiceAvailability } from '../../helpers/serviceAvailability';
|
||||||
import { capSetSize, capMapSize } from '../../helpers/capCollectionSize';
|
import { capSetSize, capMapSize } from '../../helpers/capCollectionSize';
|
||||||
|
|
||||||
const SECTIONS_REFRESH_INTERVAL_MS = 10000;
|
const SECTIONS_REFRESH_INTERVAL_MS = 15000;
|
||||||
const LATENCY_TEST_BUTTON_CLASS = 'dashboard-sections-grid-item-test-latency';
|
const LATENCY_TEST_BUTTON_CLASS = 'dashboard-sections-grid-item-test-latency';
|
||||||
const LATENCY_TEST_BUTTON_LABEL_CLASS =
|
const LATENCY_TEST_BUTTON_LABEL_CLASS =
|
||||||
'dashboard-sections-grid-item-test-latency__label';
|
'dashboard-sections-grid-item-test-latency__label';
|
||||||
@@ -96,6 +96,8 @@ const customProxyLatencies = new Map<string, number>();
|
|||||||
const singleTestingOutboundCodes: Record<string, boolean> = {};
|
const singleTestingOutboundCodes: Record<string, boolean> = {};
|
||||||
let isTestingAllSections = false;
|
let isTestingAllSections = false;
|
||||||
|
|
||||||
|
let dashboardVisibilityPaused = false;
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window.addEventListener('pagehide', () => {
|
window.addEventListener('pagehide', () => {
|
||||||
pageUnloading = true;
|
pageUnloading = true;
|
||||||
@@ -105,6 +107,25 @@ if (typeof window !== 'undefined') {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof document !== 'undefined' &&
|
||||||
|
typeof document.addEventListener === 'function'
|
||||||
|
) {
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
if (dashboardDataUpdatesStarted) {
|
||||||
|
stopDashboardDataUpdates();
|
||||||
|
dashboardVisibilityPaused = true;
|
||||||
|
}
|
||||||
|
} else if (dashboardVisibilityPaused) {
|
||||||
|
dashboardVisibilityPaused = false;
|
||||||
|
if (dashboardMounted && getDashboardServiceAvailability() !== 'stopped') {
|
||||||
|
startDashboardDataUpdates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Fetchers
|
// Fetchers
|
||||||
|
|
||||||
async function fetchDashboardSectionsOnce(mountId: number) {
|
async function fetchDashboardSectionsOnce(mountId: number) {
|
||||||
@@ -640,6 +661,11 @@ function startDashboardDataUpdates() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof document !== 'undefined' && document.hidden) {
|
||||||
|
dashboardVisibilityPaused = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dashboardDataUpdatesStarted = true;
|
dashboardDataUpdatesStarted = true;
|
||||||
const dataUpdatesId = ++dashboardDataUpdatesId;
|
const dataUpdatesId = ++dashboardDataUpdatesId;
|
||||||
void fetchDashboardSections({ force: true });
|
void fetchDashboardSections({ force: true });
|
||||||
@@ -2026,8 +2052,12 @@ function renderStoreWidget(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function fetchConnections() {
|
async function fetchConnections() {
|
||||||
|
const shouldFetchHostnames = expandedSections.has('active_clients');
|
||||||
|
const needsFallbackPolling = directSocketsFailed || !canUseDirectClashApi();
|
||||||
|
if (!needsFallbackPolling && !shouldFetchHostnames) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const shouldFetchHostnames = expandedSections.has('active_clients');
|
|
||||||
const [res, hostnames] = await Promise.all([
|
const [res, hostnames] = await Promise.all([
|
||||||
TachyonShellMethods.getClashApiConnections(),
|
TachyonShellMethods.getClashApiConnections(),
|
||||||
shouldFetchHostnames
|
shouldFetchHostnames
|
||||||
@@ -2037,7 +2067,7 @@ async function fetchConnections() {
|
|||||||
if (res.success && res.data && typeof res.data === 'object') {
|
if (res.success && res.data && typeof res.data === 'object') {
|
||||||
const payload = res.data as any;
|
const payload = res.data as any;
|
||||||
|
|
||||||
if (directSocketsFailed || !canUseDirectClashApi()) {
|
if (needsFallbackPolling) {
|
||||||
const downloadTotal = Number(payload.downloadTotal) || 0;
|
const downloadTotal = Number(payload.downloadTotal) || 0;
|
||||||
const uploadTotal = Number(payload.uploadTotal) || 0;
|
const uploadTotal = Number(payload.uploadTotal) || 0;
|
||||||
const memory = Number(payload.memory) || 0;
|
const memory = Number(payload.memory) || 0;
|
||||||
@@ -2391,6 +2421,7 @@ async function onPageMount() {
|
|||||||
function onPageUnmount() {
|
function onPageUnmount() {
|
||||||
dashboardMounted = false;
|
dashboardMounted = false;
|
||||||
dashboardMountId += 1;
|
dashboardMountId += 1;
|
||||||
|
dashboardVisibilityPaused = false;
|
||||||
|
|
||||||
directSocketsFailed = false;
|
directSocketsFailed = false;
|
||||||
lastTrafficPollTime = 0;
|
lastTrafficPollTime = 0;
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ function normalizeConnectionsPayload(value: unknown): ClashConnectionsPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const RENDER_INTERVAL_MS = 500;
|
const RENDER_INTERVAL_MS = 500;
|
||||||
const CONNECTIONS_RPC_POLL_INTERVAL_MS = 1500;
|
const CONNECTIONS_RPC_POLL_INTERVAL_MS = 3000;
|
||||||
const CLOSED_CONNECTION_LIMIT = 300;
|
const CLOSED_CONNECTION_LIMIT = 300;
|
||||||
const ALL_FILTER_VALUE = 'all';
|
const ALL_FILTER_VALUE = 'all';
|
||||||
|
|
||||||
@@ -1826,9 +1826,51 @@ async function onPageMount() {
|
|||||||
}, RENDER_INTERVAL_MS);
|
}, RENDER_INTERVAL_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let monitoringVisibilityPaused = false;
|
||||||
|
|
||||||
|
function pauseMonitoringUpdates() {
|
||||||
|
if (!monitoringMounted || monitoringVisibilityPaused) return;
|
||||||
|
monitoringVisibilityPaused = true;
|
||||||
|
if (renderTimer) {
|
||||||
|
clearInterval(renderTimer);
|
||||||
|
renderTimer = null;
|
||||||
|
}
|
||||||
|
stopConnectionsUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resumeMonitoringUpdates() {
|
||||||
|
if (!monitoringMounted || !monitoringVisibilityPaused) return;
|
||||||
|
monitoringVisibilityPaused = false;
|
||||||
|
if (serviceAvailability === 'running') {
|
||||||
|
startConnectionsUpdates();
|
||||||
|
}
|
||||||
|
if (!renderTimer) {
|
||||||
|
renderTimer = setInterval(() => {
|
||||||
|
if (monitoringPaused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
renderConnections();
|
||||||
|
}, RENDER_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof document !== 'undefined' &&
|
||||||
|
typeof document.addEventListener === 'function'
|
||||||
|
) {
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
pauseMonitoringUpdates();
|
||||||
|
} else {
|
||||||
|
resumeMonitoringUpdates();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function onPageUnmount() {
|
function onPageUnmount() {
|
||||||
monitoringMounted = false;
|
monitoringMounted = false;
|
||||||
monitoringMountId += 1;
|
monitoringMountId += 1;
|
||||||
|
monitoringVisibilityPaused = false;
|
||||||
|
|
||||||
if (renderTimer) {
|
if (renderTimer) {
|
||||||
clearInterval(renderTimer);
|
clearInterval(renderTimer);
|
||||||
|
|||||||
@@ -5721,13 +5721,17 @@ var TabService = class _TabService {
|
|||||||
handleMutations() {
|
handleMutations() {
|
||||||
this.notify();
|
this.notify();
|
||||||
}
|
}
|
||||||
|
cleanTabId(tab) {
|
||||||
|
if (!tab) return null;
|
||||||
|
return tab.replace(/^(?:cbi-tachyon-|tab-)/, "");
|
||||||
|
}
|
||||||
getTabsInfo() {
|
getTabsInfo() {
|
||||||
const tabs = Array.from(
|
const tabs = Array.from(
|
||||||
document.querySelectorAll(".cbi-tab, .cbi-tab-disabled")
|
document.querySelectorAll(".cbi-tab, .cbi-tab-disabled")
|
||||||
);
|
);
|
||||||
return tabs.map((el) => ({
|
return tabs.map((el) => ({
|
||||||
el,
|
el,
|
||||||
id: el.dataset.tab || "",
|
id: this.cleanTabId(el.dataset.tab) || "",
|
||||||
active: el.classList.contains("cbi-tab") && !el.classList.contains("cbi-tab-disabled")
|
active: el.classList.contains("cbi-tab") && !el.classList.contains("cbi-tab-disabled")
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -5735,7 +5739,7 @@ var TabService = class _TabService {
|
|||||||
const active = document.querySelector(
|
const active = document.querySelector(
|
||||||
".cbi-tab:not(.cbi-tab-disabled)"
|
".cbi-tab:not(.cbi-tab-disabled)"
|
||||||
);
|
);
|
||||||
return active?.dataset.tab || null;
|
return this.cleanTabId(active?.dataset.tab);
|
||||||
}
|
}
|
||||||
notify() {
|
notify() {
|
||||||
const tabs = this.getTabsInfo();
|
const tabs = this.getTabsInfo();
|
||||||
@@ -6711,9 +6715,9 @@ function applyUiStateToStore(uiState) {
|
|||||||
|
|
||||||
// src/tachyon/services/runtimeUiState.service.ts
|
// src/tachyon/services/runtimeUiState.service.ts
|
||||||
var RUNTIME_UI_STATE_REFRESH_MIN_INTERVAL_MS = 500;
|
var RUNTIME_UI_STATE_REFRESH_MIN_INTERVAL_MS = 500;
|
||||||
var RUNTIME_UI_STATE_IDLE_POLL_INTERVAL_MS = 3e3;
|
var RUNTIME_UI_STATE_IDLE_POLL_INTERVAL_MS = 1e4;
|
||||||
var RUNTIME_UI_STATE_ACTIVE_POLL_INTERVAL_MS = 500;
|
var RUNTIME_UI_STATE_ACTIVE_POLL_INTERVAL_MS = 500;
|
||||||
var RUNTIME_UI_STATE_HIDDEN_POLL_INTERVAL_MS = 3e4;
|
var RUNTIME_UI_STATE_HIDDEN_POLL_INTERVAL_MS = 6e4;
|
||||||
var runtimeUiStateRefreshPromise = null;
|
var runtimeUiStateRefreshPromise = null;
|
||||||
var lastRuntimeUiStateRefreshAt = 0;
|
var lastRuntimeUiStateRefreshAt = 0;
|
||||||
var lastRuntimeUiState;
|
var lastRuntimeUiState;
|
||||||
@@ -6832,7 +6836,7 @@ function startRuntimeUiStatePolling() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// src/tachyon/services/core.service.ts
|
// src/tachyon/services/core.service.ts
|
||||||
var LOG_WATCHER_INTERVAL_MS = 1e4;
|
var LOG_WATCHER_INTERVAL_MS = 3e4;
|
||||||
var LOG_WATCHER_START_DELAY_MS = 5e3;
|
var LOG_WATCHER_START_DELAY_MS = 5e3;
|
||||||
function componentDisplayName(component) {
|
function componentDisplayName(component) {
|
||||||
const names = {
|
const names = {
|
||||||
@@ -7299,7 +7303,7 @@ function isActiveLuciTab(tabId) {
|
|||||||
}
|
}
|
||||||
return Boolean(
|
return Boolean(
|
||||||
document.querySelector(
|
document.querySelector(
|
||||||
`.cbi-tab[data-tab="${tabId}"]:not(.cbi-tab-disabled)`
|
`.cbi-tab[data-tab="${tabId}"]:not(.cbi-tab-disabled), .cbi-tab[data-tab="cbi-tachyon-${tabId}"]:not(.cbi-tab-disabled)`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -7366,7 +7370,7 @@ function toggleSectionExpanded(sectionCode) {
|
|||||||
void renderSectionsWidget();
|
void renderSectionsWidget();
|
||||||
void renderConnectionsWidget();
|
void renderConnectionsWidget();
|
||||||
}
|
}
|
||||||
var SECTIONS_REFRESH_INTERVAL_MS = 1e4;
|
var SECTIONS_REFRESH_INTERVAL_MS = 15e3;
|
||||||
var LATENCY_TEST_BUTTON_CLASS = "dashboard-sections-grid-item-test-latency";
|
var LATENCY_TEST_BUTTON_CLASS = "dashboard-sections-grid-item-test-latency";
|
||||||
var LATENCY_TEST_BUTTON_LABEL_CLASS = "dashboard-sections-grid-item-test-latency__label";
|
var LATENCY_TEST_BUTTON_LABEL_CLASS = "dashboard-sections-grid-item-test-latency__label";
|
||||||
var sectionsRefreshTimer = null;
|
var sectionsRefreshTimer = null;
|
||||||
@@ -7391,6 +7395,7 @@ var handledLatencyJobs = /* @__PURE__ */ new Set();
|
|||||||
var customProxyLatencies = /* @__PURE__ */ new Map();
|
var customProxyLatencies = /* @__PURE__ */ new Map();
|
||||||
var singleTestingOutboundCodes = {};
|
var singleTestingOutboundCodes = {};
|
||||||
var isTestingAllSections = false;
|
var isTestingAllSections = false;
|
||||||
|
var dashboardVisibilityPaused = false;
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
window.addEventListener("pagehide", () => {
|
window.addEventListener("pagehide", () => {
|
||||||
pageUnloading = true;
|
pageUnloading = true;
|
||||||
@@ -7399,6 +7404,21 @@ if (typeof window !== "undefined") {
|
|||||||
pageUnloading = false;
|
pageUnloading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (typeof document !== "undefined" && typeof document.addEventListener === "function") {
|
||||||
|
document.addEventListener("visibilitychange", () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
if (dashboardDataUpdatesStarted) {
|
||||||
|
stopDashboardDataUpdates();
|
||||||
|
dashboardVisibilityPaused = true;
|
||||||
|
}
|
||||||
|
} else if (dashboardVisibilityPaused) {
|
||||||
|
dashboardVisibilityPaused = false;
|
||||||
|
if (dashboardMounted && getDashboardServiceAvailability() !== "stopped") {
|
||||||
|
startDashboardDataUpdates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
async function fetchDashboardSectionsOnce(mountId) {
|
async function fetchDashboardSectionsOnce(mountId) {
|
||||||
if (getDashboardServiceAvailability() === "stopped") {
|
if (getDashboardServiceAvailability() === "stopped") {
|
||||||
return false;
|
return false;
|
||||||
@@ -7793,6 +7813,10 @@ function startDashboardDataUpdates() {
|
|||||||
if (dashboardDataUpdatesStarted || !dashboardMounted || getDashboardServiceAvailability() === "stopped") {
|
if (dashboardDataUpdatesStarted || !dashboardMounted || getDashboardServiceAvailability() === "stopped") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (typeof document !== "undefined" && document.hidden) {
|
||||||
|
dashboardVisibilityPaused = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
dashboardDataUpdatesStarted = true;
|
dashboardDataUpdatesStarted = true;
|
||||||
const dataUpdatesId = ++dashboardDataUpdatesId;
|
const dataUpdatesId = ++dashboardDataUpdatesId;
|
||||||
void fetchDashboardSections({ force: true });
|
void fetchDashboardSections({ force: true });
|
||||||
@@ -8888,15 +8912,19 @@ function renderStoreWidget(containerId, storeKey, title, getItems, debugName) {
|
|||||||
container.replaceChildren(renderedWidget);
|
container.replaceChildren(renderedWidget);
|
||||||
}
|
}
|
||||||
async function fetchConnections() {
|
async function fetchConnections() {
|
||||||
|
const shouldFetchHostnames = expandedSections.has("active_clients");
|
||||||
|
const needsFallbackPolling = directSocketsFailed || !canUseDirectClashApi();
|
||||||
|
if (!needsFallbackPolling && !shouldFetchHostnames) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const shouldFetchHostnames = expandedSections.has("active_clients");
|
|
||||||
const [res, hostnames] = await Promise.all([
|
const [res, hostnames] = await Promise.all([
|
||||||
TachyonShellMethods.getClashApiConnections(),
|
TachyonShellMethods.getClashApiConnections(),
|
||||||
shouldFetchHostnames ? fetchHostnames() : Promise.resolve(/* @__PURE__ */ new Map())
|
shouldFetchHostnames ? fetchHostnames() : Promise.resolve(/* @__PURE__ */ new Map())
|
||||||
]);
|
]);
|
||||||
if (res.success && res.data && typeof res.data === "object") {
|
if (res.success && res.data && typeof res.data === "object") {
|
||||||
const payload = res.data;
|
const payload = res.data;
|
||||||
if (directSocketsFailed || !canUseDirectClashApi()) {
|
if (needsFallbackPolling) {
|
||||||
const downloadTotal = Number(payload.downloadTotal) || 0;
|
const downloadTotal = Number(payload.downloadTotal) || 0;
|
||||||
const uploadTotal = Number(payload.uploadTotal) || 0;
|
const uploadTotal = Number(payload.uploadTotal) || 0;
|
||||||
const memory = Number(payload.memory) || 0;
|
const memory = Number(payload.memory) || 0;
|
||||||
@@ -9185,6 +9213,7 @@ async function onPageMount() {
|
|||||||
function onPageUnmount() {
|
function onPageUnmount() {
|
||||||
dashboardMounted = false;
|
dashboardMounted = false;
|
||||||
dashboardMountId += 1;
|
dashboardMountId += 1;
|
||||||
|
dashboardVisibilityPaused = false;
|
||||||
directSocketsFailed = false;
|
directSocketsFailed = false;
|
||||||
lastTrafficPollTime = 0;
|
lastTrafficPollTime = 0;
|
||||||
lastUploadTotal = 0;
|
lastUploadTotal = 0;
|
||||||
@@ -17966,7 +17995,7 @@ function normalizeConnectionsPayload(value) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
var RENDER_INTERVAL_MS = 500;
|
var RENDER_INTERVAL_MS = 500;
|
||||||
var CONNECTIONS_RPC_POLL_INTERVAL_MS = 1500;
|
var CONNECTIONS_RPC_POLL_INTERVAL_MS = 3e3;
|
||||||
var CLOSED_CONNECTION_LIMIT = 300;
|
var CLOSED_CONNECTION_LIMIT = 300;
|
||||||
var ALL_FILTER_VALUE = "all";
|
var ALL_FILTER_VALUE = "all";
|
||||||
var dependencies = {};
|
var dependencies = {};
|
||||||
@@ -19301,9 +19330,44 @@ async function onPageMount3() {
|
|||||||
renderConnections2();
|
renderConnections2();
|
||||||
}, RENDER_INTERVAL_MS);
|
}, RENDER_INTERVAL_MS);
|
||||||
}
|
}
|
||||||
|
var monitoringVisibilityPaused = false;
|
||||||
|
function pauseMonitoringUpdates() {
|
||||||
|
if (!monitoringMounted || monitoringVisibilityPaused) return;
|
||||||
|
monitoringVisibilityPaused = true;
|
||||||
|
if (renderTimer) {
|
||||||
|
clearInterval(renderTimer);
|
||||||
|
renderTimer = null;
|
||||||
|
}
|
||||||
|
stopConnectionsUpdates();
|
||||||
|
}
|
||||||
|
function resumeMonitoringUpdates() {
|
||||||
|
if (!monitoringMounted || !monitoringVisibilityPaused) return;
|
||||||
|
monitoringVisibilityPaused = false;
|
||||||
|
if (serviceAvailability === "running") {
|
||||||
|
startConnectionsUpdates();
|
||||||
|
}
|
||||||
|
if (!renderTimer) {
|
||||||
|
renderTimer = setInterval(() => {
|
||||||
|
if (monitoringPaused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
renderConnections2();
|
||||||
|
}, RENDER_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof document !== "undefined" && typeof document.addEventListener === "function") {
|
||||||
|
document.addEventListener("visibilitychange", () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
pauseMonitoringUpdates();
|
||||||
|
} else {
|
||||||
|
resumeMonitoringUpdates();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
function onPageUnmount3() {
|
function onPageUnmount3() {
|
||||||
monitoringMounted = false;
|
monitoringMounted = false;
|
||||||
monitoringMountId += 1;
|
monitoringMountId += 1;
|
||||||
|
monitoringVisibilityPaused = false;
|
||||||
if (renderTimer) {
|
if (renderTimer) {
|
||||||
clearInterval(renderTimer);
|
clearInterval(renderTimer);
|
||||||
renderTimer = null;
|
renderTimer = null;
|
||||||
|
|||||||
@@ -432,10 +432,24 @@ function pid_is_sing_box(pid) {
|
|||||||
if (match(pid, /^[0-9]+$/) == null)
|
if (match(pid, /^[0-9]+$/) == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
let exe = fs.readlink("/proc/" + pid + "/exe");
|
||||||
|
if (exe != null && exe != "")
|
||||||
|
return path_basename(exe) == "sing-box";
|
||||||
|
|
||||||
|
let comm = fs.readfile("/proc/" + pid + "/comm");
|
||||||
|
if (comm != null)
|
||||||
|
return trim(comm) == "sing-box";
|
||||||
|
|
||||||
return path_basename(command_trimmed_output_from_args([ "readlink", "/proc/" + pid + "/exe" ])) == "sing-box";
|
return path_basename(command_trimmed_output_from_args([ "readlink", "/proc/" + pid + "/exe" ])) == "sing-box";
|
||||||
}
|
}
|
||||||
|
|
||||||
function sing_box_service_pid_runtime() {
|
function sing_box_service_pid_runtime() {
|
||||||
|
for (let path in [ "/var/run/sing-box.pid", "/var/run/sing-box/sing-box.pid" ]) {
|
||||||
|
let pid = int(trim(fs.readfile(path) || "0"));
|
||||||
|
if (pid > 0 && pid_is_sing_box(pid))
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
let data = command_output_from_args([ "ubus", "call", "service", "list", "{\"name\":\"sing-box\"}" ]);
|
let data = command_output_from_args([ "ubus", "call", "service", "list", "{\"name\":\"sing-box\"}" ]);
|
||||||
try {
|
try {
|
||||||
let pid = sing_box_service_pid_from_value(json(data));
|
let pid = sing_box_service_pid_from_value(json(data));
|
||||||
@@ -444,14 +458,7 @@ function sing_box_service_pid_runtime() {
|
|||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
// ubus is unavailable or answered with something that is not the
|
// ubus is unavailable or answered with something that is not the
|
||||||
// service list; the pid files below are the fallback that exists for
|
// service list; the fallback below handles this case.
|
||||||
// exactly this case.
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let path in [ "/var/run/sing-box.pid", "/var/run/sing-box/sing-box.pid" ]) {
|
|
||||||
let pid = int(trim(fs.readfile(path) || "0"));
|
|
||||||
if (pid > 0 && pid_is_sing_box(pid))
|
|
||||||
return pid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let pids = split(trim(command_trimmed_output_from_args([ "pidof", "sing-box" ])), /[ \t\r\n]+/);
|
let pids = split(trim(command_trimmed_output_from_args([ "pidof", "sing-box" ])), /[ \t\r\n]+/);
|
||||||
@@ -611,9 +618,25 @@ function sing_box_service_stable(min_age) {
|
|||||||
return age != null && age >= min_age;
|
return age != null && age >= min_age;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let network_check_cache = {
|
||||||
|
key: "",
|
||||||
|
checked_at: 0,
|
||||||
|
result: false
|
||||||
|
};
|
||||||
|
|
||||||
function tachyon_runtime_network_configured(rt_table, nft_table, mark) {
|
function tachyon_runtime_network_configured(rt_table, nft_table, mark) {
|
||||||
return command_success_from_args([ "nft", "list", "table", "inet", nft_table ]) &&
|
let key = as_string(rt_table) + ":" + as_string(nft_table) + ":" + as_string(mark);
|
||||||
|
let now = int(clock()[0]);
|
||||||
|
if (network_check_cache.key == key && network_check_cache.result && (now - network_check_cache.checked_at) < 5)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
let result = command_success_from_args([ "nft", "list", "table", "inet", nft_table ]) &&
|
||||||
command_success_from_args([ "ucode", "-L", LIB_DIR, LIB_DIR + "/nft/apply.uc", "tproxy-route-rule-present", rt_table, mark ]);
|
command_success_from_args([ "ucode", "-L", LIB_DIR, LIB_DIR + "/nft/apply.uc", "tproxy-route-rule-present", rt_table, mark ]);
|
||||||
|
|
||||||
|
network_check_cache.key = key;
|
||||||
|
network_check_cache.checked_at = now;
|
||||||
|
network_check_cache.result = result;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function tachyon_running(rt_table, nft_table, mark) {
|
function tachyon_running(rt_table, nft_table, mark) {
|
||||||
|
|||||||
@@ -1049,9 +1049,32 @@ function ui_capabilities_json() {
|
|||||||
write_json(capability_flags());
|
write_json(capability_flags());
|
||||||
}
|
}
|
||||||
|
|
||||||
function process_memory_rss_mb(process_name) {
|
function get_process_pids(process_name) {
|
||||||
|
let pids = [];
|
||||||
|
let d = fs.opendir("/proc");
|
||||||
|
if (d) {
|
||||||
|
let entry;
|
||||||
|
while ((entry = d.read()) != null) {
|
||||||
|
if (match(entry, /^[0-9]+$/) != null) {
|
||||||
|
let comm = fs.readfile("/proc/" + entry + "/comm");
|
||||||
|
if (comm && trim(comm) == process_name)
|
||||||
|
push(pids, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.close();
|
||||||
|
return pids;
|
||||||
|
}
|
||||||
let output = common.command_output_from_args([ "pidof", process_name ]);
|
let output = common.command_output_from_args([ "pidof", process_name ]);
|
||||||
let pids = split(trim(as_string(output)), /[ \t\r\n]+/);
|
for (let p in split(trim(as_string(output)), /[ \t\r\n]+/)) {
|
||||||
|
let pid = trim(as_string(p));
|
||||||
|
if (pid && match(pid, /^[0-9]+$/) != null)
|
||||||
|
push(pids, pid);
|
||||||
|
}
|
||||||
|
return pids;
|
||||||
|
}
|
||||||
|
|
||||||
|
function process_memory_rss_mb(process_name) {
|
||||||
|
let pids = get_process_pids(process_name);
|
||||||
let total_kb = 0;
|
let total_kb = 0;
|
||||||
for (let p in pids) {
|
for (let p in pids) {
|
||||||
let pid = trim(as_string(p));
|
let pid = trim(as_string(p));
|
||||||
|
|||||||
Reference in New Issue
Block a user