Files
clash-proxy/src/services/api.ts

361 lines
9.3 KiB
TypeScript
Raw Normal View History

2024-01-15 10:18:04 +08:00
import axios, { AxiosInstance } from "axios";
2021-12-25 23:20:59 +08:00
import { getClashInfo } from "./cmds";
import { invoke } from "@tauri-apps/api/core";
import { useLockFn } from "ahooks";
2024-01-15 10:18:04 +08:00
let instancePromise: Promise<AxiosInstance> = null!;
2024-01-15 10:18:04 +08:00
async function getInstancePromise() {
2022-12-15 12:22:20 +08:00
let server = "";
let secret = "";
2021-12-25 23:20:59 +08:00
try {
2024-01-15 10:18:04 +08:00
const info = await getClashInfo();
2021-12-25 23:20:59 +08:00
2022-03-10 02:19:06 +08:00
if (info?.server) {
server = info.server;
2024-01-15 10:18:04 +08:00
2022-03-10 02:19:06 +08:00
// compatible width `external-controller`
if (server.startsWith(":")) server = `127.0.0.1${server}`;
else if (/^\d+$/.test(server)) server = `127.0.0.1:${server}`;
}
2022-01-07 23:29:20 +08:00
if (info?.secret) secret = info?.secret;
2021-12-25 23:20:59 +08:00
} catch {}
2021-12-25 22:33:29 +08:00
const axiosIns = axios.create({
2024-01-15 10:18:04 +08:00
baseURL: `http://${server}`,
2021-12-25 22:33:29 +08:00
headers: secret ? { Authorization: `Bearer ${secret}` } : {},
2023-03-16 17:03:12 +08:00
timeout: 15000,
2021-12-25 22:33:29 +08:00
});
2024-01-15 10:18:04 +08:00
axiosIns.interceptors.response.use((r) => r.data);
return axiosIns;
}
/// initialize some information
/// enable force update axiosIns
export const getAxios = async (force: boolean = false) => {
if (!instancePromise || force) {
instancePromise = getInstancePromise();
}
return instancePromise;
2022-12-15 12:22:20 +08:00
};
2021-12-25 22:33:29 +08:00
/// Get Version
2022-12-15 12:22:20 +08:00
export const getVersion = async () => {
2024-01-15 10:18:04 +08:00
const instance = await getAxios();
return instance.get("/version") as Promise<{
2021-12-25 22:33:29 +08:00
premium: boolean;
2022-11-23 17:44:40 +08:00
meta?: boolean;
2021-12-25 22:33:29 +08:00
version: string;
}>;
2022-12-15 12:22:20 +08:00
};
2021-12-25 22:33:29 +08:00
/// Get current base configs
2022-12-15 12:22:20 +08:00
export const getClashConfig = async () => {
2024-01-15 10:18:04 +08:00
const instance = await getAxios();
return instance.get("/configs") as Promise<IConfigData>;
2022-12-15 12:22:20 +08:00
};
2021-12-25 22:33:29 +08:00
2023-12-10 15:22:04 +08:00
/// Update geo data
export const updateGeoData = async () => {
2024-01-15 10:18:04 +08:00
const instance = await getAxios();
return instance.post("/configs/geo");
2023-12-10 15:22:04 +08:00
};
2023-12-10 15:57:10 +08:00
/// Upgrade clash core
export const upgradeCore = async () => {
2024-01-15 10:18:04 +08:00
const instance = await getAxios();
return instance.post("/upgrade");
2023-12-10 15:57:10 +08:00
};
2021-12-25 22:33:29 +08:00
/// Get current rules
2022-12-15 12:22:20 +08:00
export const getRules = async () => {
2024-01-15 10:18:04 +08:00
const instance = await getAxios();
const response = await instance.get<any, any>("/rules");
return response?.rules as IRuleItem[];
2022-12-15 12:22:20 +08:00
};
2021-12-25 22:33:29 +08:00
2022-02-16 02:22:01 +08:00
/// Get Proxy delay
export const getProxyDelay = async (
name: string,
url?: string,
2024-11-23 11:34:17 +08:00
timeout?: number,
) => {
2022-02-16 02:22:01 +08:00
const params = {
timeout: timeout || 10000,
2024-11-23 11:34:17 +08:00
url: url || "http://cp.cloudflare.com/generate_204",
2022-02-16 02:22:01 +08:00
};
2024-01-15 10:18:04 +08:00
const instance = await getAxios();
const result = await instance.get(
2022-12-15 12:22:20 +08:00
`/proxies/${encodeURIComponent(name)}/delay`,
2024-11-23 11:34:17 +08:00
{ params },
2022-12-15 12:22:20 +08:00
);
2024-01-15 10:18:04 +08:00
return result as any as { delay: number };
2022-12-15 12:22:20 +08:00
};
2022-02-16 02:22:01 +08:00
2021-12-25 22:33:29 +08:00
/// Update the Proxy Choose
2022-12-15 12:22:20 +08:00
export const updateProxy = async (group: string, proxy: string) => {
2024-01-15 10:18:04 +08:00
const instance = await getAxios();
return instance.put(`/proxies/${encodeURIComponent(group)}`, { name: proxy });
2022-12-15 12:22:20 +08:00
};
2021-12-25 22:33:29 +08:00
// get proxy
2022-12-15 12:22:20 +08:00
export const getProxiesInner = async () => {
const response = await invoke<{ proxies: Record<string, IProxyItem> }>(
"get_proxies",
);
return response.proxies as Record<string, IProxyItem>;
2022-12-15 12:22:20 +08:00
};
2022-09-24 18:48:11 +08:00
/// Get the Proxy information
export const getProxies = async (): Promise<{
global: IProxyGroupItem;
direct: IProxyItem;
groups: IProxyGroupItem[];
records: Record<string, IProxyItem>;
proxies: IProxyItem[];
}> => {
const [proxyRecord, providerRecord] = await Promise.all([
getProxiesInner(),
2024-01-18 01:01:47 +08:00
getProxyProviders(),
]);
// provider name map
const providerMap = Object.fromEntries(
Object.entries(providerRecord).flatMap(([provider, item]) =>
2024-11-23 11:34:17 +08:00
item.proxies.map((p) => [p.name, { ...p, provider }]),
),
);
2021-12-25 22:33:29 +08:00
// compatible with proxy-providers
const generateItem = (name: string) => {
if (proxyRecord[name]) return proxyRecord[name];
if (providerMap[name]) return providerMap[name];
2024-01-11 12:34:05 +08:00
return {
name,
type: "unknown",
udp: false,
xudp: false,
tfo: false,
mptcp: false,
smux: false,
2024-01-11 12:34:05 +08:00
history: [],
};
};
const { GLOBAL: global, DIRECT: direct, REJECT: reject } = proxyRecord;
2024-06-15 19:23:58 +08:00
let groups: IProxyGroupItem[] = Object.values(proxyRecord).reduce<
IProxyGroupItem[]
>((acc, each) => {
if (each.name !== "GLOBAL" && each.all) {
acc.push({
...each,
all: each.all!.map((item) => generateItem(item)),
});
}
2024-06-15 19:23:58 +08:00
return acc;
}, []);
if (global?.all) {
2024-06-15 19:23:58 +08:00
let globalGroups: IProxyGroupItem[] = global.all.reduce<IProxyGroupItem[]>(
(acc, name) => {
if (proxyRecord[name]?.all) {
acc.push({
...proxyRecord[name],
all: proxyRecord[name].all!.map((item) => generateItem(item)),
});
}
return acc;
},
2024-11-23 11:34:17 +08:00
[],
2024-06-15 19:23:58 +08:00
);
let globalNames = new Set(globalGroups.map((each) => each.name));
2024-02-11 17:59:56 +08:00
groups = groups
.filter((group) => {
return !globalNames.has(group.name);
2024-02-11 17:59:56 +08:00
})
.concat(globalGroups);
2021-12-25 22:33:29 +08:00
}
2022-02-26 17:39:08 +08:00
const proxies = [direct, reject].concat(
Object.values(proxyRecord).filter(
2024-11-23 11:34:17 +08:00
(p) => !p.all?.length && p.name !== "DIRECT" && p.name !== "REJECT",
),
2022-02-26 17:39:08 +08:00
);
2022-11-19 17:22:29 +08:00
const _global: IProxyGroupItem = {
...global,
all: global?.all?.map((item) => generateItem(item)) || [],
};
return { global: _global, direct, groups, records: proxyRecord, proxies };
2022-12-15 12:22:20 +08:00
};
// get proxy providers
2024-01-18 01:01:47 +08:00
export const getProxyProviders = async () => {
const response = await invoke<{
providers: Record<string, IProxyProviderItem>;
}>("get_providers_proxies");
const providers = response.providers as Record<string, IProxyProviderItem>;
2024-01-18 01:01:47 +08:00
return Object.fromEntries(
Object.entries(providers).filter(([key, item]) => {
const type = item.vehicleType.toLowerCase();
return type === "http" || type === "file";
2024-11-23 11:34:17 +08:00
}),
2024-01-18 01:01:47 +08:00
);
};
export const getRuleProviders = async () => {
const instance = await getAxios();
const response = await instance.get<any, any>("/providers/rules");
const providers = (response.providers || {}) as Record<
string,
IRuleProviderItem
>;
2022-10-11 22:51:18 +08:00
return Object.fromEntries(
Object.entries(providers).filter(([key, item]) => {
const type = item.vehicleType.toLowerCase();
return type === "http" || type === "file";
2024-11-23 11:34:17 +08:00
}),
2022-10-11 22:51:18 +08:00
);
2022-12-15 12:22:20 +08:00
};
2022-03-18 14:43:22 +08:00
// proxy providers health check
2022-12-15 12:22:20 +08:00
export const providerHealthCheck = async (name: string) => {
2024-01-15 10:18:04 +08:00
const instance = await getAxios();
return instance.get(
2024-11-23 11:34:17 +08:00
`/providers/proxies/${encodeURIComponent(name)}/healthcheck`,
2022-03-30 01:30:22 +08:00
);
2022-12-15 12:22:20 +08:00
};
2022-03-30 01:30:22 +08:00
2024-01-18 01:01:47 +08:00
export const proxyProviderUpdate = async (name: string) => {
2024-01-15 10:18:04 +08:00
const instance = await getAxios();
return instance.put(`/providers/proxies/${encodeURIComponent(name)}`);
2023-08-05 21:38:44 +08:00
};
2024-01-18 01:01:47 +08:00
export const ruleProviderUpdate = async (name: string) => {
const instance = await getAxios();
return instance.put(`/providers/rules/${encodeURIComponent(name)}`);
};
2022-12-15 12:22:20 +08:00
export const getConnections = async () => {
2024-01-15 10:18:04 +08:00
const instance = await getAxios();
const result = await instance.get("/connections");
return result as any as IConnections;
2022-12-15 12:22:20 +08:00
};
2022-03-18 14:43:22 +08:00
// Close specific connection
2022-12-15 12:22:20 +08:00
export const deleteConnection = async (id: string) => {
2024-01-15 10:18:04 +08:00
const instance = await getAxios();
await instance.delete<any, any>(`/connections/${encodeURIComponent(id)}`);
2022-12-15 12:22:20 +08:00
};
2022-03-18 14:43:22 +08:00
// Close all connections
2022-12-15 12:22:20 +08:00
export const closeAllConnections = async () => {
2024-01-15 10:18:04 +08:00
const instance = await getAxios();
2025-03-14 13:31:34 +08:00
await instance.delete("/connections");
2022-12-15 12:22:20 +08:00
};
// Get Group Proxy Delays
export const getGroupProxyDelays = async (
groupName: string,
url?: string,
2024-11-23 11:34:17 +08:00
timeout?: number,
) => {
const params = {
timeout: timeout || 10000,
2024-11-23 11:34:17 +08:00
url: url || "http://cp.cloudflare.com/generate_204",
};
console.log(
`[API] 获取代理组延迟,组: ${groupName}, URL: ${params.url}, 超时: ${params.timeout}ms`,
);
try {
const instance = await getAxios();
console.log(
`[API] 发送HTTP请求: GET /group/${encodeURIComponent(groupName)}/delay`,
);
const result = await instance.get(
`/group/${encodeURIComponent(groupName)}/delay`,
{ params },
);
console.log(
`[API] 获取代理组延迟成功,组: ${groupName}, 结果数量:`,
Object.keys(result || {}).length,
);
return result as any as Record<string, number>;
} catch (error) {
console.error(`[API] 获取代理组延迟失败,组: ${groupName}`, error);
throw error;
}
};
// Is debug enabled
export const isDebugEnabled = async () => {
try {
const instance = await getAxios();
await instance.get("/debug/pprof");
return true;
} catch {
return false;
}
};
// GC
export const gc = async () => {
try {
const instance = await getAxios();
await instance.put("/debug/gc");
} catch (error) {
console.error(`Error gcing: ${error}`);
}
};
2025-03-14 13:31:34 +08:00
// Get current IP and geolocation information
export const getIpInfo = async () => {
// 添加重试机制
const maxRetries = 3;
const retryDelay = 1500;
const timeout = 5000;
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
// 使用axios直接请求IP.sb的API不通过clash代理
const response = await axios.get("https://api.ip.sb/geoip", { timeout });
return response.data as {
ip: string;
country_code: string;
country: string;
region: string;
city: string;
organization: string;
asn: number;
asn_organization: string;
longitude: number;
latitude: number;
timezone: string;
};
} catch (error) {
console.log(`获取IP信息失败尝试 ${attempt + 1}/${maxRetries}`, error);
lastError = error;
// 如果不是最后一次尝试,则等待后重试
if (attempt < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, retryDelay));
}
}
}
throw lastError;
2025-03-14 13:31:34 +08:00
};