feat: update diagnostics checks

This commit is contained in:
divocat
2025-10-13 22:40:49 +03:00
parent aea6fd9453
commit 74edbcf07f
26 changed files with 400 additions and 81 deletions

View File

@@ -1,10 +1,26 @@
import { IBaseApiResponse } from '../types';
import { IBaseApiResponse } from './types';
import { withTimeout } from '../helpers';
export async function createBaseApiRequest<T>(
fetchFn: () => Promise<Response>,
options?: {
timeoutMs?: number;
operationName?: string;
timeoutMessage?: string;
},
): Promise<IBaseApiResponse<T>> {
const wrappedFn = () =>
options?.timeoutMs && options?.operationName
? withTimeout(
fetchFn(),
options.timeoutMs,
options.operationName,
options.timeoutMessage,
)
: fetchFn();
try {
const response = await fetchFn();
const response = await wrappedFn();
if (!response.ok) {
return {

View File

@@ -0,0 +1,2 @@
export * from './types';
export * from './createBaseApiRequest';

View File

@@ -0,0 +1,9 @@
export type IBaseApiResponse<T> =
| {
success: true;
data: T;
}
| {
success: false;
message: string;
};

View File

@@ -1,6 +1,6 @@
import { ClashAPI, IBaseApiResponse } from '../types';
import { createBaseApiRequest } from './createBaseApiRequest';
import { ClashAPI } from '../types';
import { getClashApiUrl } from '../../helpers';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
export async function getClashConfig(): Promise<
IBaseApiResponse<ClashAPI.Config>

View File

@@ -1,6 +1,6 @@
import { ClashAPI, IBaseApiResponse } from '../types';
import { createBaseApiRequest } from './createBaseApiRequest';
import { ClashAPI } from '../types';
import { getClashApiUrl } from '../../helpers';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
export async function getClashGroupDelay(
group: string,

View File

@@ -1,6 +1,6 @@
import { ClashAPI, IBaseApiResponse } from '../types';
import { createBaseApiRequest } from './createBaseApiRequest';
import { ClashAPI } from '../types';
import { getClashApiUrl } from '../../helpers';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
export async function getClashProxies(): Promise<
IBaseApiResponse<ClashAPI.Proxies>

View File

@@ -1,6 +1,6 @@
import { ClashAPI, IBaseApiResponse } from '../types';
import { createBaseApiRequest } from './createBaseApiRequest';
import { ClashAPI } from '../types';
import { getClashApiUrl } from '../../helpers';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
export async function getClashVersion(): Promise<
IBaseApiResponse<ClashAPI.Version>

View File

@@ -1,4 +1,3 @@
export * from './createBaseApiRequest';
export * from './getConfig';
export * from './getGroupDelay';
export * from './getProxies';

View File

@@ -1,6 +1,5 @@
import { IBaseApiResponse } from '../types';
import { createBaseApiRequest } from './createBaseApiRequest';
import { getClashApiUrl } from '../../helpers';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
export async function triggerLatencyGroupTest(
tag: string,

View File

@@ -1,6 +1,5 @@
import { IBaseApiResponse } from '../types';
import { createBaseApiRequest } from './createBaseApiRequest';
import { getClashApiUrl } from '../../helpers';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
export async function triggerProxySelector(
selector: string,

View File

@@ -1,13 +1,3 @@
export type IBaseApiResponse<T> =
| {
success: true;
data: T;
}
| {
success: false;
message: string;
};
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace ClashAPI {
export interface Version {

View File

@@ -0,0 +1 @@
export * from './methods';

View File

@@ -0,0 +1,23 @@
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
import { FAKEIP_CHECK_DOMAIN } from '../../constants';
interface IGetFakeIpCheckResponse {
fakeip: boolean;
IP: string;
}
export async function getFakeIpCheck(): Promise<
IBaseApiResponse<IGetFakeIpCheckResponse>
> {
return createBaseApiRequest<IGetFakeIpCheckResponse>(
() =>
fetch(`https://${FAKEIP_CHECK_DOMAIN}/check`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
}),
{
operationName: 'getFakeIpCheck',
timeoutMs: 5000,
},
);
}

View File

@@ -0,0 +1,23 @@
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
import { IP_CHECK_DOMAIN } from '../../constants';
interface IGetIpCheckResponse {
fakeip: boolean;
IP: string;
}
export async function getIpCheck(): Promise<
IBaseApiResponse<IGetIpCheckResponse>
> {
return createBaseApiRequest<IGetIpCheckResponse>(
() =>
fetch(`https://${IP_CHECK_DOMAIN}/check`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
}),
{
operationName: 'getIpCheck',
timeoutMs: 5000,
},
);
}

View File

@@ -0,0 +1,2 @@
export * from './getIpCheck';
export * from './getFakeIpCheck';

View File

@@ -3,3 +3,6 @@ export * from './renderCircleAlertIcon24';
export * from './renderCircleCheckIcon24';
export * from './renderCircleSlashIcon24';
export * from './renderCircleXIcon24';
export * from './renderCheckIcon24';
export * from './renderXIcon24';
export * from './renderTriangleAlertIcon24';

View File

@@ -0,0 +1,23 @@
import { svgEl } from '../helpers';
export function renderCheckIcon24() {
const NS = 'http://www.w3.org/2000/svg';
return svgEl(
'svg',
{
xmlns: NS,
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
class: 'lucide lucide-check-icon lucide-check',
},
[
svgEl('path', {
d: 'M20 6 9 17l-5-5',
}),
],
);
}

View File

@@ -0,0 +1,25 @@
import { svgEl } from '../helpers';
export function renderTriangleAlertIcon24() {
const NS = 'http://www.w3.org/2000/svg';
return svgEl(
'svg',
{
xmlns: NS,
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
class: 'lucide lucide-triangle-alert-icon lucide-triangle-alert',
},
[
svgEl('path', {
d: 'm21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3',
}),
svgEl('path', { d: 'M12 9v4' }),
svgEl('path', { d: 'M12 17h.01' }),
],
);
}

View File

@@ -0,0 +1,19 @@
import { svgEl } from '../helpers';
export function renderXIcon24() {
const NS = 'http://www.w3.org/2000/svg';
return svgEl(
'svg',
{
xmlns: NS,
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
class: 'lucide lucide-x-icon lucide-x',
},
[svgEl('path', { d: 'M18 6 6 18' }), svgEl('path', { d: 'm6 6 12 12' })],
);
}

View File

@@ -66,19 +66,19 @@ export async function runDnsCheck() {
{
state: data.bootstrap_dns_status ? 'success' : 'error',
key: _('Bootsrap DNS'),
value: `${data.bootstrap_dns_server} ${data.bootstrap_dns_status ? '✅' : '❌'}`,
value: data.bootstrap_dns_server,
},
],
),
{
state: data.dns_status ? 'success' : 'error',
key: _('Main DNS'),
value: `${data.dns_server} [${data.dns_type}] ${data.dns_status ? '✅' : '❌'}`,
value: `${data.dns_server} [${data.dns_type}]`,
},
{
state: data.local_dns_status ? 'success' : 'error',
key: _('Local DNS'),
value: data.local_dns_status ? '✅' : '',
value: '',
},
],
});

View File

@@ -8,6 +8,22 @@ export async function runFakeIPCheck() {
title: _('Fake IP checks'),
description: _('Not implemented yet'),
state: 'skipped',
items: [],
items: [
{
state: 'success',
key: 'success',
value: '',
},
{
state: 'warning',
key: 'warning',
value: '',
},
{
state: 'error',
key: 'error',
value: '',
},
],
});
}

View File

@@ -1,9 +1,13 @@
import { getNftRulesCheck } from '../../../methods';
import { updateDiagnosticsCheck } from '../updateDiagnosticsCheck';
import { getFakeIpCheck, getIpCheck } from '../../../../fakeip';
export async function runNftCheck() {
const code = 'nft_check';
await getFakeIpCheck();
await getIpCheck();
updateDiagnosticsCheck({
code,
title: _('Nftables checks'),
@@ -73,42 +77,42 @@ export async function runNftCheck() {
{
state: data.table_exist ? 'success' : 'error',
key: _('Table exist'),
value: data.table_exist ? _('Yes') : _('No'),
value: '',
},
{
state: data.rules_mangle_exist ? 'success' : 'error',
key: _('Rules mangle exist'),
value: data.rules_mangle_exist ? _('Yes') : _('No'),
value: '',
},
{
state: data.rules_mangle_counters ? 'success' : 'error',
key: _('Rules mangle counters'),
value: data.rules_mangle_counters ? _('Yes') : _('No'),
value: '',
},
{
state: data.rules_mangle_output_exist ? 'success' : 'error',
key: _('Rules mangle output exist'),
value: data.rules_mangle_output_exist ? _('Yes') : _('No'),
value: '',
},
{
state: data.rules_mangle_output_counters ? 'success' : 'error',
key: _('Rules mangle output counters'),
value: data.rules_mangle_output_counters ? _('Yes') : _('No'),
value: '',
},
{
state: data.rules_proxy_exist ? 'success' : 'error',
key: _('Rules proxy exist'),
value: data.rules_proxy_exist ? _('Yes') : _('No'),
value: '',
},
{
state: data.rules_proxy_counters ? 'success' : 'error',
key: _('Rules proxy counters'),
value: data.rules_proxy_counters ? _('Yes') : _('No'),
value: '',
},
{
state: data.rules_other_mark_exist ? 'warning' : 'success',
key: _('Rules other mark exist'),
value: data.rules_other_mark_exist ? _('Yes') : _('No'),
state: !data.rules_other_mark_exist ? 'success' : 'warning',
key: _('None other Mark rules'),
value: '',
},
],
});

View File

@@ -67,32 +67,32 @@ export async function runSingBoxCheck() {
{
state: data.sing_box_installed ? 'success' : 'error',
key: _('Sing-box installed'),
value: data.sing_box_installed ? _('Yes') : _('No'),
value: '',
},
{
state: data.sing_box_version_ok ? 'success' : 'error',
key: _('Sing-box version >= 1.12.4'),
value: data.sing_box_version_ok ? _('Yes') : _('No'),
value: '',
},
{
state: data.sing_box_service_exist ? 'success' : 'error',
key: _('Sing-box service exist'),
value: data.sing_box_service_exist ? _('Yes') : _('No'),
value: '',
},
{
state: data.sing_box_autostart_disabled ? 'success' : 'error',
key: _('Sing-box autostart disabled'),
value: data.sing_box_autostart_disabled ? _('Yes') : _('No'),
value: '',
},
{
state: data.sing_box_process_running ? 'success' : 'error',
key: _('Sing-box process running'),
value: data.sing_box_process_running ? _('Yes') : _('No'),
value: '',
},
{
state: data.sing_box_ports_listening ? 'success' : 'error',
key: _('Sing-box listening ports'),
value: data.sing_box_ports_listening ? _('Yes') : _('No'),
value: '',
},
],
});

