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,26 +1,33 @@
|
||||
import { List, Paper, ThemeProvider, SvgIcon } from "@mui/material";
|
||||
import { List, Paper, SvgIcon, ThemeProvider } from "@mui/material";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||
import dayjs from "dayjs";
|
||||
import relativeTime from "dayjs/plugin/relativeTime";
|
||||
import { useLocalStorage } from "foxact/use-local-storage";
|
||||
import { useEffect, useCallback, useState, useRef } from "react";
|
||||
import React from "react";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useLocation, useRoutes, useNavigate } from "react-router-dom";
|
||||
import { useLocation, useNavigate, useRoutes } from "react-router-dom";
|
||||
import { SWRConfig, mutate } from "swr";
|
||||
|
||||
import iconDark from "@/assets/image/icon_dark.svg?react";
|
||||
import iconLight from "@/assets/image/icon_light.svg?react";
|
||||
import LogoSvg from "@/assets/image/logo.svg?react";
|
||||
import { NoticeManager } from "@/components/base/NoticeManager";
|
||||
import { LayoutItem } from "@/components/layout/layout-item";
|
||||
import { LayoutTraffic } from "@/components/layout/layout-traffic";
|
||||
import { UpdateButton } from "@/components/layout/update-button";
|
||||
import { useCustomTheme } from "@/components/layout/use-custom-theme";
|
||||
import { useClashInfo } from "@/hooks/use-clash";
|
||||
import { useConnectionData } from "@/hooks/use-connection-data";
|
||||
import { useI18n } from "@/hooks/use-i18n";
|
||||
import { useListen } from "@/hooks/use-listen";
|
||||
import { useLogData } from "@/hooks/use-log-data-new";
|
||||
import { useMemoryData } from "@/hooks/use-memory-data";
|
||||
import { useTrafficData } from "@/hooks/use-traffic-data";
|
||||
import { useVerge } from "@/hooks/use-verge";
|
||||
import { getAxios } from "@/services/api";
|
||||
import { forceRefreshClashConfig } from "@/services/cmds";
|
||||
import { useThemeMode, useEnableLog } from "@/services/states";
|
||||
import { showNotice } from "@/services/noticeService";
|
||||
import { useClashLog, useThemeMode } from "@/services/states";
|
||||
import getSystem from "@/utils/get-system";
|
||||
|
||||
import { routers } from "./_routers";
|
||||
@@ -28,19 +35,6 @@ import { routers } from "./_routers";
|
||||
import "dayjs/locale/ru";
|
||||
import "dayjs/locale/zh-cn";
|
||||
|
||||
import { useListen } from "@/hooks/use-listen";
|
||||
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
|
||||
import { useClashInfo } from "@/hooks/use-clash";
|
||||
import { initGlobalLogService } from "@/services/global-log-service";
|
||||
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
import { showNotice } from "@/services/noticeService";
|
||||
import { NoticeManager } from "@/components/base/NoticeManager";
|
||||
import { LogLevel } from "@/hooks/use-log-data";
|
||||
|
||||
const appWindow = getCurrentWebviewWindow();
|
||||
export const portableFlag = false;
|
||||
|
||||
@@ -157,14 +151,20 @@ const handleNoticeMessage = (
|
||||
};
|
||||
|
||||
const Layout = () => {
|
||||
useTrafficData();
|
||||
useMemoryData();
|
||||
useConnectionData();
|
||||
useLogData();
|
||||
const mode = useThemeMode();
|
||||
const isDark = mode === "light" ? false : true;
|
||||
const { t } = useTranslation();
|
||||
const { theme } = useCustomTheme();
|
||||
const { verge } = useVerge();
|
||||
const { clashInfo } = useClashInfo();
|
||||
const [enableLog] = useEnableLog();
|
||||
const [logLevel] = useLocalStorage<LogLevel>("log:log-level", "info");
|
||||
const [clashLog] = useClashLog();
|
||||
const enableLog = clashLog.enable;
|
||||
const logLevel = clashLog.logLevel;
|
||||
// const [logLevel] = useLocalStorage<LogLevel>("log:log-level", "info");
|
||||
const { language, start_page } = verge ?? {};
|
||||
const { switchLanguage } = useI18n();
|
||||
const navigate = useNavigate();
|
||||
@@ -193,19 +193,17 @@ const Layout = () => {
|
||||
);
|
||||
|
||||
// 初始化全局日志服务
|
||||
useEffect(() => {
|
||||
if (clashInfo) {
|
||||
initGlobalLogService(enableLog, logLevel);
|
||||
}
|
||||
}, [clashInfo, enableLog, logLevel]);
|
||||
// useEffect(() => {
|
||||
// if (clashInfo) {
|
||||
// initGlobalLogService(enableLog, logLevel);
|
||||
// }
|
||||
// }, [clashInfo, enableLog, logLevel]);
|
||||
|
||||
// 设置监听器
|
||||
useEffect(() => {
|
||||
const listeners = [
|
||||
addListener("verge://refresh-clash-config", async () => {
|
||||
await getAxios(true);
|
||||
// 后端配置变更事件触发,强制刷新配置缓存
|
||||
await forceRefreshClashConfig();
|
||||
mutate("getProxies");
|
||||
mutate("getVersion");
|
||||
mutate("getClashConfig");
|
||||
@@ -521,15 +519,16 @@ const Layout = () => {
|
||||
borderTopRightRadius: "0px",
|
||||
}}
|
||||
onContextMenu={(e) => {
|
||||
if (
|
||||
OS === "windows" &&
|
||||
!["input", "textarea"].includes(
|
||||
e.currentTarget.tagName.toLowerCase(),
|
||||
) &&
|
||||
!e.currentTarget.isContentEditable
|
||||
) {
|
||||
e.preventDefault();
|
||||
}
|
||||
// TODO: 禁止右键菜单
|
||||
// if (
|
||||
// OS === "windows" &&
|
||||
// !["input", "textarea"].includes(
|
||||
// e.currentTarget.tagName.toLowerCase(),
|
||||
// ) &&
|
||||
// !e.currentTarget.isContentEditable
|
||||
// ) {
|
||||
// e.preventDefault();
|
||||
// }
|
||||
}}
|
||||
sx={[
|
||||
({ palette }) => ({ bgcolor: palette.background.paper }),
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useLockFn } from "ahooks";
|
||||
import { useCallback, useMemo, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Virtuoso } from "react-virtuoso";
|
||||
import { closeAllConnections } from "tauri-plugin-mihomo-api";
|
||||
|
||||
import { BaseEmpty, BasePage } from "@/components/base";
|
||||
import { BaseSearchBox } from "@/components/base/base-search-box";
|
||||
@@ -19,9 +20,8 @@ import {
|
||||
} from "@/components/connection/connection-detail";
|
||||
import { ConnectionItem } from "@/components/connection/connection-item";
|
||||
import { ConnectionTable } from "@/components/connection/connection-table";
|
||||
import { useConnectionData } from "@/hooks/use-connection-data";
|
||||
import { useVisibility } from "@/hooks/use-visibility";
|
||||
import { useAppData } from "@/providers/app-data-context";
|
||||
import { closeAllConnections } from "@/services/cmds";
|
||||
import { useConnectionSetting } from "@/services/states";
|
||||
import parseTraffic from "@/utils/parse-traffic";
|
||||
|
||||
@@ -39,8 +39,9 @@ const ConnectionsPage = () => {
|
||||
const [match, setMatch] = useState(() => (_: string) => true);
|
||||
const [curOrderOpt, setOrderOpt] = useState("Default");
|
||||
|
||||
// 使用全局数据
|
||||
const { connections } = useAppData();
|
||||
const {
|
||||
response: { data: connections },
|
||||
} = useConnectionData();
|
||||
|
||||
const [setting, setSetting] = useConnectionSetting();
|
||||
|
||||
@@ -72,30 +73,30 @@ const ConnectionsPage = () => {
|
||||
if (isPaused) {
|
||||
return (
|
||||
frozenData ?? {
|
||||
uploadTotal: connections.uploadTotal,
|
||||
downloadTotal: connections.downloadTotal,
|
||||
connections: connections.data,
|
||||
uploadTotal: connections?.uploadTotal,
|
||||
downloadTotal: connections?.downloadTotal,
|
||||
connections: connections?.connections,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
uploadTotal: connections.uploadTotal,
|
||||
downloadTotal: connections.downloadTotal,
|
||||
connections: connections.data,
|
||||
uploadTotal: connections?.uploadTotal,
|
||||
downloadTotal: connections?.downloadTotal,
|
||||
connections: connections?.connections,
|
||||
};
|
||||
}, [isPaused, frozenData, connections, pageVisible]);
|
||||
|
||||
const [filterConn] = useMemo(() => {
|
||||
const orderFunc = orderOpts[curOrderOpt];
|
||||
let conns = displayData.connections.filter((conn) => {
|
||||
let conns = displayData.connections?.filter((conn) => {
|
||||
const { host, destinationIP, process } = conn.metadata;
|
||||
return (
|
||||
match(host || "") || match(destinationIP || "") || match(process || "")
|
||||
);
|
||||
});
|
||||
|
||||
if (orderFunc) conns = orderFunc(conns);
|
||||
if (orderFunc) conns = orderFunc(conns ?? []);
|
||||
|
||||
return [conns];
|
||||
}, [displayData, match, curOrderOpt, orderOpts]);
|
||||
@@ -112,9 +113,9 @@ const ConnectionsPage = () => {
|
||||
setIsPaused((prev) => {
|
||||
if (!prev) {
|
||||
setFrozenData({
|
||||
uploadTotal: connections.uploadTotal,
|
||||
downloadTotal: connections.downloadTotal,
|
||||
connections: connections.data,
|
||||
uploadTotal: connections?.uploadTotal ?? 0,
|
||||
downloadTotal: connections?.downloadTotal ?? 0,
|
||||
connections: connections?.connections ?? [],
|
||||
});
|
||||
} else {
|
||||
setFrozenData(null);
|
||||
@@ -206,7 +207,7 @@ const ConnectionsPage = () => {
|
||||
<BaseSearchBox onSearch={handleSearch} />
|
||||
</Box>
|
||||
|
||||
{filterConn.length === 0 ? (
|
||||
{!filterConn || filterConn.length === 0 ? (
|
||||
<BaseEmpty />
|
||||
) : isTableLayout ? (
|
||||
<ConnectionTable
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
PauseCircleOutlineRounded,
|
||||
} from "@mui/icons-material";
|
||||
import { Box, Button, IconButton, MenuItem } from "@mui/material";
|
||||
import { useLocalStorage } from "foxact/use-local-storage";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Virtuoso } from "react-virtuoso";
|
||||
@@ -13,27 +12,22 @@ import { BaseSearchBox } from "@/components/base/base-search-box";
|
||||
import { SearchState } from "@/components/base/base-search-box";
|
||||
import { BaseStyledSelect } from "@/components/base/base-styled-select";
|
||||
import LogItem from "@/components/log/log-item";
|
||||
import { LogLevel } from "@/hooks/use-log-data";
|
||||
import {
|
||||
useGlobalLogData,
|
||||
clearGlobalLogs,
|
||||
changeLogLevel,
|
||||
toggleLogEnabled,
|
||||
} from "@/services/global-log-service";
|
||||
import { useEnableLog } from "@/services/states";
|
||||
|
||||
// 后端通过 /logs?level={level} 进行筛选,前端不再需要手动筛选日志级别
|
||||
import { useLogData } from "@/hooks/use-log-data-new";
|
||||
import { toggleLogEnabled } from "@/services/global-log-service";
|
||||
import { LogFilter, useClashLog } from "@/services/states";
|
||||
|
||||
const LogPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const [enableLog, setEnableLog] = useEnableLog();
|
||||
const [logLevel, setLogLevel] = useLocalStorage<LogLevel>(
|
||||
"log:log-level",
|
||||
"info",
|
||||
);
|
||||
const [clashLog, setClashLog] = useClashLog();
|
||||
const enableLog = clashLog.enable;
|
||||
const logState = clashLog.logFilter;
|
||||
|
||||
const [match, setMatch] = useState(() => (_: string) => true);
|
||||
const logData = useGlobalLogData("all");
|
||||
const [searchState, setSearchState] = useState<SearchState>();
|
||||
const {
|
||||
response: { data: logData },
|
||||
refreshGetClashLog,
|
||||
} = useLogData();
|
||||
|
||||
const filterLogs = useMemo(() => {
|
||||
if (!logData || logData.length === 0) {
|
||||
@@ -49,18 +43,21 @@ const LogPage = () => {
|
||||
|
||||
const matchesSearch = match(searchText);
|
||||
|
||||
return matchesSearch;
|
||||
return (
|
||||
(logState == "all" ? true : data.type.includes(logState)) &&
|
||||
matchesSearch
|
||||
);
|
||||
});
|
||||
}, [logData, match]);
|
||||
}, [logData, logState, match]);
|
||||
|
||||
const handleLogLevelChange = (newLevel: LogLevel) => {
|
||||
setLogLevel(newLevel);
|
||||
changeLogLevel(newLevel);
|
||||
const handleLogLevelChange = (newLevel: string) => {
|
||||
setClashLog((pre: any) => ({ ...pre, logFilter: newLevel }));
|
||||
// changeLogLevel(newLevel);
|
||||
};
|
||||
|
||||
const handleToggleLog = async () => {
|
||||
await toggleLogEnabled();
|
||||
setEnableLog(!enableLog);
|
||||
setClashLog((pre: any) => ({ ...pre, enable: !enableLog }));
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -92,7 +89,8 @@ const LogPage = () => {
|
||||
size="small"
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
clearGlobalLogs();
|
||||
refreshGetClashLog(true);
|
||||
// clearGlobalLogs();
|
||||
}}
|
||||
>
|
||||
{t("Clear")}
|
||||
@@ -111,14 +109,14 @@ const LogPage = () => {
|
||||
}}
|
||||
>
|
||||
<BaseStyledSelect
|
||||
value={logLevel}
|
||||
onChange={(e) => handleLogLevelChange(e.target.value as LogLevel)}
|
||||
value={logState}
|
||||
onChange={(e) => handleLogLevelChange(e.target.value as LogFilter)}
|
||||
>
|
||||
<MenuItem value="all">ALL</MenuItem>
|
||||
<MenuItem value="debug">DEBUG</MenuItem>
|
||||
<MenuItem value="info">INFO</MenuItem>
|
||||
<MenuItem value="warning">WARNING</MenuItem>
|
||||
<MenuItem value="error">ERROR</MenuItem>
|
||||
<MenuItem value="warn">WARN</MenuItem>
|
||||
<MenuItem value="err">ERROR</MenuItem>
|
||||
</BaseStyledSelect>
|
||||
<BaseSearchBox
|
||||
onSearch={(matcher, state) => {
|
||||
|
||||
@@ -34,6 +34,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import useSWR, { mutate } from "swr";
|
||||
import { closeAllConnections } from "tauri-plugin-mihomo-api";
|
||||
|
||||
import { BasePage, DialogRef } from "@/components/base";
|
||||
import { BaseStyledTextField } from "@/components/base/base-styled-text-field";
|
||||
@@ -47,7 +48,6 @@ import { ConfigViewer } from "@/components/setting/mods/config-viewer";
|
||||
import { useListen } from "@/hooks/use-listen";
|
||||
import { useProfiles } from "@/hooks/use-profiles";
|
||||
import {
|
||||
closeAllConnections,
|
||||
createProfile,
|
||||
deleteProfile,
|
||||
enhanceProfiles,
|
||||
|
||||
@@ -3,14 +3,13 @@ import { useLockFn } from "ahooks";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import useSWR from "swr";
|
||||
import { closeAllConnections, getBaseConfig } from "tauri-plugin-mihomo-api";
|
||||
|
||||
import { BasePage } from "@/components/base";
|
||||
import { ProviderButton } from "@/components/proxy/provider-button";
|
||||
import { ProxyGroups } from "@/components/proxy/proxy-groups";
|
||||
import { useVerge } from "@/hooks/use-verge";
|
||||
import {
|
||||
closeAllConnections,
|
||||
getClashConfig,
|
||||
getRuntimeProxyChainConfig,
|
||||
patchClashMode,
|
||||
updateProxyChainConfigInRuntime,
|
||||
@@ -33,7 +32,7 @@ const ProxyPage = () => {
|
||||
|
||||
const { data: clashConfig, mutate: mutateClash } = useSWR(
|
||||
"getClashConfig",
|
||||
getClashConfig,
|
||||
getBaseConfig,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
revalidateIfStale: true,
|
||||
|
||||
@@ -74,7 +74,7 @@ const RulesPage = () => {
|
||||
<BaseSearchBox onSearch={(match) => setMatch(() => match)} />
|
||||
</Box>
|
||||
|
||||
{filteredRules.length > 0 ? (
|
||||
{filteredRules && filteredRules.length > 0 ? (
|
||||
<>
|
||||
<Virtuoso
|
||||
ref={virtuosoRef}
|
||||
|
||||
Reference in New Issue
Block a user