refactor: reorganize all methods

This commit is contained in:
divocat
2025-10-14 21:27:16 +03:00
parent a9fdf286e0
commit de3e67f999
40 changed files with 355 additions and 562 deletions

View File

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

View File

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

View File

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

View File

@@ -1,14 +0,0 @@
import { ClashAPI } from '../types';
import { getClashApiUrl } from '../../helpers';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
export async function getClashConfig(): Promise<
IBaseApiResponse<ClashAPI.Config>
> {
return createBaseApiRequest<ClashAPI.Config>(() =>
fetch(`${getClashApiUrl()}/configs`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
}),
);
}

View File

@@ -1,20 +0,0 @@
import { ClashAPI } from '../types';
import { getClashApiUrl } from '../../helpers';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
export async function getClashGroupDelay(
group: string,
url = 'https://www.gstatic.com/generate_204',
timeout = 2000,
): Promise<IBaseApiResponse<ClashAPI.Delays>> {
const endpoint = `${getClashApiUrl()}/group/${group}/delay?url=${encodeURIComponent(
url,
)}&timeout=${timeout}`;
return createBaseApiRequest<ClashAPI.Delays>(() =>
fetch(endpoint, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
}),
);
}

View File

@@ -1,14 +0,0 @@
import { ClashAPI } from '../types';
import { getClashApiUrl } from '../../helpers';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
export async function getClashVersion(): Promise<
IBaseApiResponse<ClashAPI.Version>
> {
return createBaseApiRequest<ClashAPI.Version>(() =>
fetch(`${getClashApiUrl()}/version`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
}),
);
}

View File

@@ -1,6 +0,0 @@
export * from './getConfig';
export * from './getGroupDelay';
export * from './getProxies';
export * from './getVersion';
export * from './triggerProxySelector';
export * from './triggerLatencyTest';

View File

@@ -1,34 +0,0 @@
import { getClashApiUrl } from '../../helpers';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
export async function triggerLatencyGroupTest(
tag: string,
timeout: number = 5000,
url: string = 'https://www.gstatic.com/generate_204',
): Promise<IBaseApiResponse<void>> {
return createBaseApiRequest<void>(() =>
fetch(
`${getClashApiUrl()}/group/${tag}/delay?url=${encodeURIComponent(url)}&timeout=${timeout}`,
{
method: 'GET',
headers: { 'Content-Type': 'application/json' },
},
),
);
}
export async function triggerLatencyProxyTest(
tag: string,
timeout: number = 2000,
url: string = 'https://www.gstatic.com/generate_204',
): Promise<IBaseApiResponse<void>> {
return createBaseApiRequest<void>(() =>
fetch(
`${getClashApiUrl()}/proxies/${tag}/delay?url=${encodeURIComponent(url)}&timeout=${timeout}`,
{
method: 'GET',
headers: { 'Content-Type': 'application/json' },
},
),
);
}

View File

@@ -1,43 +0,0 @@
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace ClashAPI {
export interface Version {
meta: boolean;
premium: boolean;
version: string;
}
export interface Config {
port: number;
'socks-port': number;
'redir-port': number;
'tproxy-port': number;
'mixed-port': number;
'allow-lan': boolean;
'bind-address': string;
mode: 'Rule' | 'Global' | 'Direct';
'mode-list': string[];
'log-level': 'debug' | 'info' | 'warn' | 'error';
ipv6: boolean;
tun: null | Record<string, unknown>;
}
export interface ProxyHistoryEntry {
time: string;
delay: number;
}
export interface ProxyBase {
type: string;
name: string;
udp: boolean;
history: ProxyHistoryEntry[];
now?: string;
all?: string[];
}
export interface Proxies {
proxies: Record<string, ProxyBase>;
}
export type Delays = Record<string, number>;
}

View File

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

View File

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

View File

@@ -5,6 +5,5 @@
export * from './validators';
export * from './helpers';
export * from './clash';
export * from './podkop';
export * from './constants';

View File

@@ -1,4 +1,3 @@
import { IBaseApiResponse } from './types';
import { withTimeout } from '../helpers';
export async function createBaseApiRequest<T>(
@@ -42,3 +41,13 @@ export async function createBaseApiRequest<T>(
};
}
}
export type IBaseApiResponse<T> =
| {
success: true;
data: T;
}
| {
success: false;
message: string;
};