View File

@@ -1,9 +1,12 @@
import {
renderCheckIcon24,
renderCircleAlertIcon24,
renderCircleCheckIcon24,
renderCircleSlashIcon24,
renderCircleXIcon24,
renderLoaderCircleIcon24,
renderTriangleAlertIcon24,
renderXIcon24,
} from '../../../icons';
import { IDiagnosticsChecksStoreItem } from '../../../store';
@@ -14,15 +17,35 @@ function renderCheckSummary(items: IRenderCheckSectionProps['items']) {
return E('div', {}, '');
}
const renderedItems = items.map((item) =>
E(
const renderedItems = items.map((item) => {
function getIcon() {
const iconWrap = E('span', {
class: 'pdk_diagnostic_alert__summary__item__icon',
});
if (item.state === 'success') {
iconWrap.appendChild(renderCheckIcon24());
}
if (item.state === 'warning') {
iconWrap.appendChild(renderTriangleAlertIcon24());
}
if (item.state === 'error') {
iconWrap.appendChild(renderXIcon24());
}
return iconWrap;
}
return E(
'div',
{
class: `pdk_diagnostic_alert__summary__item pdk_diagnostic_alert__summary__item--${item.state}`,
},
[E('b', {}, item.key), E('div', {}, item.value)],
),
);
[getIcon(), E('b', {}, item.key), E('div', {}, item.value)],
);
});
return E('div', { class: 'pdk_diagnostic_alert__summary' }, renderedItems);
}

View File

@@ -267,7 +267,7 @@ export const GlobalStyles = `
.pdk_diagnostic_alert__summary__item {
display: grid;
grid-template-columns: auto 1fr;
grid-template-columns: 16px auto 1fr;
grid-column-gap: 10px;
}
@@ -283,4 +283,9 @@ export const GlobalStyles = `
color: var(--success-color-medium, green);
}
.pdk_diagnostic_alert__summary__item__icon {
width: 16px;
height: 16px;
}
`;