import { useLockFn } from "ahooks"; import useSWR, { mutate } from "swr"; import { getVersion } from "tauri-plugin-mihomo-api"; import { getClashInfo, getRuntimeConfig, patchClashConfig, } from "@/services/cmds"; const PORT_KEYS = [ "port", "socks-port", "mixed-port", "redir-port", "tproxy-port", ] as const; type ClashInfoPatch = Partial< Pick< IConfigData, | "port" | "socks-port" | "mixed-port" | "redir-port" | "tproxy-port" | "external-controller" | "secret" > >; const hasClashInfoPayload = (patch: ClashInfoPatch) => PORT_KEYS.some((key) => patch[key] != null) || patch["external-controller"] != null || patch.secret != null; const validatePortRange = (port: number) => { if (port < 1000) { throw new Error("The port should not < 1000"); } if (port > 65536) { throw new Error("The port should not > 65536"); } }; const validatePorts = (patch: ClashInfoPatch) => { PORT_KEYS.forEach((key) => { const port = patch[key]; if (!port) return; validatePortRange(port); }); }; export const useRuntimeConfig = (shouldFetch: boolean = true) => { return useSWR(shouldFetch ? "getRuntimeConfig" : null, getRuntimeConfig); }; export const useClash = () => { const { data: clash, mutate: mutateClash } = useRuntimeConfig(); const { data: versionData, mutate: mutateVersion } = useSWR( "getVersion", getVersion, ); const patchClash = useLockFn(async (patch: Partial) => { await patchClashConfig(patch); mutateClash(); }); const version = versionData?.meta ? `${versionData.version} Mihomo` : versionData?.version || "-"; return { clash, version, mutateClash, mutateVersion, patchClash, }; }; export const useClashInfo = () => { const { data: clashInfo, mutate: mutateInfo } = useSWR( "getClashInfo", getClashInfo, ); const patchInfo = async (patch: ClashInfoPatch) => { if (!hasClashInfoPayload(patch)) return; validatePorts(patch); await patchClashConfig(patch); mutateInfo(); mutate("getClashConfig"); }; return { clashInfo, mutateInfo, patchInfo, }; };