diff --git a/.github/update.log b/.github/update.log index c7d8fda0b8..ec8c7a3582 100644 --- a/.github/update.log +++ b/.github/update.log @@ -616,3 +616,4 @@ Update On Wed Apr 10 20:31:24 CEST 2024 Update On Thu Apr 11 20:26:19 CEST 2024 Update On Fri Apr 12 20:24:06 CEST 2024 Update On Sat Apr 13 20:24:55 CEST 2024 +Update On Mon Apr 15 01:03:26 CEST 2024 diff --git a/clash-nyanpasu/.eslintrc.cjs b/clash-nyanpasu/.eslintrc.cjs index c5c9f176a6..c5391b4dd2 100644 --- a/clash-nyanpasu/.eslintrc.cjs +++ b/clash-nyanpasu/.eslintrc.cjs @@ -19,6 +19,7 @@ module.exports = { "@typescript-eslint/no-unused-vars": "warn", "@typescript-eslint/no-explicit-any": "warn", "react/react-in-jsx-scope": "off", + "@typescript-eslint/no-namespace": "off", }, settings: { react: { diff --git a/clash-nyanpasu/frontend/interface/ipc/index.ts b/clash-nyanpasu/frontend/interface/ipc/index.ts index f122a8dc71..ec5575d1ea 100644 --- a/clash-nyanpasu/frontend/interface/ipc/index.ts +++ b/clash-nyanpasu/frontend/interface/ipc/index.ts @@ -1 +1,2 @@ export * from "./useNyanpasu"; +export * from "./useClash"; diff --git a/clash-nyanpasu/frontend/interface/ipc/useClash.ts b/clash-nyanpasu/frontend/interface/ipc/useClash.ts new file mode 100644 index 0000000000..8bbccfdd84 --- /dev/null +++ b/clash-nyanpasu/frontend/interface/ipc/useClash.ts @@ -0,0 +1,27 @@ +import useSWR from "swr"; +import { clash } from "../service/clash"; + +export const useClash = () => { + const { setConfigs, setProxies, deleteConnections, ...api } = clash(); + + const getConfigs = useSWR("getClashConfig", api.getConfigs); + + const getVersion = useSWR("getClashVersion", api.getVersion); + + const getRules = useSWR("getClashRules", api.getRules); + + const getProxiesDelay = useSWR("getClashProxiesDelay", api.getProxiesDelay); + + const getProxies = useSWR("getClashProxies", api.getProxies); + + return { + getConfigs, + setConfigs, + getVersion, + getRules, + getProxiesDelay, + getProxies, + setProxies, + deleteConnections, + }; +}; diff --git a/clash-nyanpasu/frontend/interface/package.json b/clash-nyanpasu/frontend/interface/package.json index f0249134cd..0a5f721223 100644 --- a/clash-nyanpasu/frontend/interface/package.json +++ b/clash-nyanpasu/frontend/interface/package.json @@ -5,6 +5,7 @@ "module": "index.ts", "dependencies": { "@tauri-apps/api": "1.5.3", + "ofetch": "1.3.4", "swr": "2.2.5" } } diff --git a/clash-nyanpasu/frontend/interface/service/clash.ts b/clash-nyanpasu/frontend/interface/service/clash.ts new file mode 100644 index 0000000000..6290b3fff1 --- /dev/null +++ b/clash-nyanpasu/frontend/interface/service/clash.ts @@ -0,0 +1,146 @@ +import { ofetch } from "ofetch"; +import { getClashInfo } from "./tauri"; + +export namespace Clash { + export interface Config { + port: number; + mode: string; + ipv6: boolean; + "socket-port": number; + "allow-lan": boolean; + "log-level": string; + "mixed-port": number; + "redir-port": number; + "socks-port": number; + "tproxy-port": number; + "external-controller": string; + secret: string; + } + + export interface Version { + premium: boolean; + meta?: boolean; + version: string; + } + + export interface Rule { + type: string; + payload: string; + proxy: string; + } + + export interface Proxy { + name: string; + type: string; + udp: boolean; + history: { + time: string; + delay: number; + }[]; + all?: string[]; + now?: string; + provider?: string; + } +} + +const prepareServer = (server: string) => { + if (server.startsWith(":")) { + return `127.0.0.1${server}`; + } else if (/^\d+$/.test(server)) { + return `127.0.0.1:${server}`; + } else { + return server; + } +}; + +export const clash = () => { + const buildRequest = async () => { + const info = await getClashInfo(); + + return ofetch.create({ + baseURL: `http://${prepareServer(info?.server as string)}`, + headers: info?.secret + ? { Authorization: `Bearer ${info?.secret}` } + : undefined, + }); + }; + + const getConfigs = async () => { + return (await buildRequest())("/configs"); + }; + + const setConfigs = async (config: Partial) => { + return (await buildRequest())("/configs", { + method: "PATCH", + body: config, + }); + }; + + const getVersion = async () => { + return (await buildRequest())("/version"); + }; + + const getRules = async () => { + interface PrivateRule { + rules: Clash.Rule[]; + } + + return (await buildRequest())("/rules"); + }; + + const getProxiesDelay = async ( + name: string, + options?: { + url?: string; + timeout?: number; + }, + ) => { + return (await buildRequest())<{ delay: number }>( + `/proxies/${encodeURIComponent(name)}/delay`, + { + params: { + timeout: options?.timeout || 10000, + url: options?.url || "http://www.gstatic.com/generate_204", + }, + }, + ); + }; + + const getProxies = async () => { + interface PrivateProxy { + proxies: Clash.Proxy[]; + } + + return (await buildRequest())("/proxies"); + }; + + const setProxies = async ({ + group, + proxy, + }: { + group: string; + proxy: string; + }) => { + return (await buildRequest())(`/proxies/${encodeURIComponent(group)}`, { + method: "PUT", + body: { name: proxy }, + }); + }; + + const deleteConnections = async () => { + return (await buildRequest())(`/connections`, { + method: "DELETE", + }); + }; + + return { + getConfigs, + setConfigs, + getVersion, + getRules, + getProxiesDelay, + getProxies, + setProxies, + deleteConnections, + }; +}; diff --git a/clash-nyanpasu/frontend/interface/service/index.ts b/clash-nyanpasu/frontend/interface/service/index.ts index c758311d3e..257a6f7b86 100644 --- a/clash-nyanpasu/frontend/interface/service/index.ts +++ b/clash-nyanpasu/frontend/interface/service/index.ts @@ -1,2 +1,3 @@ export * from "./types"; export * from "./tauri"; +export * from "./clash"; diff --git a/clash-nyanpasu/frontend/interface/service/tauri.ts b/clash-nyanpasu/frontend/interface/service/tauri.ts index 8359bee451..b5d91d4e58 100644 --- a/clash-nyanpasu/frontend/interface/service/tauri.ts +++ b/clash-nyanpasu/frontend/interface/service/tauri.ts @@ -1,5 +1,5 @@ import { invoke } from "@tauri-apps/api/tauri"; -import { VergeConfig } from "./types"; +import { ClashInfo, VergeConfig } from "./types"; export const nyanpasuConfig = { get: async () => { @@ -10,3 +10,7 @@ export const nyanpasuConfig = { return await invoke("patch_verge_config", { payload }); }, }; + +export const getClashInfo = async () => { + return await invoke("get_clash_info"); +}; diff --git a/clash-nyanpasu/frontend/interface/service/types.ts b/clash-nyanpasu/frontend/interface/service/types.ts index 3595d0a336..3ae8a2554d 100644 --- a/clash-nyanpasu/frontend/interface/service/types.ts +++ b/clash-nyanpasu/frontend/interface/service/types.ts @@ -44,3 +44,9 @@ export interface VergeConfig { external_controller_port_strategy: "fixed" | "random" | "allow_fallback"; }; } + +export interface ClashInfo { + port?: number; + server?: string; + secret?: string; +} diff --git a/clash-nyanpasu/frontend/nyanpasu/package.json b/clash-nyanpasu/frontend/nyanpasu/package.json index ed31c5dcc3..c3b632a854 100644 --- a/clash-nyanpasu/frontend/nyanpasu/package.json +++ b/clash-nyanpasu/frontend/nyanpasu/package.json @@ -14,7 +14,7 @@ "@dnd-kit/utilities": "3.2.2", "@emotion/react": "11.11.4", "@emotion/styled": "11.11.5", - "@generouted/react-router": "1.19.2", + "@generouted/react-router": "1.19.3", "@juggle/resize-observer": "3.4.0", "@mui/icons-material": "5.15.15", "@mui/lab": "5.0.0-alpha.170", diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/proxies.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/proxies.tsx index 755ed0e4e6..2569d052b6 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/pages/proxies.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/proxies.tsx @@ -1,57 +1,45 @@ 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, - updateConfigs, -} from "@/services/api"; -import { patchClashConfig } from "@/services/cmds"; -import { Box, Button, ButtonGroup, Paper } from "@mui/material"; +import { Box, Button, ButtonGroup } from "@mui/material"; import { useLockFn } from "ahooks"; import { useEffect, useMemo } from "react"; import { useTranslation } from "react-i18next"; -import useSWR from "swr"; +import { useNyanpasu, useClash } from "@nyanpasu/interface"; export default function ProxyPage() { const { t } = useTranslation(); - const { data: clashConfig, mutate: mutateClash } = useSWR( - "getClashConfig", - getClashConfig, - ); + const { nyanpasuConfig } = useNyanpasu(); - const { verge } = useVerge(); + const { getConfigs, setConfigs, deleteConnections } = useClash(); const modeList = useMemo(() => { - if ( - verge?.clash_core === "mihomo" || - verge?.clash_core === "mihomo-alpha" || - verge?.clash_core === "clash-rs" - ) { - return ["rule", "global", "direct"]; - } - return ["rule", "global", "direct", "script"]; - }, [verge?.clash_core]); + const defaultModes = ["rule", "global", "direct"]; - const curMode = clashConfig?.mode?.toLowerCase(); + return ["mihomo", "mihomo-alpha", "clash-rs"].includes( + nyanpasuConfig?.clash_core, + ) + ? defaultModes + : [...defaultModes, "script"]; + }, [nyanpasuConfig?.clash_core]); - const onChangeMode = useLockFn(async (mode: string) => { - // 断开连接 - if (mode !== curMode && verge?.auto_close_connection) { - closeAllConnections(); + const currentMode = getConfigs.data?.mode?.toLowerCase(); + + const onChangeMode = useLockFn(async (mode) => { + if (mode !== currentMode && nyanpasuConfig?.auto_close_connection) { + await deleteConnections(); } - await updateConfigs({ mode }); - await patchClashConfig({ mode }); - mutateClash(); + + await setConfigs({ mode }); + await getConfigs.mutate(); }); useEffect(() => { - if (curMode && !modeList.includes(curMode)) { + if (currentMode && !modeList.includes(currentMode)) { onChangeMode("rule"); } - }, [curMode]); + }, [currentMode, modeList, onChangeMode]); return ( (