View File

@@ -0,0 +1,18 @@
import { getClashApiUrl } from '../../../helpers';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
export async function getGroupLatency(
tag: string,
timeout: number = 5000,
url: string = 'https://www.gstatic.com/generate_204',
): Promise<IBaseApiResponse<void>> {
return createBaseApiRequest<void>(() =>
fetch(
`${getClashApiUrl()}/group/${tag}/delay?url=${encodeURIComponent(url)}&timeout=${timeout}`,
{
method: 'GET',
headers: { 'Content-Type': 'application/json' },
},
),
);
}

View File

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

View File

@@ -0,0 +1,18 @@
import { getClashApiUrl } from '../../../helpers';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
export async function getProxyLatency(
tag: string,
timeout: number = 2000,
url: string = 'https://www.gstatic.com/generate_204',
): Promise<IBaseApiResponse<void>> {
return createBaseApiRequest<void>(() =>
fetch(
`${getClashApiUrl()}/proxies/${tag}/delay?url=${encodeURIComponent(url)}&timeout=${timeout}`,
{
method: 'GET',
headers: { 'Content-Type': 'application/json' },
},
),
);
}

View File

@@ -0,0 +1,11 @@
import { getGroupLatency } from './getGroupLatency';
import { getProxies } from './getProxies';
import { getProxyLatency } from './getProxyLatency';
import { setProxy } from './setProxy';
export const ClashMethods = {
getGroupLatency,
getProxies,
getProxyLatency,
setProxy,
};

View File

