fix: resolve -1ms latency display, diagnostic outbounds check timeout, country.is fallback, and docker AGH false positive

- Fix -1ms badge and green coloring in dashboard sections when node latency check fails or returns -1
- Handle Clash API group delay responses properly in dashboard single and batch latency tests
- Target active outbound node in diagnostics outbounds check instead of group tag to prevent router timeout
- Add nslookup fallback in country.uc resolve_service_host and retain system DNS resolution
- Ensure flag emoji country deduction acts as fallback in URLTest groups when exclude_countries is specified
- Ignore processes in container network namespaces when diagnosing system port 53 / DNS conflicts for AdGuardHome
- Bump fe-app-tachyon version to 1.3.25 and rebuild LuCI bundle
This commit is contained in:
Dushnilin
2026-09-11 23:53:41 +03:00
parent f987e88d23
commit 0bec2f6f86
11 changed files with 407 additions and 58 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "fe-app-tachyon", "name": "fe-app-tachyon",
"version": "1.3.24", "version": "1.3.25",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -1282,7 +1282,11 @@ function buildProxyGroupOutbounds(
)?.latency || )?.latency ||
0; 0;
const latency = item?.value.history?.[0]?.delay || activeMemberLatency || 0; const itemDelay = Number(item?.value.history?.[0]?.delay);
const validItemDelay =
Number.isFinite(itemDelay) && itemDelay > 0 ? itemDelay : 0;
const latency =
validItemDelay || (activeMemberLatency > 0 ? activeMemberLatency : 0);
const isGroupType = Boolean( const isGroupType = Boolean(
priorityConfig || urlTestConfig || isRuntimeUrlTest, priorityConfig || urlTestConfig || isRuntimeUrlTest,
@@ -763,7 +763,18 @@ async function handleTestLatency(
timeout, timeout,
); );
if (response.success && response.data) { if (response.success && response.data) {
customProxyLatencies.set(tag, response.data.delay || -1); const delay = (response.data as any)?.delay;
if (typeof delay === 'number' && delay > 0) {
customProxyLatencies.set(tag, delay);
} else {
const groupDelays = Object.values(response.data || {}).filter(
(v): v is number => typeof v === 'number' && v > 0,
);
customProxyLatencies.set(
tag,
groupDelays.length > 0 ? Math.min(...groupDelays) : -1,
);
}
} else { } else {
customProxyLatencies.set(tag, -1); customProxyLatencies.set(tag, -1);
} }
@@ -825,7 +836,18 @@ async function handleTestSingleOutbound(
'2000', '2000',
); );
if (response.success && response.data) { if (response.success && response.data) {
customProxyLatencies.set(outboundCode, response.data.delay || -1); const delay = (response.data as any)?.delay;
if (typeof delay === 'number' && delay > 0) {
customProxyLatencies.set(outboundCode, delay);
} else {
const groupDelays = Object.values(response.data || {}).filter(
(v): v is number => typeof v === 'number' && v > 0,
);
customProxyLatencies.set(
outboundCode,
groupDelays.length > 0 ? Math.min(...groupDelays) : -1,
);
}
} else { } else {
customProxyLatencies.set(outboundCode, -1); customProxyLatencies.set(outboundCode, -1);
} }
@@ -1753,14 +1775,44 @@ async function renderSectionsWidget() {
const sectionsWithCustomLatencies = sectionsWidget.data.map((section) => ({ const sectionsWithCustomLatencies = sectionsWidget.data.map((section) => ({
...section, ...section,
outbounds: section.outbounds.map((outbound) => ({ outbounds: section.outbounds.map((outbound) => {
...outbound, const customLatency = customProxyLatencies.get(outbound.code);
latency: const latency =
SERVICE_TYPES.has(outbound.type) || SERVICE_TYPES.has(outbound.type) || customLatency == null
!customProxyLatencies.has(outbound.code)
? outbound.latency ? outbound.latency
: customProxyLatencies.get(outbound.code)!, : customLatency;
})),
const urlTestInfo = outbound.urlTestInfo
? {
...outbound.urlTestInfo,
outbounds: outbound.urlTestInfo.outbounds.map((member) => ({
...member,
latency: customProxyLatencies.has(member.code)
? customProxyLatencies.get(member.code)!
: member.latency,
})),
}
: undefined;
const priorityInfo = outbound.priorityInfo
? {
...outbound.priorityInfo,
outbounds: outbound.priorityInfo.outbounds.map((member) => ({
...member,
latency: customProxyLatencies.has(member.code)
? customProxyLatencies.get(member.code)!
: member.latency,
})),
}
: undefined;
return {
...outbound,
latency,
urlTestInfo,
priorityInfo,
};
}),
})); }));
if (sectionsWidget.loading || sectionsWidget.failed) { if (sectionsWidget.loading || sectionsWidget.failed) {
@@ -467,6 +467,10 @@ function renderDefaultState({
return 'tachyon_dashboard-page__outbound-grid__item__latency--empty'; return 'tachyon_dashboard-page__outbound-grid__item__latency--empty';
} }
if (outbound.latency === -1 || outbound.latency < 0) {
return 'tachyon_dashboard-page__outbound-grid__item__latency--red';
}
if (outbound.latency < 800) { if (outbound.latency < 800) {
return 'tachyon_dashboard-page__outbound-grid__item__latency--green'; return 'tachyon_dashboard-page__outbound-grid__item__latency--green';
} }
@@ -763,9 +767,11 @@ function renderDefaultState({
? [renderLoaderCircleIcon24(), E('span', {}, _('Checking...'))] ? [renderLoaderCircleIcon24(), E('span', {}, _('Checking...'))]
: isConnectionNode : isConnectionNode
? connectionStatusText ? connectionStatusText
: outbound.latency : outbound.latency && outbound.latency > 0
? `${outbound.latency}ms` ? `${outbound.latency}ms`
: 'N/A', : outbound.latency === -1 || outbound.latency < 0
? _('Not responding')
: 'N/A',
), ),
], ],
), ),
@@ -886,6 +892,11 @@ function renderDefaultState({
if (!selectedOutbound!.latency) if (!selectedOutbound!.latency)
return 'var(--primary-color-low, lightgray)'; return 'var(--primary-color-low, lightgray)';
if (
selectedOutbound!.latency === -1 ||
selectedOutbound!.latency < 0
)
return 'var(--error-color-medium, red)';
if (selectedOutbound!.latency < 800) if (selectedOutbound!.latency < 800)
return 'var(--success-color-medium, green)'; return 'var(--success-color-medium, green)';
if (selectedOutbound!.latency < 1500) if (selectedOutbound!.latency < 1500)
@@ -899,15 +910,20 @@ function renderDefaultState({
? _('Checking...') ? _('Checking...')
: selectedOutbound.latency && selectedOutbound.latency > 0 : selectedOutbound.latency && selectedOutbound.latency > 0
? `${selectedOutbound.latency}ms` ? `${selectedOutbound.latency}ms`
: selectedOutbound.latency === -1 : selectedOutbound.latency === -1 ||
selectedOutbound.latency < 0
? _('Not responding') ? _('Not responding')
: selectedOutbound.runtimeAvailable : selectedOutbound.runtimeAvailable
? _('Connected') ? _('Connected')
: _('Not connected'); : _('Not connected');
} else { } else {
latencyText = selectedOutbound.latency latencyText =
? `${selectedOutbound.latency}ms` selectedOutbound.latency && selectedOutbound.latency > 0
: ''; ? `${selectedOutbound.latency}ms`
: selectedOutbound.latency === -1 ||
selectedOutbound.latency < 0
? _('Not responding')
: '';
} }
return E( return E(
@@ -435,4 +435,57 @@ describe('renderSections', () => {
expect(testingBadge).not.toBeNull(); expect(testingBadge).not.toBeNull();
expect(testingBadge?.textContent).toContain('Checking...'); expect(testingBadge?.textContent).toContain('Checking...');
}); });
it('renders -1 latency as red Not responding instead of green -1ms', () => {
const section: any = {
code: 'proxy',
sectionName: 'proxy',
displayName: 'Proxy Section',
action: 'proxy',
withTagSelect: true,
outbounds: [
{
code: 'vless-failed',
displayName: 'Failed Node',
latency: -1,
type: 'VLESS',
selected: false,
},
{
code: 'vless-ok',
displayName: 'OK Node',
latency: 150,
type: 'VLESS',
selected: true,
},
],
};
const el = renderSections({
loading: false,
failed: false,
section,
onTestLatency: vi.fn(),
onChooseOutbound: vi.fn(),
onCopyOutbound: vi.fn(),
onShowUrlTestInfo: vi.fn(),
onShowPriorityInfo: vi.fn(),
onUpdateSubscription: vi.fn(),
latencyFetching: false,
subscriptionUpdating: false,
});
const redBadge = el.querySelector(
'.tachyon_dashboard-page__outbound-grid__item__latency--red',
);
expect(redBadge).not.toBeNull();
expect(redBadge?.textContent).toBe('Not responding');
expect(redBadge?.textContent).not.toContain('-1');
const greenBadge = el.querySelector(
'.tachyon_dashboard-page__outbound-grid__item__latency--green',
);
expect(greenBadge).not.toBeNull();
expect(greenBadge?.textContent).toBe('150ms');
});
}); });
@@ -70,10 +70,15 @@ export async function runSectionsCheck() {
const isSubscription = section.proxyConfigType === 'subscription'; const isSubscription = section.proxyConfigType === 'subscription';
if (selectedOutbound?.code) { const activeNodeCode =
selectedOutbound?.urlTestInfo?.selectedCode ||
selectedOutbound?.priorityInfo?.selectedCode ||
selectedOutbound?.code;
if (activeNodeCode) {
const latencyProxy = const latencyProxy =
await TachyonShellMethods.getClashApiProxyLatency( await TachyonShellMethods.getClashApiProxyLatency(
selectedOutbound.code, activeNodeCode,
section.latencyTestTimeout, section.latencyTestTimeout,
); );
const proxySuccess = const proxySuccess =
@@ -81,10 +86,10 @@ export async function runSectionsCheck() {
if (proxySuccess) { if (proxySuccess) {
const delay = latencyProxy.data?.delay; const delay = latencyProxy.data?.delay;
if (typeof delay === 'number') { if (typeof delay === 'number' && delay > 0) {
return { return {
state: 'success', state: 'success',
latency: `[${selectedOutbound.displayName ?? ''}] ${delay}ms`, latency: `[${selectedOutbound?.displayName ?? ''}] ${delay}ms`,
}; };
} }
@@ -95,14 +100,21 @@ export async function runSectionsCheck() {
const minDelay = Math.min(...groupDelays); const minDelay = Math.min(...groupDelays);
return { return {
state: 'success', state: 'success',
latency: `[${selectedOutbound.displayName ?? ''}] ${minDelay}ms`, latency: `[${selectedOutbound?.displayName ?? ''}] ${minDelay}ms`,
}; };
} }
} }
if (selectedOutbound?.latency && selectedOutbound.latency > 0) {
return {
state: 'success',
latency: `[${selectedOutbound.displayName ?? ''}] ${selectedOutbound.latency}ms`,
};
}
return { return {
state: 'error', state: 'error',
latency: `[${selectedOutbound.displayName ?? ''}] ${_('Not responding')}`, latency: `[${selectedOutbound?.displayName ?? ''}] ${_('Not responding')}`,
}; };
} }
@@ -212,6 +224,13 @@ export async function runSectionsCheck() {
}; };
} }
if (selectedOutbound?.latency && selectedOutbound.latency > 0) {
return {
state: 'success',
latency: `${selectedOutbound.latency} ms`,
};
}
return { return {
state: 'error', state: 'error',
latency: _('Not responding'), latency: _('Not responding'),
@@ -284,4 +284,97 @@ describe('runSectionsCheck', () => {
}), }),
); );
}); });
it('targets active child node code when outbound has urlTestInfo.selectedCode', async () => {
mocks.getDashboardSections.mockResolvedValue({
success: true,
data: [
{
withTagSelect: true,
code: 'ai-out',
sectionName: 'ai',
displayName: 'AI',
outbounds: [
{
code: 'ai-urltest',
displayName: 'Fastest',
latency: 158,
type: 'URLTest',
selected: true,
urlTestInfo: {
selectedCode: 'ai-node-fastest',
selectedName: 'Fast Node',
outbounds: [],
},
},
],
},
],
});
mocks.getClashApiProxyLatency.mockResolvedValue({
success: true,
data: { delay: 158 },
});
await expect(runSectionsCheck()).resolves.toBeUndefined();
expect(mocks.getClashApiProxyLatency).toHaveBeenCalledWith(
'ai-node-fastest',
undefined,
);
expect(mocks.updateCheckStore).toHaveBeenLastCalledWith(
expect.objectContaining({
state: 'success',
items: [
{
state: 'success',
key: 'AI',
value: '[Fastest] 158ms',
},
],
}),
);
});
it('falls back to verified outbound latency when live probe fails', async () => {
mocks.getDashboardSections.mockResolvedValue({
success: true,
data: [
{
withTagSelect: true,
code: 'ai-out',
sectionName: 'ai',
displayName: 'AI',
outbounds: [
{
code: 'ai-urltest',
displayName: 'Fastest',
latency: 158,
type: 'URLTest',
selected: true,
},
],
},
],
});
mocks.getClashApiProxyLatency.mockResolvedValue({
success: false,
error: 'timeout',
});
await expect(runSectionsCheck()).resolves.toBeUndefined();
expect(mocks.updateCheckStore).toHaveBeenLastCalledWith(
expect.objectContaining({
state: 'success',
items: [
{
state: 'success',
key: 'AI',
value: '[Fastest] 158ms',
},
],
}),
);
});
}); });
@@ -2597,6 +2597,9 @@ function renderDefaultState({
if (!outbound.latency) { if (!outbound.latency) {
return "tachyon_dashboard-page__outbound-grid__item__latency--empty"; return "tachyon_dashboard-page__outbound-grid__item__latency--empty";
} }
if (outbound.latency === -1 || outbound.latency < 0) {
return "tachyon_dashboard-page__outbound-grid__item__latency--red";
}
if (outbound.latency < 800) { if (outbound.latency < 800) {
return "tachyon_dashboard-page__outbound-grid__item__latency--green"; return "tachyon_dashboard-page__outbound-grid__item__latency--green";
} }
@@ -2815,7 +2818,7 @@ function renderDefaultState({
); );
} }
}, },
isTestingSingle ? [renderLoaderCircleIcon24(), E("span", {}, _("Checking..."))] : isConnectionNode ? connectionStatusText : outbound.latency ? `${outbound.latency}ms` : "N/A" isTestingSingle ? [renderLoaderCircleIcon24(), E("span", {}, _("Checking..."))] : isConnectionNode ? connectionStatusText : outbound.latency && outbound.latency > 0 ? `${outbound.latency}ms` : outbound.latency === -1 || outbound.latency < 0 ? _("Not responding") : "N/A"
) )
] ]
) )
@@ -2916,6 +2919,8 @@ function renderDefaultState({
} }
if (!selectedOutbound.latency) if (!selectedOutbound.latency)
return "var(--primary-color-low, lightgray)"; return "var(--primary-color-low, lightgray)";
if (selectedOutbound.latency === -1 || selectedOutbound.latency < 0)
return "var(--error-color-medium, red)";
if (selectedOutbound.latency < 800) if (selectedOutbound.latency < 800)
return "var(--success-color-medium, green)"; return "var(--success-color-medium, green)";
if (selectedOutbound.latency < 1500) if (selectedOutbound.latency < 1500)
@@ -2924,9 +2929,9 @@ function renderDefaultState({
} }
let latencyText = ""; let latencyText = "";
if (isConnectionNode2) { if (isConnectionNode2) {
latencyText = latencyFetching ? _("Checking...") : selectedOutbound.latency && selectedOutbound.latency > 0 ? `${selectedOutbound.latency}ms` : selectedOutbound.latency === -1 ? _("Not responding") : selectedOutbound.runtimeAvailable ? _("Connected") : _("Not connected"); latencyText = latencyFetching ? _("Checking...") : selectedOutbound.latency && selectedOutbound.latency > 0 ? `${selectedOutbound.latency}ms` : selectedOutbound.latency === -1 || selectedOutbound.latency < 0 ? _("Not responding") : selectedOutbound.runtimeAvailable ? _("Connected") : _("Not connected");
} else { } else {
latencyText = selectedOutbound.latency ? `${selectedOutbound.latency}ms` : ""; latencyText = selectedOutbound.latency && selectedOutbound.latency > 0 ? `${selectedOutbound.latency}ms` : selectedOutbound.latency === -1 || selectedOutbound.latency < 0 ? _("Not responding") : "";
} }
return E( return E(
"span", "span",
@@ -5670,7 +5675,9 @@ function buildProxyGroupOutbounds(section, proxies, outboundMetadata, urltestGro
)?.latency || priorityInfo?.outbounds.find( )?.latency || priorityInfo?.outbounds.find(
(m) => m.selected || m.code === priorityInfo.selectedCode (m) => m.selected || m.code === priorityInfo.selectedCode
)?.latency || 0; )?.latency || 0;
const latency = item?.value.history?.[0]?.delay || activeMemberLatency || 0; const itemDelay = Number(item?.value.history?.[0]?.delay);
const validItemDelay = Number.isFinite(itemDelay) && itemDelay > 0 ? itemDelay : 0;
const latency = validItemDelay || (activeMemberLatency > 0 ? activeMemberLatency : 0);
const isGroupType = Boolean( const isGroupType = Boolean(
priorityConfig || urlTestConfig || isRuntimeUrlTest priorityConfig || urlTestConfig || isRuntimeUrlTest
); );
@@ -8302,7 +8309,18 @@ async function handleTestLatency(latencyType, sectionName, tag, timeout) {
timeout timeout
); );
if (response.success && response.data) { if (response.success && response.data) {
customProxyLatencies.set(tag, response.data.delay || -1); const delay = response.data?.delay;
if (typeof delay === "number" && delay > 0) {
customProxyLatencies.set(tag, delay);
} else {
const groupDelays = Object.values(response.data || {}).filter(
(v) => typeof v === "number" && v > 0
);
customProxyLatencies.set(
tag,
groupDelays.length > 0 ? Math.min(...groupDelays) : -1
);
}
} else { } else {
customProxyLatencies.set(tag, -1); customProxyLatencies.set(tag, -1);
} }
@@ -8355,7 +8373,18 @@ async function handleTestSingleOutbound(_sectionName, outboundCode) {
"2000" "2000"
); );
if (response.success && response.data) { if (response.success && response.data) {
customProxyLatencies.set(outboundCode, response.data.delay || -1); const delay = response.data?.delay;
if (typeof delay === "number" && delay > 0) {
customProxyLatencies.set(outboundCode, delay);
} else {
const groupDelays = Object.values(response.data || {}).filter(
(v) => typeof v === "number" && v > 0
);
customProxyLatencies.set(
outboundCode,
groupDelays.length > 0 ? Math.min(...groupDelays) : -1
);
}
} else { } else {
customProxyLatencies.set(outboundCode, -1); customProxyLatencies.set(outboundCode, -1);
} }
@@ -9079,10 +9108,30 @@ async function renderSectionsWidget() {
const SERVICE_TYPES = /* @__PURE__ */ new Set(["SING_BOX", "ZAPRET", "ZAPRET2", "BYEDPI"]); const SERVICE_TYPES = /* @__PURE__ */ new Set(["SING_BOX", "ZAPRET", "ZAPRET2", "BYEDPI"]);
const sectionsWithCustomLatencies = sectionsWidget.data.map((section) => ({ const sectionsWithCustomLatencies = sectionsWidget.data.map((section) => ({
...section, ...section,
outbounds: section.outbounds.map((outbound) => ({ outbounds: section.outbounds.map((outbound) => {
...outbound, const customLatency = customProxyLatencies.get(outbound.code);
latency: SERVICE_TYPES.has(outbound.type) || !customProxyLatencies.has(outbound.code) ? outbound.latency : customProxyLatencies.get(outbound.code) const latency = SERVICE_TYPES.has(outbound.type) || customLatency == null ? outbound.latency : customLatency;
})) const urlTestInfo = outbound.urlTestInfo ? {
...outbound.urlTestInfo,
outbounds: outbound.urlTestInfo.outbounds.map((member) => ({
...member,
latency: customProxyLatencies.has(member.code) ? customProxyLatencies.get(member.code) : member.latency
}))
} : void 0;
const priorityInfo = outbound.priorityInfo ? {
...outbound.priorityInfo,
outbounds: outbound.priorityInfo.outbounds.map((member) => ({
...member,
latency: customProxyLatencies.has(member.code) ? customProxyLatencies.get(member.code) : member.latency
}))
} : void 0;
return {
...outbound,
latency,
urlTestInfo,
priorityInfo
};
})
})); }));
if (sectionsWidget.loading || sectionsWidget.failed) { if (sectionsWidget.loading || sectionsWidget.failed) {
const renderedWidget = renderSections({ const renderedWidget = renderSections({
@@ -15646,18 +15695,19 @@ async function runSectionsCheck() {
(item) => item.type?.toLowerCase() === "urltest" (item) => item.type?.toLowerCase() === "urltest"
) ?? section.outbounds[0]; ) ?? section.outbounds[0];
const isSubscription = section.proxyConfigType === "subscription"; const isSubscription = section.proxyConfigType === "subscription";
if (selectedOutbound2?.code) { const activeNodeCode = selectedOutbound2?.urlTestInfo?.selectedCode || selectedOutbound2?.priorityInfo?.selectedCode || selectedOutbound2?.code;
if (activeNodeCode) {
const latencyProxy2 = await TachyonShellMethods.getClashApiProxyLatency( const latencyProxy2 = await TachyonShellMethods.getClashApiProxyLatency(
selectedOutbound2.code, activeNodeCode,
section.latencyTestTimeout section.latencyTestTimeout
); );
const proxySuccess = latencyProxy2.success && !latencyProxy2.data?.message; const proxySuccess = latencyProxy2.success && !latencyProxy2.data?.message;
if (proxySuccess) { if (proxySuccess) {
const delay = latencyProxy2.data?.delay; const delay = latencyProxy2.data?.delay;
if (typeof delay === "number") { if (typeof delay === "number" && delay > 0) {
return { return {
state: "success", state: "success",
latency: `[${selectedOutbound2.displayName ?? ""}] ${delay}ms` latency: `[${selectedOutbound2?.displayName ?? ""}] ${delay}ms`
}; };
} }
const groupDelays = Object.values(latencyProxy2.data || {}).filter( const groupDelays = Object.values(latencyProxy2.data || {}).filter(
@@ -15667,13 +15717,19 @@ async function runSectionsCheck() {
const minDelay = Math.min(...groupDelays); const minDelay = Math.min(...groupDelays);
return { return {
state: "success", state: "success",
latency: `[${selectedOutbound2.displayName ?? ""}] ${minDelay}ms` latency: `[${selectedOutbound2?.displayName ?? ""}] ${minDelay}ms`
}; };
} }
} }
if (selectedOutbound2?.latency && selectedOutbound2.latency > 0) {
return {
state: "success",
latency: `[${selectedOutbound2.displayName ?? ""}] ${selectedOutbound2.latency}ms`
};
}
return { return {
state: "error", state: "error",
latency: `[${selectedOutbound2.displayName ?? ""}] ${_("Not responding")}` latency: `[${selectedOutbound2?.displayName ?? ""}] ${_("Not responding")}`
}; };
} }
const latencyGroup = await TachyonShellMethods.getClashApiGroupLatency( const latencyGroup = await TachyonShellMethods.getClashApiGroupLatency(
@@ -15762,6 +15818,12 @@ async function runSectionsCheck() {
latency: `[${selectedOutbound.displayName || section.code}] ${_("Connectivity probe failed")}` latency: `[${selectedOutbound.displayName || section.code}] ${_("Connectivity probe failed")}`
}; };
} }
if (selectedOutbound?.latency && selectedOutbound.latency > 0) {
return {
state: "success",
latency: `${selectedOutbound.latency} ms`
};
}
return { return {
state: "error", state: "error",
latency: _("Not responding") latency: _("Not responding")
+19 -1
View File
@@ -2142,11 +2142,25 @@ function find_process_pid(name) {
return length(pids) > 0 && pids[0] != "" ? pids[0] : ""; return length(pids) > 0 && pids[0] != "" ? pids[0] : "";
} }
function is_container_process(pid) {
pid = as_string(pid);
if (pid == "")
return false;
let host_net = fs.readlink("/proc/1/ns/net");
let proc_net = fs.readlink("/proc/" + pid + "/ns/net");
if (host_net != null && proc_net != null && host_net != proc_net)
return true;
let cgroup = fs.readfile("/proc/" + pid + "/cgroup") || "";
if (index(cgroup, "docker") >= 0 || index(cgroup, "containerd") >= 0)
return true;
return false;
}
function is_adguardhome_primary_dns(cfg) { function is_adguardhome_primary_dns(cfg) {
let agh_pid = find_process_pid("AdGuardHome"); let agh_pid = find_process_pid("AdGuardHome");
if (agh_pid == "") if (agh_pid == "")
agh_pid = find_process_pid("adguardhome"); agh_pid = find_process_pid("adguardhome");
if (agh_pid == "") if (agh_pid == "" || is_container_process(agh_pid))
return false; return false;
let dnsmasq_port = uci_core.get("dhcp.@dnsmasq[0].port"); let dnsmasq_port = uci_core.get("dhcp.@dnsmasq[0].port");
@@ -4337,6 +4351,8 @@ function diagnose_system_conflicts(cfg, lang, causes, fn_add_fix) {
if (pid == "" && svc == "adguardhome") if (pid == "" && svc == "adguardhome")
pid = find_process_pid("AdGuardHome"); pid = find_process_pid("AdGuardHome");
if (pid != "") { if (pid != "") {
if (is_container_process(pid))
continue;
if ((svc == "adguardhome" || svc == "AdGuardHome") && is_adguardhome_primary_dns(cfg)) if ((svc == "adguardhome" || svc == "AdGuardHome") && is_adguardhome_primary_dns(cfg))
continue; continue;
@@ -4355,6 +4371,8 @@ function diagnose_system_conflicts(cfg, lang, causes, fn_add_fix) {
for (let pkg in competing_pkgs) { for (let pkg in competing_pkgs) {
let pid = find_process_pid(pkg); let pid = find_process_pid(pkg);
if (pid != "") { if (pid != "") {
if (is_container_process(pid))
continue;
push(causes, { push(causes, {
probability: 88, probability: 88,
cause: lang == "en" ? sprintf("Conflicting proxy framework running: %s (PID %s) causes routing/nftables rule clashes", pkg, pid) cause: lang == "en" ? sprintf("Conflicting proxy framework running: %s (PID %s) causes routing/nftables rule clashes", pkg, pid)
+17 -9
View File
@@ -135,12 +135,25 @@ function normalized_resolver(value) {
function resolve_service_host(host, resolver) { function resolve_service_host(host, resolver) {
resolver = normalized_resolver(resolver); resolver = normalized_resolver(resolver);
if (host == "" || resolver == "") if (host == "")
return ""; return "";
return first_ipv4_line(command_output_from_args([ let ip = "";
"dig", "+short", "@" + resolver, host, "A", "+time=2", "+tries=1" if (resolver != "") {
])); ip = first_ipv4_line(command_output_from_args([
"dig", "+short", "@" + resolver, host, "A", "+time=2", "+tries=1"
]));
if (ip == "")
ip = first_nslookup_address(command_output_from_args([ "nslookup", host, resolver ]));
}
if (ip == "") {
ip = first_ipv4_line(command_output_from_args([
"dig", "+short", host, "A", "+time=2", "+tries=1"
]));
if (ip == "")
ip = first_nslookup_address(command_output_from_args([ "nslookup", host ]));
}
return public_ipv4(ip) ? ip : "";
} }
function resolve_server(value) { function resolve_server(value) {
@@ -174,11 +187,6 @@ function lookup_ip_batch(ips, resolver) {
let endpoint = country_is_endpoint(); let endpoint = country_is_endpoint();
if (endpoint.host != "" && !valid_ipv4(endpoint.host) && !valid_ipv6(endpoint.host)) { if (endpoint.host != "" && !valid_ipv4(endpoint.host) && !valid_ipv6(endpoint.host)) {
let service_ip = resolve_service_host(endpoint.host, resolver); let service_ip = resolve_service_host(endpoint.host, resolver);
if (normalized_resolver(resolver) != "" && !public_ipv4(service_ip)) {
remove_file(body_path);
warn("Server country service host resolution failed\n");
return { countries: {}, stop: true };
}
if (public_ipv4(service_ip)) { if (public_ipv4(service_ip)) {
push(args, "--resolve"); push(args, "--resolve");
push(args, endpoint.host + ":" + endpoint.port + ":" + service_ip); push(args, endpoint.host + ":" + endpoint.port + ":" + service_ip);
@@ -191,11 +191,19 @@ function section_has_direct_priority_level(section) {
} }
function urltest_country_metadata(section, urltest_id, state) { function urltest_country_metadata(section, urltest_id, state) {
let metadata = object_or_empty(object_or_empty(state.outboundMetadata).countries); let names = object_or_empty(object_or_empty(state.outboundMetadata).names);
let from_flags = runtime_urltest.countries_from_flag_names(names);
let detect_method = connections.urltest_detect_server_country(section, urltest_id); let detect_method = connections.urltest_detect_server_country(section, urltest_id);
if (detect_method == "flag_emoji") if (detect_method == "flag_emoji")
return runtime_urltest.countries_from_flag_names(object_or_empty(object_or_empty(state.outboundMetadata).names)); return from_flags;
return metadata; let metadata = object_or_empty(object_or_empty(state.outboundMetadata).countries);
let result = {};
for (let tag, c in from_flags)
result[tag] = c;
for (let tag, c in metadata)
if (c != "")
result[tag] = c;
return result;
} }
function array_contains(values, needle) { function array_contains(values, needle) {
@@ -409,11 +417,19 @@ function urltest_filtered_outbounds(section, urltest_id, urltest_candidate_tags,
} }
function priority_level_country_metadata(group_id, level_id, state) { function priority_level_country_metadata(group_id, level_id, state) {
let metadata = object_or_empty(object_or_empty(state.outboundMetadata).countries); let names = object_or_empty(object_or_empty(state.outboundMetadata).names);
let from_flags = runtime_urltest.countries_from_flag_names(names);
let detect_method = connections.priority_level_detect_server_country(group_id, level_id); let detect_method = connections.priority_level_detect_server_country(group_id, level_id);
if (detect_method == "flag_emoji") if (detect_method == "flag_emoji")
return runtime_urltest.countries_from_flag_names(object_or_empty(object_or_empty(state.outboundMetadata).names)); return from_flags;
return metadata; let metadata = object_or_empty(object_or_empty(state.outboundMetadata).countries);
let result = {};
for (let tag, c in from_flags)
result[tag] = c;
for (let tag, c in metadata)
if (c != "")
result[tag] = c;
return result;
} }
function priority_level_filtered_outbounds(group_id, level_id, urltest_candidate_tags, state) { function priority_level_filtered_outbounds(group_id, level_id, urltest_candidate_tags, state) {
@@ -444,10 +460,18 @@ function priority_level_filtered_outbounds(group_id, level_id, urltest_candidate
} }
function dashboard_country_metadata(section, state) { function dashboard_country_metadata(section, state) {
let metadata = object_or_empty(object_or_empty(state.outboundMetadata).countries); let names = object_or_empty(object_or_empty(state.outboundMetadata).names);
let from_flags = runtime_urltest.countries_from_flag_names(names);
if (connections.dashboard_detect_server_country(section) == "flag_emoji") if (connections.dashboard_detect_server_country(section) == "flag_emoji")
return runtime_urltest.countries_from_flag_names(object_or_empty(object_or_empty(state.outboundMetadata).names)); return from_flags;
return metadata; let metadata = object_or_empty(object_or_empty(state.outboundMetadata).countries);
let result = {};
for (let tag, c in from_flags)
result[tag] = c;
for (let tag, c in metadata)
if (c != "")
result[tag] = c;
return result;
} }
function selected_group_outbounds(group_names, group_outbounds) { function selected_group_outbounds(group_names, group_outbounds) {