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:
@@ -1,12 +1,11 @@
|
||||
import { useLockFn } from "ahooks";
|
||||
import useSWR, { mutate } from "swr";
|
||||
import { getVersion } from "tauri-plugin-mihomo-api";
|
||||
|
||||
import { getVersion } from "@/services/cmds";
|
||||
import {
|
||||
getClashInfo,
|
||||
patchClashConfig,
|
||||
getRuntimeConfig,
|
||||
forceRefreshClashConfig,
|
||||
} from "@/services/cmds";
|
||||
|
||||
export const useClash = () => {
|
||||
@@ -25,11 +24,9 @@ export const useClash = () => {
|
||||
mutateClash();
|
||||
});
|
||||
|
||||
const version = versionData?.premium
|
||||
? `${versionData.version} Premium`
|
||||
: versionData?.meta
|
||||
? `${versionData.version} Mihomo`
|
||||
: versionData?.version || "-";
|
||||
const version = versionData?.meta
|
||||
? `${versionData.version} Mihomo`
|
||||
: versionData?.version || "-";
|
||||
|
||||
return {
|
||||
clash,
|
||||
@@ -123,10 +120,7 @@ export const useClashInfo = () => {
|
||||
|
||||
await patchClashConfig(patch);
|
||||
mutateInfo();
|
||||
// 配置修改后强制刷新缓存
|
||||
await forceRefreshClashConfig();
|
||||
mutate("getClashConfig");
|
||||
// IPC调用不需要刷新axios实例
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
108
src/hooks/use-connection-data.ts
Normal file
108
src/hooks/use-connection-data.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { useLocalStorage } from "foxact/use-local-storage";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { mutate } from "swr";
|
||||
import useSWRSubscription from "swr/subscription";
|
||||
import { MihomoWebSocket } from "tauri-plugin-mihomo-api";
|
||||
|
||||
export const initConnData: IConnections = {
|
||||
uploadTotal: 0,
|
||||
downloadTotal: 0,
|
||||
connections: [],
|
||||
};
|
||||
|
||||
export const useConnectionData = () => {
|
||||
const [date, setDate] = useLocalStorage("mihomo_connection_date", Date.now());
|
||||
const subscriptKey = `getClashConnection-${date}`;
|
||||
|
||||
const ws = useRef<MihomoWebSocket | null>(null);
|
||||
const wsFirstConnection = useRef<boolean>(true);
|
||||
const timeoutRef = useRef<ReturnType<typeof setTimeout>>(null);
|
||||
|
||||
const response = useSWRSubscription<IConnections, any, string | null>(
|
||||
subscriptKey,
|
||||
(_key, { next }) => {
|
||||
const reconnect = async () => {
|
||||
await ws.current?.close();
|
||||
ws.current = null;
|
||||
timeoutRef.current = setTimeout(async () => await connect(), 500);
|
||||
};
|
||||
|
||||
const connect = () =>
|
||||
MihomoWebSocket.connect_connections()
|
||||
.then((ws_) => {
|
||||
ws.current = ws_;
|
||||
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
||||
|
||||
ws_.addListener(async (msg) => {
|
||||
if (msg.type === "Text") {
|
||||
if (msg.data.startsWith("Websocket error")) {
|
||||
next(msg.data);
|
||||
await reconnect();
|
||||
} else {
|
||||
const data = JSON.parse(msg.data) as IConnections;
|
||||
next(null, (old = initConnData) => {
|
||||
const oldConn = old.connections;
|
||||
const maxLen = data.connections?.length;
|
||||
const connections: IConnectionsItem[] = [];
|
||||
const rest = (data.connections || []).filter((each) => {
|
||||
const index = oldConn.findIndex((o) => o.id === each.id);
|
||||
if (index >= 0 && index < maxLen) {
|
||||
const old = oldConn[index];
|
||||
each.curUpload = each.upload - old.upload;
|
||||
each.curDownload = each.download - old.download;
|
||||
connections[index] = each;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
for (let i = 0; i < maxLen; ++i) {
|
||||
if (!connections[i] && rest.length > 0) {
|
||||
connections[i] = rest.shift()!;
|
||||
connections[i].curUpload = 0;
|
||||
connections[i].curDownload = 0;
|
||||
}
|
||||
}
|
||||
return { ...data, connections };
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((_) => {
|
||||
if (!ws.current) {
|
||||
timeoutRef.current = setTimeout(async () => await connect(), 500);
|
||||
}
|
||||
});
|
||||
|
||||
if (
|
||||
wsFirstConnection.current ||
|
||||
(ws.current && !wsFirstConnection.current)
|
||||
) {
|
||||
wsFirstConnection.current = false;
|
||||
if (ws.current) {
|
||||
ws.current.close();
|
||||
ws.current = null;
|
||||
}
|
||||
connect();
|
||||
}
|
||||
|
||||
return () => {
|
||||
ws.current?.close();
|
||||
};
|
||||
},
|
||||
{
|
||||
fallbackData: initConnData,
|
||||
keepPreviousData: true,
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
mutate(`$sub$${subscriptKey}`);
|
||||
}, [date, subscriptKey]);
|
||||
|
||||
const refreshGetClashConnection = () => {
|
||||
setDate(Date.now());
|
||||
};
|
||||
|
||||
return { response, refreshGetClashConnection };
|
||||
};
|
||||
151
src/hooks/use-log-data-new.ts
Normal file
151
src/hooks/use-log-data-new.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
import dayjs from "dayjs";
|
||||
import { useLocalStorage } from "foxact/use-local-storage";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { mutate } from "swr";
|
||||
import useSWRSubscription from "swr/subscription";
|
||||
import { MihomoWebSocket } from "tauri-plugin-mihomo-api";
|
||||
|
||||
import { getClashLogs } from "@/services/cmds";
|
||||
import { useClashLog } from "@/services/states";
|
||||
|
||||
const MAX_LOG_NUM = 1000;
|
||||
|
||||
export const useLogData = () => {
|
||||
const [clashLog] = useClashLog();
|
||||
const enableLog = clashLog.enable;
|
||||
const logLevel = clashLog.logLevel;
|
||||
|
||||
const [date, setDate] = useLocalStorage("mihomo_logs_date", Date.now());
|
||||
const subscriptKey = enableLog ? `getClashLog-${date}` : null;
|
||||
|
||||
const ws = useRef<MihomoWebSocket | null>(null);
|
||||
const wsFirstConnection = useRef<boolean>(true);
|
||||
const timeoutRef = useRef<ReturnType<typeof setTimeout>>(null);
|
||||
|
||||
const response = useSWRSubscription<ILogItem[], any, string | null>(
|
||||
subscriptKey,
|
||||
(_key, { next }) => {
|
||||
const reconnect = async () => {
|
||||
await ws.current?.close();
|
||||
ws.current = null;
|
||||
timeoutRef.current = setTimeout(async () => await connect(), 500);
|
||||
};
|
||||
|
||||
const connect = () =>
|
||||
MihomoWebSocket.connect_logs(logLevel)
|
||||
.then(async (ws_) => {
|
||||
ws.current = ws_;
|
||||
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
||||
|
||||
const logs = await getClashLogs();
|
||||
let filterLogs: ILogItem[] = [];
|
||||
switch (logLevel) {
|
||||
case "debug":
|
||||
filterLogs = logs.filter((i) =>
|
||||
["debug", "info", "warning", "error"].includes(i.type),
|
||||
);
|
||||
break;
|
||||
case "info":
|
||||
filterLogs = logs.filter((i) =>
|
||||
["info", "warning", "error"].includes(i.type),
|
||||
);
|
||||
break;
|
||||
case "warning":
|
||||
filterLogs = logs.filter((i) =>
|
||||
["warning", "error"].includes(i.type),
|
||||
);
|
||||
break;
|
||||
case "error":
|
||||
filterLogs = logs.filter((i) => i.type === "error");
|
||||
break;
|
||||
case "silent":
|
||||
filterLogs = [];
|
||||
break;
|
||||
default:
|
||||
filterLogs = logs;
|
||||
break;
|
||||
}
|
||||
next(null, filterLogs);
|
||||
|
||||
const buffer: ILogItem[] = [];
|
||||
let flushTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
const flush = () => {
|
||||
if (buffer.length > 0) {
|
||||
next(null, (l) => {
|
||||
let newList = [...(l ?? []), ...buffer.splice(0)];
|
||||
if (newList.length > MAX_LOG_NUM) {
|
||||
newList = newList.slice(
|
||||
-Math.min(MAX_LOG_NUM, newList.length),
|
||||
);
|
||||
}
|
||||
return newList;
|
||||
});
|
||||
}
|
||||
flushTimer = null;
|
||||
};
|
||||
ws_.addListener(async (msg) => {
|
||||
if (msg.type === "Text") {
|
||||
if (msg.data.startsWith("Websocket error")) {
|
||||
next(msg.data);
|
||||
await reconnect();
|
||||
} else {
|
||||
const data = JSON.parse(msg.data) as ILogItem;
|
||||
data.time = dayjs().format("MM-DD HH:mm:ss");
|
||||
buffer.push(data);
|
||||
|
||||
// flush data
|
||||
if (!flushTimer) {
|
||||
flushTimer = setTimeout(flush, 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((_) => {
|
||||
if (!ws.current) {
|
||||
timeoutRef.current = setTimeout(async () => await connect(), 500);
|
||||
}
|
||||
});
|
||||
|
||||
if (
|
||||
wsFirstConnection.current ||
|
||||
(ws.current && !wsFirstConnection.current)
|
||||
) {
|
||||
wsFirstConnection.current = false;
|
||||
if (ws.current) {
|
||||
ws.current.close();
|
||||
ws.current = null;
|
||||
}
|
||||
connect();
|
||||
}
|
||||
|
||||
return () => {
|
||||
ws.current?.close();
|
||||
};
|
||||
},
|
||||
{
|
||||
fallbackData: [],
|
||||
keepPreviousData: true,
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
mutate(`$sub$${subscriptKey}`);
|
||||
}, [date, subscriptKey]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!logLevel) return;
|
||||
ws.current?.close();
|
||||
setDate(Date.now());
|
||||
}, [logLevel]);
|
||||
|
||||
const refreshGetClashLog = (clear = false) => {
|
||||
if (clear) {
|
||||
mutate(`$sub$${subscriptKey}`, []);
|
||||
} else {
|
||||
setDate(Date.now());
|
||||
}
|
||||
};
|
||||
|
||||
return { response, refreshGetClashLog };
|
||||
};
|
||||
@@ -1,11 +1,11 @@
|
||||
import {
|
||||
useGlobalLogData,
|
||||
clearGlobalLogs,
|
||||
LogLevel,
|
||||
// LogLevel,
|
||||
} from "@/services/global-log-service";
|
||||
|
||||
// 为了向后兼容,导出相同的类型
|
||||
export type { LogLevel };
|
||||
// export type { LogLevel };
|
||||
|
||||
export const useLogData = useGlobalLogData;
|
||||
|
||||
|
||||
84
src/hooks/use-memory-data.ts
Normal file
84
src/hooks/use-memory-data.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { useLocalStorage } from "foxact/use-local-storage";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { mutate } from "swr";
|
||||
import useSWRSubscription from "swr/subscription";
|
||||
import { MihomoWebSocket } from "tauri-plugin-mihomo-api";
|
||||
|
||||
export interface IMemoryUsageItem {
|
||||
inuse: number;
|
||||
oslimit?: number;
|
||||
}
|
||||
|
||||
export const useMemoryData = () => {
|
||||
const [date, setDate] = useLocalStorage("mihomo_memory_date", Date.now());
|
||||
const subscriptKey = `getClashMemory-${date}`;
|
||||
|
||||
const ws = useRef<MihomoWebSocket | null>(null);
|
||||
const wsFirstConnection = useRef<boolean>(true);
|
||||
const timeoutRef = useRef<ReturnType<typeof setTimeout>>(null);
|
||||
|
||||
const response = useSWRSubscription<IMemoryUsageItem, any, string | null>(
|
||||
subscriptKey,
|
||||
(_key, { next }) => {
|
||||
const reconnect = async () => {
|
||||
await ws.current?.close();
|
||||
ws.current = null;
|
||||
timeoutRef.current = setTimeout(async () => await connect(), 500);
|
||||
};
|
||||
|
||||
const connect = () =>
|
||||
MihomoWebSocket.connect_memory()
|
||||
.then((ws_) => {
|
||||
ws.current = ws_;
|
||||
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
||||
|
||||
ws_.addListener(async (msg) => {
|
||||
if (msg.type === "Text") {
|
||||
if (msg.data.startsWith("Websocket error")) {
|
||||
next(msg.data, { inuse: 0 });
|
||||
await reconnect();
|
||||
} else {
|
||||
const data = JSON.parse(msg.data) as IMemoryUsageItem;
|
||||
next(null, data);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((_) => {
|
||||
if (!ws.current) {
|
||||
timeoutRef.current = setTimeout(async () => await connect(), 500);
|
||||
}
|
||||
});
|
||||
|
||||
if (
|
||||
wsFirstConnection.current ||
|
||||
(ws.current && !wsFirstConnection.current)
|
||||
) {
|
||||
wsFirstConnection.current = false;
|
||||
if (ws.current) {
|
||||
ws.current.close();
|
||||
ws.current = null;
|
||||
}
|
||||
connect();
|
||||
}
|
||||
|
||||
return () => {
|
||||
ws.current?.close();
|
||||
};
|
||||
},
|
||||
{
|
||||
fallbackData: { inuse: 0 },
|
||||
keepPreviousData: true,
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
mutate(`$sub$${subscriptKey}`);
|
||||
}, [date, subscriptKey]);
|
||||
|
||||
const refreshGetClashMemory = () => {
|
||||
setDate(Date.now());
|
||||
};
|
||||
|
||||
return { response, refreshGetClashMemory };
|
||||
};
|
||||
@@ -1,12 +1,12 @@
|
||||
import useSWR, { mutate } from "swr";
|
||||
import { selectNodeForGroup } from "tauri-plugin-mihomo-api";
|
||||
|
||||
import {
|
||||
getProfiles,
|
||||
patchProfile,
|
||||
patchProfilesConfig,
|
||||
forceRefreshProxies,
|
||||
} from "@/services/cmds";
|
||||
import { getProxies, updateProxy } from "@/services/cmds";
|
||||
import { calcuProxies } from "@/services/cmds";
|
||||
|
||||
export const useProfiles = () => {
|
||||
const {
|
||||
@@ -72,7 +72,7 @@ export const useProfiles = () => {
|
||||
console.log("[ActivateSelected] 开始处理代理选择");
|
||||
|
||||
const [proxiesData, profileData] = await Promise.all([
|
||||
getProxies(),
|
||||
calcuProxies(),
|
||||
getProfiles(),
|
||||
]);
|
||||
|
||||
@@ -124,7 +124,7 @@ export const useProfiles = () => {
|
||||
`[ActivateSelected] 需要切换代理组 ${name}: ${now} -> ${targetProxy}`,
|
||||
);
|
||||
hasChange = true;
|
||||
updateProxy(name, targetProxy);
|
||||
selectNodeForGroup(name, targetProxy);
|
||||
}
|
||||
|
||||
newSelected.push({ name, now: targetProxy || now });
|
||||
@@ -141,11 +141,8 @@ export const useProfiles = () => {
|
||||
await patchProfile(profileData.current!, { selected: newSelected });
|
||||
console.log("[ActivateSelected] 代理选择配置保存成功");
|
||||
|
||||
// 切换节点后强制刷新后端缓存
|
||||
await forceRefreshProxies();
|
||||
|
||||
setTimeout(() => {
|
||||
mutate("getProxies", getProxies());
|
||||
mutate("getProxies", calcuProxies());
|
||||
}, 100);
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import { useLockFn } from "ahooks";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import {
|
||||
closeConnections,
|
||||
getConnections,
|
||||
selectNodeForGroup,
|
||||
} from "tauri-plugin-mihomo-api";
|
||||
|
||||
import { useProfiles } from "@/hooks/use-profiles";
|
||||
import { useVerge } from "@/hooks/use-verge";
|
||||
import {
|
||||
updateProxy,
|
||||
updateProxyAndSync,
|
||||
forceRefreshProxies,
|
||||
syncTrayProxySelection,
|
||||
getConnections,
|
||||
deleteConnection,
|
||||
} from "@/services/cmds";
|
||||
import { syncTrayProxySelection } from "@/services/cmds";
|
||||
|
||||
// 缓存连接清理
|
||||
const cleanupConnections = async (previousProxy: string) => {
|
||||
try {
|
||||
const { connections } = await getConnections();
|
||||
const cleanupPromises = connections
|
||||
const cleanupPromises = (connections ?? [])
|
||||
.filter((conn) => conn.chains.includes(previousProxy))
|
||||
.map((conn) => deleteConnection(conn.id));
|
||||
.map((conn) => closeConnections(conn.id));
|
||||
|
||||
if (cleanupPromises.length > 0) {
|
||||
await Promise.allSettled(cleanupPromises);
|
||||
@@ -77,7 +75,8 @@ export const useProxySelection = (options: ProxySelectionOptions = {}) => {
|
||||
await patchCurrent({ selected: current.selected });
|
||||
}
|
||||
|
||||
await updateProxyAndSync(groupName, proxyName);
|
||||
await selectNodeForGroup(groupName, proxyName);
|
||||
await syncTrayProxySelection();
|
||||
console.log(
|
||||
`[ProxySelection] 代理和状态同步完成: ${groupName} -> ${proxyName}`,
|
||||
);
|
||||
@@ -98,8 +97,7 @@ export const useProxySelection = (options: ProxySelectionOptions = {}) => {
|
||||
);
|
||||
|
||||
try {
|
||||
await updateProxy(groupName, proxyName);
|
||||
await forceRefreshProxies();
|
||||
await selectNodeForGroup(groupName, proxyName);
|
||||
await syncTrayProxySelection();
|
||||
onSuccess?.();
|
||||
console.log(
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import useSWR, { mutate } from "swr";
|
||||
import { closeAllConnections } from "tauri-plugin-mihomo-api";
|
||||
|
||||
import { useVerge } from "@/hooks/use-verge";
|
||||
import { useAppData } from "@/providers/app-data-context";
|
||||
import { closeAllConnections, getAutotemProxy } from "@/services/cmds";
|
||||
import { getAutotemProxy } from "@/services/cmds";
|
||||
|
||||
// 系统代理状态检测统一逻辑
|
||||
export const useSystemProxyState = () => {
|
||||
|
||||
84
src/hooks/use-traffic-data.ts
Normal file
84
src/hooks/use-traffic-data.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { useLocalStorage } from "foxact/use-local-storage";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { mutate } from "swr";
|
||||
import useSWRSubscription from "swr/subscription";
|
||||
import { MihomoWebSocket } from "tauri-plugin-mihomo-api";
|
||||
|
||||
import { TrafficRef } from "@/components/layout/traffic-graph";
|
||||
|
||||
export const useTrafficData = () => {
|
||||
const [date, setDate] = useLocalStorage("mihomo_traffic_date", Date.now());
|
||||
const subscriptKey = `getClashTraffic-${date}`;
|
||||
|
||||
const trafficRef = useRef<TrafficRef>(null);
|
||||
const ws = useRef<MihomoWebSocket | null>(null);
|
||||
const wsFirstConnection = useRef<boolean>(true);
|
||||
const timeoutRef = useRef<ReturnType<typeof setTimeout>>(null);
|
||||
|
||||
const response = useSWRSubscription<ITrafficItem, any, string | null>(
|
||||
subscriptKey,
|
||||
(_key, { next }) => {
|
||||
const reconnect = async () => {
|
||||
await ws.current?.close();
|
||||
ws.current = null;
|
||||
timeoutRef.current = setTimeout(async () => await connect(), 500);
|
||||
};
|
||||
|
||||
const connect = async () => {
|
||||
MihomoWebSocket.connect_traffic()
|
||||
.then(async (ws_) => {
|
||||
ws.current = ws_;
|
||||
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
||||
|
||||
ws_.addListener(async (msg) => {
|
||||
if (msg.type === "Text") {
|
||||
if (msg.data.startsWith("Websocket error")) {
|
||||
next(msg.data, { up: 0, down: 0 });
|
||||
await reconnect();
|
||||
} else {
|
||||
const data = JSON.parse(msg.data) as ITrafficItem;
|
||||
trafficRef.current?.appendData(data);
|
||||
next(null, data);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((_) => {
|
||||
if (!ws.current) {
|
||||
timeoutRef.current = setTimeout(async () => await connect(), 500);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (
|
||||
wsFirstConnection.current ||
|
||||
(ws.current && !wsFirstConnection.current)
|
||||
) {
|
||||
wsFirstConnection.current = false;
|
||||
if (ws.current) {
|
||||
ws.current.close();
|
||||
ws.current = null;
|
||||
}
|
||||
connect();
|
||||
}
|
||||
|
||||
return () => {
|
||||
ws.current?.close();
|
||||
};
|
||||
},
|
||||
{
|
||||
fallbackData: { up: 0, down: 0 },
|
||||
keepPreviousData: true,
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
mutate(`$sub$${subscriptKey}`);
|
||||
}, [date, subscriptKey]);
|
||||
|
||||
const refreshGetClashTraffic = () => {
|
||||
setDate(Date.now());
|
||||
};
|
||||
|
||||
return { response, refreshGetClashTraffic };
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
import useSWR from "swr";
|
||||
|
||||
import { useClashInfo } from "@/hooks/use-clash";
|
||||
import { useVisibility } from "@/hooks/use-visibility";
|
||||
import { getSystemMonitorOverviewSafe } from "@/services/cmds";
|
||||
// import { useClashInfo } from "@/hooks/use-clash";
|
||||
// import { useVisibility } from "@/hooks/use-visibility";
|
||||
|
||||
import { useTrafficData } from "./use-traffic-data";
|
||||
|
||||
// 增强的流量数据点接口
|
||||
export interface ITrafficDataPoint {
|
||||
@@ -175,14 +175,17 @@ class TrafficDataSampler {
|
||||
// 全局单例
|
||||
const refCounter = new ReferenceCounter();
|
||||
let globalSampler: TrafficDataSampler | null = null;
|
||||
let lastValidData: ISystemMonitorOverview | null = null;
|
||||
// let lastValidData: ISystemMonitorOverview | null = null;
|
||||
|
||||
/**
|
||||
* 增强的流量监控Hook - 支持数据压缩、采样和引用计数
|
||||
*/
|
||||
export const useTrafficMonitorEnhanced = () => {
|
||||
const { clashInfo } = useClashInfo();
|
||||
const pageVisible = useVisibility();
|
||||
// const { clashInfo } = useClashInfo();
|
||||
// const pageVisible = useVisibility();
|
||||
const {
|
||||
response: { data: traffic },
|
||||
} = useTrafficData();
|
||||
|
||||
// 初始化采样器
|
||||
if (!globalSampler) {
|
||||
@@ -230,69 +233,87 @@ export const useTrafficMonitorEnhanced = () => {
|
||||
refCounter.onCountChange(handleCountChange);
|
||||
}, []);
|
||||
|
||||
// 只有在有引用时才启用SWR
|
||||
const shouldFetch = clashInfo && pageVisible && refCounter.getCount() > 0;
|
||||
// const monitorData = useRef<ISystemMonitorOverview | null>(null);
|
||||
useEffect(() => {
|
||||
if (globalSampler) {
|
||||
// 添加到采样器
|
||||
const timestamp = Date.now();
|
||||
const dataPoint: ITrafficDataPoint = {
|
||||
up: traffic?.up || 0,
|
||||
down: traffic?.down || 0,
|
||||
timestamp,
|
||||
name: new Date(timestamp).toLocaleTimeString("en-US", {
|
||||
hour12: false,
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
}),
|
||||
};
|
||||
globalSampler.addDataPoint(dataPoint);
|
||||
triggerUpdate();
|
||||
}
|
||||
}, [traffic, triggerUpdate]);
|
||||
|
||||
const { data: monitorData, error } = useSWR<ISystemMonitorOverview>(
|
||||
shouldFetch ? "getSystemMonitorOverviewSafe" : null,
|
||||
getSystemMonitorOverviewSafe,
|
||||
{
|
||||
refreshInterval: shouldFetch ? 1000 : 0, // 只有在需要时才刷新
|
||||
keepPreviousData: true,
|
||||
onSuccess: (data) => {
|
||||
// console.log("[TrafficMonitorEnhanced] 获取到监控数据:", data);
|
||||
// const { data: monitorData, error } = useSWR<ISystemMonitorOverview>(
|
||||
// shouldFetch ? "getSystemMonitorOverviewSafe" : null,
|
||||
// getSystemMonitorOverviewSafe,
|
||||
// {
|
||||
// refreshInterval: shouldFetch ? 1000 : 0, // 只有在需要时才刷新
|
||||
// keepPreviousData: true,
|
||||
// onSuccess: (data) => {
|
||||
// // console.log("[TrafficMonitorEnhanced] 获取到监控数据:", data);
|
||||
|
||||
if (data?.traffic?.raw && globalSampler) {
|
||||
// 保存最后有效数据
|
||||
lastValidData = data;
|
||||
// if (data?.traffic?.raw && globalSampler) {
|
||||
// // 保存最后有效数据
|
||||
// lastValidData = data;
|
||||
|
||||
// 添加到采样器
|
||||
const timestamp = Date.now();
|
||||
const dataPoint: ITrafficDataPoint = {
|
||||
up: data.traffic.raw.up_rate || 0,
|
||||
down: data.traffic.raw.down_rate || 0,
|
||||
timestamp,
|
||||
name: new Date(timestamp).toLocaleTimeString("en-US", {
|
||||
hour12: false,
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
}),
|
||||
};
|
||||
// // 添加到采样器
|
||||
// const timestamp = Date.now();
|
||||
// const dataPoint: ITrafficDataPoint = {
|
||||
// up: data.traffic.raw.up_rate || 0,
|
||||
// down: data.traffic.raw.down_rate || 0,
|
||||
// timestamp,
|
||||
// name: new Date(timestamp).toLocaleTimeString("en-US", {
|
||||
// hour12: false,
|
||||
// hour: "2-digit",
|
||||
// minute: "2-digit",
|
||||
// second: "2-digit",
|
||||
// }),
|
||||
// };
|
||||
|
||||
globalSampler.addDataPoint(dataPoint);
|
||||
triggerUpdate();
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error(
|
||||
"[TrafficMonitorEnhanced] 网络错误,使用最后有效数据. 错误详情:",
|
||||
{
|
||||
message: error?.message || "未知错误",
|
||||
stack: error?.stack || "无堆栈信息",
|
||||
},
|
||||
);
|
||||
// 网络错误时不清空数据,继续使用最后有效值
|
||||
// 但是添加一个错误标记的数据点(流量为0)
|
||||
if (globalSampler) {
|
||||
const timestamp = Date.now();
|
||||
const errorPoint: ITrafficDataPoint = {
|
||||
up: 0,
|
||||
down: 0,
|
||||
timestamp,
|
||||
name: new Date(timestamp).toLocaleTimeString("en-US", {
|
||||
hour12: false,
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
}),
|
||||
};
|
||||
globalSampler.addDataPoint(errorPoint);
|
||||
triggerUpdate();
|
||||
}
|
||||
},
|
||||
},
|
||||
);
|
||||
// globalSampler.addDataPoint(dataPoint);
|
||||
// triggerUpdate();
|
||||
// }
|
||||
// },
|
||||
// onError: (error) => {
|
||||
// console.error(
|
||||
// "[TrafficMonitorEnhanced] 网络错误,使用最后有效数据. 错误详情:",
|
||||
// {
|
||||
// message: error?.message || "未知错误",
|
||||
// stack: error?.stack || "无堆栈信息",
|
||||
// },
|
||||
// );
|
||||
// // 网络错误时不清空数据,继续使用最后有效值
|
||||
// // 但是添加一个错误标记的数据点(流量为0)
|
||||
// if (globalSampler) {
|
||||
// const timestamp = Date.now();
|
||||
// const errorPoint: ITrafficDataPoint = {
|
||||
// up: 0,
|
||||
// down: 0,
|
||||
// timestamp,
|
||||
// name: new Date(timestamp).toLocaleTimeString("en-US", {
|
||||
// hour12: false,
|
||||
// hour: "2-digit",
|
||||
// minute: "2-digit",
|
||||
// second: "2-digit",
|
||||
// }),
|
||||
// };
|
||||
// globalSampler.addDataPoint(errorPoint);
|
||||
// triggerUpdate();
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
// );
|
||||
|
||||
// 获取指定时间范围的数据
|
||||
const getDataForTimeRange = useCallback(
|
||||
@@ -324,28 +345,28 @@ export const useTrafficMonitorEnhanced = () => {
|
||||
}, []);
|
||||
|
||||
// 构建返回的监控数据,优先使用当前数据,fallback到最后有效数据
|
||||
const currentData = monitorData || lastValidData;
|
||||
const trafficMonitorData = {
|
||||
traffic: currentData?.traffic || {
|
||||
raw: { up: 0, down: 0, up_rate: 0, down_rate: 0 },
|
||||
formatted: {
|
||||
up_rate: "0B",
|
||||
down_rate: "0B",
|
||||
total_up: "0B",
|
||||
total_down: "0B",
|
||||
},
|
||||
is_fresh: false,
|
||||
},
|
||||
memory: currentData?.memory || {
|
||||
raw: { inuse: 0, oslimit: 0, usage_percent: 0 },
|
||||
formatted: { inuse: "0B", oslimit: "0B", usage_percent: 0 },
|
||||
is_fresh: false,
|
||||
},
|
||||
};
|
||||
// const currentData = monitorData.current || lastValidData;
|
||||
// const trafficMonitorData = {
|
||||
// traffic: currentData?.traffic || {
|
||||
// raw: { up: 0, down: 0, up_rate: 0, down_rate: 0 },
|
||||
// formatted: {
|
||||
// up_rate: "0B",
|
||||
// down_rate: "0B",
|
||||
// total_up: "0B",
|
||||
// total_down: "0B",
|
||||
// },
|
||||
// is_fresh: false,
|
||||
// },
|
||||
// memory: currentData?.memory || {
|
||||
// raw: { inuse: 0, oslimit: 0, usage_percent: 0 },
|
||||
// formatted: { inuse: "0B", oslimit: "0B", usage_percent: 0 },
|
||||
// is_fresh: false,
|
||||
// },
|
||||
// };
|
||||
|
||||
return {
|
||||
// 监控数据
|
||||
monitorData: trafficMonitorData,
|
||||
// monitorData: trafficMonitorData,
|
||||
|
||||
// 图表数据管理
|
||||
graphData: {
|
||||
@@ -355,10 +376,9 @@ export const useTrafficMonitorEnhanced = () => {
|
||||
},
|
||||
|
||||
// 状态信息
|
||||
isLoading: !currentData && !error,
|
||||
error,
|
||||
isDataFresh: currentData?.traffic?.is_fresh || false,
|
||||
hasValidData: !!lastValidData,
|
||||
// isLoading: !currentData,
|
||||
// isDataFresh: currentData?.traffic?.is_fresh || false,
|
||||
// hasValidData: !!lastValidData,
|
||||
|
||||
// 性能统计
|
||||
samplerStats: getSamplerStats(),
|
||||
@@ -369,30 +389,28 @@ export const useTrafficMonitorEnhanced = () => {
|
||||
/**
|
||||
* 轻量级流量数据Hook
|
||||
*/
|
||||
export const useTrafficDataEnhanced = () => {
|
||||
const { monitorData, isLoading, error, isDataFresh, hasValidData } =
|
||||
useTrafficMonitorEnhanced();
|
||||
// export const useTrafficDataEnhanced = () => {
|
||||
// const { monitorData, isLoading, isDataFresh, hasValidData } =
|
||||
// useTrafficMonitorEnhanced();
|
||||
|
||||
return {
|
||||
traffic: monitorData.traffic,
|
||||
memory: monitorData.memory,
|
||||
isLoading,
|
||||
error,
|
||||
isDataFresh,
|
||||
hasValidData,
|
||||
};
|
||||
};
|
||||
// return {
|
||||
// traffic: monitorData.traffic,
|
||||
// memory: monitorData.memory,
|
||||
// isLoading,
|
||||
// isDataFresh,
|
||||
// hasValidData,
|
||||
// };
|
||||
// };
|
||||
|
||||
/**
|
||||
* 图表数据Hook
|
||||
*/
|
||||
export const useTrafficGraphDataEnhanced = () => {
|
||||
const { graphData, isDataFresh, samplerStats, referenceCount } =
|
||||
const { graphData, samplerStats, referenceCount } =
|
||||
useTrafficMonitorEnhanced();
|
||||
|
||||
return {
|
||||
...graphData,
|
||||
isDataFresh,
|
||||
samplerStats,
|
||||
referenceCount,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user