@@ -1,7 +1,7 @@
import { getClashApiUrl } from '../../helpers';
import { getClashApiUrl } from '../../../helpers';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
export async function triggerProxySelector(
export async function setProxy(
selector: string,
outbound: string,
): Promise<IBaseApiResponse<void>> {

View File

@@ -1,4 +1,4 @@
import { Podkop } from '../types';
import { Podkop } from '../../types';
export async function getConfigSections(): Promise<Podkop.ConfigSection[]> {
return uci.load('podkop').then(() => uci.sections('podkop'));

View File

@@ -1,7 +1,7 @@
import { Podkop } from '../types';
import { getConfigSections } from './getConfigSections';
import { getClashProxies } from '../../clash';
import { getProxyUrlName, splitProxyString } from '../../helpers';
import { Podkop } from '../../types';
import { ClashMethods } from '../clash';
import { getProxyUrlName, splitProxyString } from '../../../helpers';
interface IGetDashboardSectionsResponse {
success: boolean;
@@ -10,7 +10,7 @@ interface IGetDashboardSectionsResponse {
export async function getDashboardSections(): Promise<IGetDashboardSectionsResponse> {
const configSections = await getConfigSections();
const clashProxies = await getClashProxies();
const clashProxies = await ClashMethods.getProxies();
if (!clashProxies.success) {
return {

View File

@@ -0,0 +1,7 @@
import { getConfigSections } from './getConfigSections';
import { getDashboardSections } from './getDashboardSections';
export const CustomPodkopMethods = {
getConfigSections,
getDashboardSections,
};

View File

@@ -1,5 +1,5 @@
import { FAKEIP_CHECK_DOMAIN } from '../../../constants';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
import { FAKEIP_CHECK_DOMAIN } from '../../constants';
interface IGetFakeIpCheckResponse {
fakeip: boolean;

View File

@@ -1,5 +1,5 @@
import { IP_CHECK_DOMAIN } from '../../../constants';
import { createBaseApiRequest, IBaseApiResponse } from '../../api';
import { IP_CHECK_DOMAIN } from '../../constants';
interface IGetIpCheckResponse {
fakeip: boolean;

View File

@@ -0,0 +1,7 @@
import { getFakeIpCheck } from './getFakeIpCheck';
import { getIpCheck } from './getIpCheck';
export const RemoteFakeIPMethods = {
getFakeIpCheck,
getIpCheck,
};

View File

@@ -1,24 +0,0 @@
import { executeShellCommand } from '../../helpers';
import { Podkop } from '../types';
export async function getDNSCheck(): Promise<
Podkop.MethodResponse<Podkop.DnsCheckResult>
> {
const response = await executeShellCommand({
command: '/usr/bin/podkop',
args: ['check_dns_available'],
timeout: 10000,
});
if (response.stdout) {
return {
success: true,
data: JSON.parse(response.stdout) as Podkop.DnsCheckResult,
};
}
return {
success: false,
error: '',
};
}

View File

@@ -1,24 +0,0 @@
import { executeShellCommand } from '../../helpers';
import { Podkop } from '../types';
export async function getFakeIPCheck(): Promise<
Podkop.MethodResponse<Podkop.FakeIPCheckResult>
> {
const response = await executeShellCommand({
command: '/usr/bin/podkop',
args: ['check_fakeip'],
timeout: 10000,
});
if (response.stdout) {
return {
success: true,
data: JSON.parse(response.stdout) as Podkop.FakeIPCheckResult,
};
}
return {
success: false,
error: '',
};
}

View File

@@ -1,24 +0,0 @@
import { executeShellCommand } from '../../helpers';
import { Podkop } from '../types';
export async function getNftRulesCheck(): Promise<
Podkop.MethodResponse<Podkop.NftRulesCheckResult>
> {
const response = await executeShellCommand({
command: '/usr/bin/podkop',
args: ['check_nft_rules'],
timeout: 10000,
});
if (response.stdout) {
return {
success: true,
data: JSON.parse(response.stdout) as Podkop.NftRulesCheckResult,
};
}
return {
success: false,
error: '',
};
}

View File

@@ -1,21 +0,0 @@
import { executeShellCommand } from '../../helpers';
export async function getPodkopStatus(): Promise<{
enabled: number;
status: string;
}> {
const response = await executeShellCommand({
command: '/usr/bin/podkop',
args: ['get_status'],
timeout: 10000,
});
if (response.stdout) {
return JSON.parse(response.stdout.replace(/\n/g, '')) as {
enabled: number;
status: string;
};
}
return { enabled: 0, status: 'unknown' };
}

View File

@@ -1,24 +0,0 @@
import { executeShellCommand } from '../../helpers';
import { Podkop } from '../types';
export async function getSingBoxCheck(): Promise<
Podkop.MethodResponse<Podkop.SingBoxCheckResult>
> {
const response = await executeShellCommand({
command: '/usr/bin/podkop',
args: ['check_sing_box'],
timeout: 10000,
});
if (response.stdout) {
return {
success: true,
data: JSON.parse(response.stdout) as Podkop.SingBoxCheckResult,
};
}
return {
success: false,
error: '',
};
}

View File

@@ -1,23 +0,0 @@
import { executeShellCommand } from '../../helpers';
export async function getSingBoxStatus(): Promise<{
running: number;
enabled: number;
status: string;
}> {
const response = await executeShellCommand({
command: '/usr/bin/podkop',
args: ['get_sing_box_status'],
timeout: 10000,
});
if (response.stdout) {
return JSON.parse(response.stdout.replace(/\n/g, '')) as {
running: number;
enabled: number;
status: string;
};
}
return { running: 0, enabled: 0, status: 'unknown' };
}

View File

@@ -1,8 +1,4 @@
export * from './getConfigSections';
export * from './getDashboardSections';
export * from './getPodkopStatus';
export * from './getSingBoxStatus';
export * from './getDNSCheck';
export * from './getNftRulesCheck';
export * from './getSingBoxCheck';
export * from './getFakeIPCheck';
export * from './clash';
export * from './custom';
export * from './fakeip';
export * from './shell';

View File

@@ -0,0 +1,24 @@
import { executeShellCommand } from '../../../helpers';
import { Podkop } from '../../types';
export async function callBaseMethod<T>(
method: Podkop.AvailableMethods,
): Promise<Podkop.MethodResponse<T>> {
const response = await executeShellCommand({
command: '/usr/bin/podkop',
args: [method],
timeout: 10000,
});
if (response.stdout) {
return {
success: true,
data: JSON.parse(response.stdout) as T,
};
}
return {
success: false,
error: '',
};
}

View File

@@ -0,0 +1,27 @@
import { callBaseMethod } from './callBaseMethod';
import { Podkop } from '../../types';
export const PodkopShellMethods = {
checkDNSAvailable: async () =>
callBaseMethod<Podkop.DnsCheckResult>(
Podkop.AvailableMethods.CHECK_DNS_AVAILABLE,
),
checkFakeIP: async () =>
callBaseMethod<Podkop.FakeIPCheckResult>(
Podkop.AvailableMethods.CHECK_FAKEIP,
),
checkNftRules: async () =>
callBaseMethod<Podkop.NftRulesCheckResult>(
Podkop.AvailableMethods.CHECK_NFT_RULES,
),
getStatus: async () =>
callBaseMethod<Podkop.GetStatus>(Podkop.AvailableMethods.GET_STATUS),
checkSingBox: async () =>
callBaseMethod<Podkop.SingBoxCheckResult>(
Podkop.AvailableMethods.CHECK_SING_BOX,
),
getSingBoxStatus: async () =>
callBaseMethod<Podkop.GetSingBoxStatus>(
Podkop.AvailableMethods.GET_SING_BOX_STATUS,
),
};

View File

@@ -1,24 +1,19 @@
import {
getDashboardSections,
getPodkopStatus,
getSingBoxStatus,
} from '../../methods';
import {
getClashApiUrl,
getClashWsUrl,
onMount,
preserveScrollForPage,
} from '../../../helpers';
import {
triggerLatencyGroupTest,
triggerLatencyProxyTest,
triggerProxySelector,
} from '../../../clash';
import { store, StoreType } from '../../../store';
import { socket } from '../../../socket';
import { prettyBytes } from '../../../helpers/prettyBytes';
import { renderSections } from './renderSections';
import { renderWidget } from './renderWidget';
import {
ClashMethods,
CustomPodkopMethods,
PodkopShellMethods,
} from '../../methods';
// Fetchers
@@ -32,7 +27,7 @@ async function fetchDashboardSections() {
},
});
const { data, success } = await getDashboardSections();
const { data, success } = await CustomPodkopMethods.getDashboardSections();
if (!success) {
console.log('[fetchDashboardSections]: failed to fetch', getClashApiUrl());
@@ -49,22 +44,12 @@ async function fetchDashboardSections() {
}
async function fetchServicesInfo() {
try {
const [podkop, singbox] = await Promise.all([
getPodkopStatus(),
getSingBoxStatus(),
]);
store.set({
servicesInfoWidget: {
loading: false,
failed: false,
data: { singbox: singbox.running, podkop: podkop.enabled },
},
});
} catch (err) {
console.log('[fetchServicesInfo]: failed to fetchServices', err);
const [podkop, singbox] = await Promise.all([
PodkopShellMethods.getStatus(),
PodkopShellMethods.getSingBoxStatus(),
]);
if (!podkop.success || !singbox.success) {
store.set({
servicesInfoWidget: {
loading: false,
@@ -73,6 +58,16 @@ async function fetchServicesInfo() {
},
});
}
if (podkop.success && singbox.success) {
store.set({
servicesInfoWidget: {
loading: false,
failed: false,
data: { singbox: singbox.data.running, podkop: podkop.data.enabled },
},
});
}
}
async function connectToClashSockets() {
@@ -155,7 +150,7 @@ async function connectToClashSockets() {
// Handlers
async function handleChooseOutbound(selector: string, tag: string) {
await triggerProxySelector(selector, tag);
await ClashMethods.setProxy(selector, tag);
await fetchDashboardSections();
}
@@ -167,7 +162,7 @@ async function handleTestGroupLatency(tag: string) {
},
});
await triggerLatencyGroupTest(tag);
await ClashMethods.getGroupLatency(tag);
await fetchDashboardSections();
store.set({
@@ -186,7 +181,7 @@ async function handleTestProxyLatency(tag: string) {
},
});
await triggerLatencyProxyTest(tag);
await ClashMethods.getProxyLatency(tag);
await fetchDashboardSections();
store.set({

View File

@@ -1,8 +1,8 @@
import { getDNSCheck } from '../../../methods';
import { updateDiagnosticsCheck } from '../updateDiagnosticsCheck';
import { insertIf } from '../../../../helpers';
import { IDiagnosticsChecksItem } from '../../../../store';
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { PodkopShellMethods } from '../../../methods';
export async function runDnsCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.DNS;
@@ -16,7 +16,7 @@ export async function runDnsCheck() {
items: [],
});
const dnsChecks = await getDNSCheck();
const dnsChecks = await PodkopShellMethods.checkDNSAvailable();
if (!dnsChecks.success) {
updateDiagnosticsCheck({

View File

@@ -1,9 +1,8 @@
import * as podkopMethods from '../../../methods';
import * as fakeIPMethods from '../../../../fakeip/methods';
import { updateDiagnosticsCheck } from '../updateDiagnosticsCheck';
import { insertIf } from '../../../../helpers';
import { IDiagnosticsChecksItem } from '../../../../store';
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { PodkopShellMethods, RemoteFakeIPMethods } from '../../../methods';
export async function runFakeIPCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.FAKEIP;
@@ -17,9 +16,9 @@ export async function runFakeIPCheck() {
items: [],
});
const routerFakeIPResponse = await podkopMethods.getFakeIPCheck();
const checkFakeIPResponse = await fakeIPMethods.getFakeIpCheck();
const checkIPResponse = await fakeIPMethods.getIpCheck();
const routerFakeIPResponse = await PodkopShellMethods.checkFakeIP();
const checkFakeIPResponse = await RemoteFakeIPMethods.getFakeIpCheck();
const checkIPResponse = await RemoteFakeIPMethods.getIpCheck();
console.log('runFakeIPCheck', {
routerFakeIPResponse,

View File

@@ -1,7 +1,7 @@
import { getNftRulesCheck } from '../../../methods';
import { updateDiagnosticsCheck } from '../updateDiagnosticsCheck';
import { getFakeIpCheck, getIpCheck } from '../../../../fakeip';
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { RemoteFakeIPMethods } from '../../../methods/fakeip';
import { PodkopShellMethods } from '../../../methods';
export async function runNftCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.NFT;
@@ -15,10 +15,10 @@ export async function runNftCheck() {
items: [],
});
await getFakeIpCheck();
await getIpCheck();
await RemoteFakeIPMethods.getFakeIpCheck();
await RemoteFakeIPMethods.getIpCheck();
const nftablesChecks = await getNftRulesCheck();
const nftablesChecks = await PodkopShellMethods.checkNftRules();
if (!nftablesChecks.success) {
updateDiagnosticsCheck({

View File

@@ -1,6 +1,6 @@
import { getSingBoxCheck } from '../../../methods';
import { updateDiagnosticsCheck } from '../updateDiagnosticsCheck';
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { PodkopShellMethods } from '../../../methods';
export async function runSingBoxCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.SINGBOX;
@@ -14,7 +14,7 @@ export async function runSingBoxCheck() {
items: [],
});
const singBoxChecks = await getSingBoxCheck();
const singBoxChecks = await PodkopShellMethods.checkSingBox();
if (!singBoxChecks.success) {
updateDiagnosticsCheck({

View File

@@ -1,5 +1,35 @@
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace ClashAPI {
export interface ProxyHistoryEntry {
time: string;
delay: number;
}
export interface ProxyBase {
type: string;
name: string;
udp: boolean;
history: ProxyHistoryEntry[];
now?: string;
all?: string[];
}
export interface Proxies {
proxies: Record<string, ProxyBase>;
}
}
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Podkop {
export enum AvailableMethods {
CHECK_DNS_AVAILABLE = 'check_dns_available',
CHECK_FAKEIP = 'check_fakeip',
CHECK_NFT_RULES = 'check_nft_rules',
GET_STATUS = 'get_status',
CHECK_SING_BOX = 'check_sing_box',
GET_SING_BOX_STATUS = 'get_sing_box_status',
}
export interface Outbound {
code: string;
displayName: string;
@@ -102,4 +132,15 @@ export namespace Podkop {
fakeip: boolean;
IP: string;
}
export interface GetStatus {
enabled: number;
status: string;
}
export interface GetSingBoxStatus {
running: number;
enabled: number;
status: string;
}
}