refactor: invock mihomo api by use tauri-plugin-mihomo (#4926)

* feat: add tauri-plugin-mihomo

* refactor: invock mihomo api by use tauri-plugin-mihomo

* chore: todo

* chore: update

* chore: update

* chore: update

* chore: update

* fix: incorrect delay status and update pretty config

* chore: update

* chore: remove cache

* chore: update

* chore: update

* fix: app freezed when change group proxy

* chore: update

* chore: update

* chore: add rustfmt.toml to tauri-plugin-mihomo

* chore: happy clippy

* refactor: connect mihomo websocket

* chore: update

* chore: update

* fix: parse bigint to number

* chore: update

* Revert "fix: parse bigint to number"

This reverts commit 74c006522e23aa52cf8979a8fb47d2b1ae0bb043.

* chore: use number instead of bigint

* chore: cleanup

* fix: rule data not refresh when switch profile

* chore: update

* chore: cleanup

* chore: update

* fix: traffic graph data display

* feat: add ipc connection pool

* chore: update

* chore: clippy

* fix: incorrect delay status

* fix: typo

* fix: empty proxies tray menu

* chore: clippy

* chore: import tauri-plugin-mihomo by using git repo

* chore: cleanup

* fix: mihomo api

* fix: incorrect delay status

* chore: update tauri-plugin-mihomo dep

chore: update
This commit is contained in:
oomeow
2025-10-08 12:32:40 +08:00
committed by GitHub
parent 72aa56007c
commit 7fc238c27b
85 changed files with 1780 additions and 3344 deletions

View File

@@ -1,4 +1,6 @@
import { invoke } from "@tauri-apps/api/core";
import dayjs from "dayjs";
import { getProxies, getProxyProviders } from "tauri-plugin-mihomo-api";
import { showNotice } from "@/services/noticeService";
@@ -107,64 +109,11 @@ export async function patchClashMode(payload: string) {
return invoke<void>("patch_clash_mode", { payload });
}
// New IPC-based API functions to replace HTTP API calls
export async function getVersion() {
return invoke<{
premium: boolean;
meta?: boolean;
version: string;
}>("get_clash_version");
}
export async function getClashConfig() {
return invoke<IConfigData>("get_clash_config");
}
export async function forceRefreshClashConfig() {
return invoke<IConfigData>("force_refresh_clash_config");
}
export async function updateGeoData() {
return invoke<void>("update_geo_data");
}
export async function upgradeCore() {
return invoke<void>("upgrade_clash_core");
}
export async function getRules() {
const response = await invoke<{ rules: IRuleItem[] }>("get_clash_rules");
return response?.rules || [];
}
export async function getProxyDelay(
name: string,
url?: string,
timeout?: number,
) {
return invoke<{ delay: number }>("clash_api_get_proxy_delay", {
name,
url,
timeout: timeout || 10000,
});
}
export async function updateProxy(group: string, proxy: string) {
// const start = Date.now();
await invoke<void>("update_proxy_choice", { group, proxy });
// const duration = Date.now() - start;
// console.log(`[API] updateProxy 耗时: ${duration}ms`);
}
export async function syncTrayProxySelection() {
return invoke<void>("sync_tray_proxy_selection");
}
export async function updateProxyAndSync(group: string, proxy: string) {
return invoke<void>("update_proxy_and_sync", { group, proxy });
}
export async function getProxies(): Promise<{
export async function calcuProxies(): Promise<{
global: IProxyGroupItem;
direct: IProxyItem;
groups: IProxyGroupItem[];
@@ -172,19 +121,17 @@ export async function getProxies(): Promise<{
proxies: IProxyItem[];
}> {
const [proxyResponse, providerResponse] = await Promise.all([
invoke<{ proxies: Record<string, IProxyItem> }>("get_proxies"),
invoke<{ providers: Record<string, IProxyProviderItem> }>(
"get_providers_proxies",
),
getProxies(),
calcuProxyProviders(),
]);
const proxyRecord = proxyResponse.proxies;
const providerRecord = providerResponse.providers || {};
const providerRecord = providerResponse;
// provider name map
const providerMap = Object.fromEntries(
Object.entries(providerRecord).flatMap(([provider, item]) =>
item.proxies.map((p) => [p.name, { ...p, provider }]),
item!.proxies.map((p) => [p.name, { ...p, provider }]),
),
);
@@ -209,7 +156,7 @@ export async function getProxies(): Promise<{
let groups: IProxyGroupItem[] = Object.values(proxyRecord).reduce<
IProxyGroupItem[]
>((acc, each) => {
if (each.name !== "GLOBAL" && each.all) {
if (each?.name !== "GLOBAL" && each?.all) {
acc.push({
...each,
all: each.all!.map((item) => generateItem(item)),
@@ -242,209 +189,57 @@ export async function getProxies(): Promise<{
const proxies = [direct, reject].concat(
Object.values(proxyRecord).filter(
(p) => !p.all?.length && p.name !== "DIRECT" && p.name !== "REJECT",
(p) => !p?.all?.length && p?.name !== "DIRECT" && p?.name !== "REJECT",
),
);
const _global: IProxyGroupItem = {
const _global = {
...global,
all: global?.all?.map((item) => generateItem(item)) || [],
};
return { global: _global, direct, groups, records: proxyRecord, proxies };
return {
global: _global as IProxyGroupItem,
direct: direct as IProxyItem,
groups,
records: proxyRecord as Record<string, IProxyItem>,
proxies: (proxies as IProxyItem[]) ?? [],
};
}
export async function getProxyProviders() {
const response = await invoke<{
providers: Record<string, IProxyProviderItem>;
}>("get_providers_proxies");
if (!response || !response.providers) {
console.warn(
"getProxyProviders: Invalid response structure, returning empty object",
);
return {};
}
const providers = response.providers as Record<string, IProxyProviderItem>;
export async function calcuProxyProviders() {
const providers = await getProxyProviders();
return Object.fromEntries(
Object.entries(providers).filter(([, item]) => {
const type = item.vehicleType.toLowerCase();
return type === "http" || type === "file";
}),
Object.entries(providers.providers)
.sort()
.filter(
([_, item]) =>
item?.vehicleType === "HTTP" || item?.vehicleType === "File",
),
);
}
export async function getRuleProviders() {
const response = await invoke<{
providers: Record<string, IRuleProviderItem>;
}>("get_rule_providers");
const providers = (response.providers || {}) as Record<
string,
IRuleProviderItem
>;
return Object.fromEntries(
Object.entries(providers).filter(([, item]) => {
const type = item.vehicleType.toLowerCase();
return type === "http" || type === "file";
}),
);
}
export async function providerHealthCheck(name: string) {
return invoke<void>("proxy_provider_health_check", { name });
}
export async function proxyProviderUpdate(name: string) {
return invoke<void>("update_proxy_provider", { name });
}
export async function ruleProviderUpdate(name: string) {
return invoke<void>("update_rule_provider", { name });
}
export async function getConnections() {
return invoke<IConnections>("get_clash_connections");
}
export async function deleteConnection(id: string) {
return invoke<void>("delete_clash_connection", { id });
}
export async function closeAllConnections() {
return invoke<void>("close_all_clash_connections");
}
export async function getGroupProxyDelays(
groupName: string,
url?: string,
timeout?: number,
) {
return invoke<Record<string, number>>("get_group_proxy_delays", {
groupName,
url,
timeout,
});
}
export async function getTrafficData() {
// console.log("[Traffic][Service] 开始调用 get_traffic_data");
const result = await invoke<ITrafficItem>("get_traffic_data");
// console.log("[Traffic][Service] get_traffic_data 返回结果:", result);
return result;
}
export async function getMemoryData() {
console.log("[Memory][Service] 开始调用 get_memory_data");
const result = await invoke<{
inuse: number;
oslimit?: number;
usage_percent?: number;
last_updated?: number;
}>("get_memory_data");
// console.debug("[Memory][Service] get_memory_data 返回结果:", result);
return result;
}
export async function getFormattedTrafficData() {
console.log("[Traffic][Service] 开始调用 get_formatted_traffic_data");
const result = await invoke<IFormattedTrafficData>(
"get_formatted_traffic_data",
);
// console.debug(
// "[Traffic][Service] get_formatted_traffic_data 返回结果:",
// result,
// );
return result;
}
export async function getFormattedMemoryData() {
console.log("[Memory][Service] 开始调用 get_formatted_memory_data");
const result = await invoke<IFormattedMemoryData>(
"get_formatted_memory_data",
);
// console.debug("[Memory][Service] get_formatted_memory_data 返回结果:", result);
return result;
}
export async function getSystemMonitorOverview() {
console.log("[Monitor][Service] 开始调用 get_system_monitor_overview");
const result = await invoke<ISystemMonitorOverview>(
"get_system_monitor_overview",
);
// console.debug(
// "[Monitor][Service] get_system_monitor_overview 返回结果:",
// result,
// );
return result;
}
// 带数据验证的安全版本
export async function getSystemMonitorOverviewSafe() {
// console.log(
// "[Monitor][Service] 开始调用安全版本 get_system_monitor_overview",
// );
try {
const result = await invoke<any>("get_system_monitor_overview");
// console.log("[Monitor][Service] 原始数据:", result);
// 导入验证器(动态导入避免循环依赖)
const { systemMonitorValidator } = await import("@/utils/data-validator");
if (systemMonitorValidator.validate(result)) {
// console.log("[Monitor][Service] 数据验证通过");
return result as ISystemMonitorOverview;
} else {
// console.warn("[Monitor][Service] 数据验证失败,使用清理后的数据");
return systemMonitorValidator.sanitize(result);
}
} catch {
// console.error("[Monitor][Service] API调用失败:", error);
// 返回安全的默认值
const { systemMonitorValidator } = await import("@/utils/data-validator");
return systemMonitorValidator.sanitize(null);
}
}
export async function startTrafficService() {
console.log("[Traffic][Service] 开始调用 start_traffic_service");
try {
const result = await invoke<void>("start_traffic_service");
console.log("[Traffic][Service] start_traffic_service 调用成功");
return result;
} catch (error) {
console.error("[Traffic][Service] start_traffic_service 调用失败:", error);
throw error;
}
}
export async function stopTrafficService() {
console.log("[Traffic][Service] 开始调用 stop_traffic_service");
const result = await invoke<void>("stop_traffic_service");
console.log("[Traffic][Service] stop_traffic_service 调用成功");
return result;
}
export async function isDebugEnabled() {
return invoke<boolean>("is_clash_debug_enabled");
}
export async function gc() {
return invoke<void>("clash_gc");
}
export async function getClashLogs() {
return invoke<any>("get_clash_logs");
}
const regex = /time="(.+?)"\s+level=(.+?)\s+msg="(.+?)"/;
const newRegex = /(.+?)\s+(.+?)\s+(.+)/;
const logs = await invoke<string[]>("get_clash_logs");
export async function startLogsMonitoring(level?: string) {
return invoke<void>("start_logs_monitoring", { level });
}
return logs.reduce<ILogItem[]>((acc, log) => {
const result = log.match(regex);
if (result) {
const [_, _time, type, payload] = result;
const time = dayjs(_time).format("MM-DD HH:mm:ss");
acc.push({ time, type, payload });
return acc;
}
export async function stopLogsMonitoring() {
return invoke<void>("stop_logs_monitoring");
const result2 = log.match(newRegex);
if (result2) {
const [_, time, type, payload] = result2;
acc.push({ time, type, payload });
}
return acc;
}, []);
}
export async function clearLogs() {
@@ -576,16 +371,6 @@ export async function cmdGetProxyDelay(
}
}
/// 用于profile切换等场景
export async function forceRefreshProxies() {
const start = Date.now();
console.log("[API] 强制刷新代理缓存");
const result = await invoke<any>("force_refresh_proxies");
const duration = Date.now() - start;
console.log(`[API] 代理缓存刷新完成,耗时: ${duration}ms`);
return result;
}
export async function cmdTestDelay(url: string) {
return invoke<number>("test_delay", { url });
}