From 1693316766728736908d24eed2d9f386227b5521 Mon Sep 17 00:00:00 2001 From: liyuanhu Date: Mon, 21 Apr 2025 14:40:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9Ewebsoket=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E5=AF=B9=E5=BA=94=E7=9A=84events?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 3 +- src-tauri/src/core/api.rs | 3 +- src/App.tsx | 136 ++++++++++-------- src/bindings.ts | 2 +- src/data/index.ts | 12 ++ src/hooks/useStartupCheck.ts | 5 +- .../components/ClearNodeDialog/index.tsx | 41 +++--- .../components/ClearNodeDialog/index.tsx | 32 ++--- .../components/ClearNodeDialog/index.tsx | 40 ++++-- src/pages/new-home/index.tsx | 8 +- src/store/web3Slice.ts | 13 +- src/vite-env.d.ts | 1 + 12 files changed, 180 insertions(+), 116 deletions(-) diff --git a/.env b/.env index 473efd1..7bc5ee5 100644 --- a/.env +++ b/.env @@ -3,5 +3,6 @@ COSMOS_ENDPOINT="http://10.66.66.234:26657" NODE_SECRET="aHVnZSBjb21wYW55IHBob25lIHdlc3QgcGxhY2Ugc2VtaW5hciBtaXJhY2xlIGxlbmQgbWFuZGF0ZSB0aGVuIGFkanVzdCBxdWl0IG1lYXQgY2hlYXAgbm9vZGxlIGNvdXBsZSBkZWZpbmUgbXVzY2xlIHB1bHNlIHNpc3RlciBwaWVjZSBkZXZpY2UgcHJpdmF0ZSBob29k" IS_DEBUG="true" ACCOUNT_NAME="de1" +VIET_EVENTS_WS_URL="ws://10.66.66.234:8080/events" VITE_BASE_URL="http://10.66.66.234:6060" -VITE_BLOCK_URL="http://localhost:3001" +VITE_BLOCK_URL="http://10.66.66.234:1317" \ No newline at end of file diff --git a/src-tauri/src/core/api.rs b/src-tauri/src/core/api.rs index 4dc6370..4c5ffa0 100644 --- a/src-tauri/src/core/api.rs +++ b/src-tauri/src/core/api.rs @@ -12,6 +12,7 @@ use paw_common::preclude::{CoreConfig, CoreResponse}; #[derive(Debug, Clone, Deserialize, Serialize, specta::Type)] pub struct ProxyNodeInfo { pub name: String, + pub ip: String, pub country_code: String, pub country_name: String, pub country_name_zh: String, @@ -76,7 +77,7 @@ pub async fn get_nodes(config: CoreConfig) -> Result> { debug!("Successfully got nodes: {:?}", result.data); Ok(result.data) } else { - error!("Failed to get nodes: {}", response.text().await?); + debug!("Failed to get nodes: {}", response.text().await?); bail!("Failed to get nodes") } } diff --git a/src/App.tsx b/src/App.tsx index 357e636..952ad1d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,7 +12,7 @@ import { setMaliciousNodeList, setNodeDownList, } from "@/store/web3Slice"; -import type { AppDispatch, RootState } from "@/store"; +import type { AppDispatch } from "@/store"; import eventBus, { eventTypes } from "@/utils/eventBus"; import { WebSocketClient } from "@/utils/webSocketClient"; @@ -20,6 +20,8 @@ import { WebSocketClient } from "@/utils/webSocketClient"; import Titlebar from "@/components/Titlebar"; import Layout from "@/layout"; import Tray from "@/components/Tray"; +import { getRandomCountryKey } from "./data"; +import dayjs from "dayjs"; function App() { // 执行启动自检 @@ -37,79 +39,99 @@ function App() { // const { } = useSelector( // (state: RootState) => state.web3Reducer // ); - const webSocketClient = useRef(); + let eventsWs: WebSocketClient | null = null; const openWsTraffic = async () => { - if (webSocketClient.current) return; - const { api_port } = await loadCoreConfig(); - // todo! 后面会把二级制文件启动的参数作为配置项,这里暂时写死 - webSocketClient.current = new WebSocketClient( - `ws://127.0.0.1:${api_port}/traffic`, - { - headers: { - Authorization: "Bearer secret", - }, - } - ); - + if (eventsWs) return; + eventsWs = new WebSocketClient("ws://10.66.66.234:8080/events", {}); + console.log(eventsWs, "openWsTraffic Start"); // 执行 WebSocket 操作 - await webSocketClient.current.connect(); - webSocketClient.current.addListener((msg: any) => { - if (msg.code === 0) { - switch (msg.event) { - case eventTypes.NODE_UP: - console.log("节点上线"); - break; - case eventTypes.NODE_DOWN: - // 添加下线节点到store 里面 - dispatch(setNodeDownList(msg.data.name)); - console.log("节点下线"); - break; - case eventTypes.MALICIOUS_NODE: - // 添加恶意节点到store 里面 - dispatch(setMaliciousNodeList(msg.data.name)); - console.log("检测到恶意节点"); - break; - case eventTypes.NODE_INIT_COMPLATE: - console.log("节点预配置完成"); - break; - case eventTypes.NODE_REMOVE: - console.log("节点清除"); - break; - case eventTypes.NODE_ADD: - console.log("添加节点"); - break; - case eventTypes.NODE_INIT: - console.log("节点预配置"); - break; - default: - break; + await eventsWs?.connect(); + await eventsWs?.addListener((msg: any) => { + try { + const msgData = msg ? JSON.parse(msg.data) : {}; + if (msgData.code === 0) { + console.log(msgData, "msgDatamsgData"); + switch (msgData.event) { + case eventTypes.NODE_UP: + console.log("节点上线"); + break; + case eventTypes.NODE_DOWN: + // 添加下线节点到store 里面 + if (msgData.data.name) { + // 获取一个随机的国家code + const countryCode = getRandomCountryKey(); + dispatch( + setNodeDownList({ + name: msgData.data.name, + code: countryCode, + }) + ); + } + console.log("节点下线"); + break; + case eventTypes.MALICIOUS_NODE: + // 添加恶意节点到store 里面 + if (msgData.data.name) { + // 获取一个随机的国家code + const countryCode = getRandomCountryKey(); + dispatch( + setMaliciousNodeList({ + name: msgData.data.name, + code: countryCode, + }) + ); + } + console.log("检测到恶意节点"); + break; + case eventTypes.NODE_INIT_COMPLATE: + console.log("节点预配置完成"); + break; + case eventTypes.NODE_REMOVE: + console.log("节点清除"); + break; + case eventTypes.NODE_ADD: + console.log("添加节点"); + break; + case eventTypes.NODE_INIT: + console.log("节点预配置"); + break; + default: + break; + } } + } catch (error) { + console.log(error, "error"); } }); + console.log(eventsWs, "openWsTraffic End"); }; const createdSoketEventBus = async () => { eventBus.on(eventTypes.NODE_INIT_COMPLATE, (data: any) => { console.log("节点预配置完成", data); - webSocketClient.current?.sendMessage(data); + eventsWs?.sendMessage(data); }); eventBus.on(eventTypes.NODE_REMOVE, (data: any) => { console.log("节点清除"); - webSocketClient.current?.sendMessage({ + const timestamp = dayjs().unix(); + const params = { code: 0, event: eventTypes.NODE_REMOVE, data: { name: data, }, - }); + timestamp, + }; + console.log(JSON.stringify(params), "节点清除 params"); + eventsWs?.sendMessage(JSON.stringify(params)); }); }; const closeWsTraffic = async () => { - if (webSocketClient.current) { - await webSocketClient.current.disconnect(); - webSocketClient.current = null; + if (eventsWs) { + await eventsWs.disconnect(); + eventsWs = null; } }; @@ -124,11 +146,13 @@ function App() { }; useEffect(() => { - // initWebsocketsAndEventBus(); - // return () => { - // closeWsTraffic(); - // removeSoketEventBus(); - // }; + setTimeout(() => { + initWebsocketsAndEventBus(); + }, 1000); + return () => { + closeWsTraffic(); + removeSoketEventBus(); + }; }, []); return ( diff --git a/src/bindings.ts b/src/bindings.ts index 65611ea..c16884a 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -209,7 +209,7 @@ export type CoreConfig = { socks_port: number; socks_user: string; socks_pass: s /** * 节点信息 */ -export type ProxyNodeInfo = { name: string; country_code: string; country_name: string; country_name_zh: string; city_name: string; city_name_zh: string; delay: number; download: number; upload: number; survive_score: number; exit: boolean; use: boolean } +export type ProxyNodeInfo = { name: string; ip: string; country_code: string; country_name: string; country_name_zh: string; city_name: string; city_name_zh: string; delay: number; download: number; upload: number; survive_score: number; exit: boolean; use: boolean } /** tauri-specta globals **/ diff --git a/src/data/index.ts b/src/data/index.ts index 3e9475a..5f18295 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -500,6 +500,18 @@ export const countryNameMap = { Curaçao: '库拉索', } +// 获取一个随机的国家key +export function getRandomCountryKey() { + const keys = Object.keys(countryCodeMap) + return keys[Math.floor(Math.random() * keys.length)] +} + +// 获取一个随机的国家name +export function getRandomCountryName() { + const keys = Object.keys(countryCodeMap) + return countryCodeMap[keys[Math.floor(Math.random() * keys.length)]] +} + // 国家代码映射 export const countryCodeMap: { [key: string]: string } = { AD: '安道尔', diff --git a/src/hooks/useStartupCheck.ts b/src/hooks/useStartupCheck.ts index 7f07ab5..d70f3bc 100644 --- a/src/hooks/useStartupCheck.ts +++ b/src/hooks/useStartupCheck.ts @@ -201,7 +201,6 @@ export function useStartupCheck() { // 只有当核心正在运行时才更新节点 if (isCoreRunning) { if (nodesResult.status === "ok") { - console.log("触发了???"); const proxies:any[] = []; nodesResult.data.forEach((node,index) => { const { country_code } = node @@ -210,8 +209,8 @@ export function useStartupCheck() { } proxies.push({ country_code: country_code,ingress_country_code: nodesResult.data[index + 1].country_code }) }) - console.log(proxies,'proxiesproxiesproxies') - // dispatch(setProxiesList1()) + // console.log(proxies,'proxiesproxiesproxies') + dispatch(setProxiesList1(proxies)) dispatch(setNodes(nodesResult.data)); } else { dispatch(setNodesError("Failed to fetch nodes")); diff --git a/src/pages/anti-forensics-forwarding/components/ClearNodeDialog/index.tsx b/src/pages/anti-forensics-forwarding/components/ClearNodeDialog/index.tsx index f0f2ee2..cce0d08 100644 --- a/src/pages/anti-forensics-forwarding/components/ClearNodeDialog/index.tsx +++ b/src/pages/anti-forensics-forwarding/components/ClearNodeDialog/index.tsx @@ -3,17 +3,17 @@ import { FormInstance } from "antd"; import { FormDialog } from "@/components/FormDialog"; import { useDispatch, useSelector } from "react-redux"; -import { nodeList, getRandomNodes } from "@/store/datas"; import "./index.scss"; import { EllipsisTooltip } from "@/components/Encapsulation"; import NotFailNodeIcon from "@/assets/svg/common/not-fail-node.svg?react"; import NotWarningNodeIcon from "@/assets/svg/common/not-warning-node.svg?react"; import { NODEDIALOGTYPE } from "../../index"; import { cn, getUrl } from "@/lib/utils"; +import eventBus, { eventTypes } from "@/utils/eventBus"; -import { } from "@/store/web3Slice"; import { AppDispatch, RootState } from "@/store"; -import { isTimestampPlusTenMinutesBeforeNow } from "@/utils/tools"; +import { removeMaliciousNodeList, removeNodeDownList } from "@/store/web3Slice"; +import { countryCodeMap } from "@/data"; export interface DialogConfig { title: string; @@ -24,8 +24,7 @@ export interface DialogConfig { export const ProxyItem: React.FC<{ proxyInfo: any; clasName?: string }> = ( props ) => { - const { name, code, exit = false } = props.proxyInfo; - + const { code, exit = false } = props.proxyInfo; return (
= (
@@ -70,7 +69,7 @@ export const ClearNodeDialog = ({ type: DialogConfig; }) => { const dispatch = useDispatch(); - const { } = useSelector( + const { maliciousNodeList, nodeDownList } = useSelector( (state: RootState) => state.web3Reducer ); const [isClear, setIsClear] = useState(false); @@ -81,18 +80,26 @@ export const ClearNodeDialog = ({ successHandle(); setIsClear(true); if (type.title === NODEDIALOGTYPE.ClearFailNode.title) { - + nodeDownList.forEach((item) => { + dispatch(removeNodeDownList(item.name)); + eventBus.emit(eventTypes.NODE_REMOVE, item.name); + }); } else if (type.title === NODEDIALOGTYPE.ClearWargingNode.title) { - + maliciousNodeList.forEach((item) => { + dispatch(removeMaliciousNodeList(item.name)); + eventBus.emit(eventTypes.NODE_REMOVE, item.name); + }); } // showDialog(false); }; const proxyList = useMemo(() => { - const newData = getRandomNodes(nodeList); - - return newData; - }, [nodeList, open, isClear, type]); + if (type.title === NODEDIALOGTYPE.ClearFailNode.title) { + return nodeDownList; + } else { + return maliciousNodeList; + } + }, [nodeDownList, maliciousNodeList, type.title]); useEffect(() => { if (!open) { @@ -119,9 +126,11 @@ export const ClearNodeDialog = ({ >
{proxyList.length > 0 ? ( - proxyList.map((item) => { - return ; - }) + proxyList + .filter((item: any) => item?.name) + .map((item: any) => { + return ; + }) ) : (
{type.title === NODEDIALOGTYPE.ClearFailNode.title ? ( diff --git a/src/pages/decentralized-lastic-network/components/ClearNodeDialog/index.tsx b/src/pages/decentralized-lastic-network/components/ClearNodeDialog/index.tsx index 5749b80..2d1ac99 100644 --- a/src/pages/decentralized-lastic-network/components/ClearNodeDialog/index.tsx +++ b/src/pages/decentralized-lastic-network/components/ClearNodeDialog/index.tsx @@ -3,7 +3,6 @@ import { FormInstance } from "antd"; import { FormDialog } from "@/components/FormDialog"; import { useDispatch, useSelector } from "react-redux"; -import { nodeList, getRandomNodes } from "@/store/datas"; import "./index.scss"; import { EllipsisTooltip } from "@/components/Encapsulation"; import NotFailNodeIcon from "@/assets/svg/common/not-fail-node.svg?react"; @@ -13,8 +12,8 @@ import { cn, getUrl } from "@/lib/utils"; import eventBus, { eventTypes } from "@/utils/eventBus"; import { AppDispatch, RootState } from "@/store"; -import {removeMaliciousNodeList,removeNodeDownList} from '@/store/web3Slice' -import { isTimestampPlusTenMinutesBeforeNow } from "@/utils/tools"; +import { removeMaliciousNodeList, removeNodeDownList } from "@/store/web3Slice"; +import { countryCodeMap } from "@/data"; export interface DialogConfig { title: string; @@ -25,8 +24,7 @@ export interface DialogConfig { export const ProxyItem: React.FC<{ proxyInfo: any; clasName?: string }> = ( props ) => { - const { name, code, exit = false } = props.proxyInfo; - + const { code, exit = false } = props.proxyInfo; return (
= (
@@ -82,15 +80,15 @@ export const ClearNodeDialog = ({ successHandle(); setIsClear(true); if (type.title === NODEDIALOGTYPE.ClearFailNode.title) { - nodeDownList.forEach((item)=>{ - dispatch(removeNodeDownList(item.name)); + nodeDownList.forEach((item) => { + dispatch(removeNodeDownList(item)); eventBus.emit(eventTypes.NODE_REMOVE, item.name); - }) + }); } else if (type.title === NODEDIALOGTYPE.ClearWargingNode.title) { - maliciousNodeList.forEach((item)=>{ - dispatch(removeMaliciousNodeList(item.name)); + maliciousNodeList.forEach((item) => { + dispatch(removeMaliciousNodeList(item)); eventBus.emit(eventTypes.NODE_REMOVE, item.name); - }) + }); } // showDialog(false); }; @@ -127,10 +125,12 @@ export const ClearNodeDialog = ({ } >
- {proxyList?.length > 0 ? ( - proxyList.map((item) => { - return ; - }) + {proxyList.length > 0 ? ( + proxyList + .filter((item: any) => item?.name) + .map((item: any) => { + return ; + }) ) : (
{type.title === NODEDIALOGTYPE.ClearFailNode.title ? ( diff --git a/src/pages/new-home/components/ClearNodeDialog/index.tsx b/src/pages/new-home/components/ClearNodeDialog/index.tsx index 641814e..cce0d08 100644 --- a/src/pages/new-home/components/ClearNodeDialog/index.tsx +++ b/src/pages/new-home/components/ClearNodeDialog/index.tsx @@ -3,16 +3,17 @@ import { FormInstance } from "antd"; import { FormDialog } from "@/components/FormDialog"; import { useDispatch, useSelector } from "react-redux"; -import { nodeList, getRandomNodes } from "@/store/datas"; import "./index.scss"; import { EllipsisTooltip } from "@/components/Encapsulation"; import NotFailNodeIcon from "@/assets/svg/common/not-fail-node.svg?react"; import NotWarningNodeIcon from "@/assets/svg/common/not-warning-node.svg?react"; import { NODEDIALOGTYPE } from "../../index"; import { cn, getUrl } from "@/lib/utils"; +import eventBus, { eventTypes } from "@/utils/eventBus"; import { AppDispatch, RootState } from "@/store"; -import { isTimestampPlusTenMinutesBeforeNow } from "@/utils/tools"; +import { removeMaliciousNodeList, removeNodeDownList } from "@/store/web3Slice"; +import { countryCodeMap } from "@/data"; export interface DialogConfig { title: string; @@ -23,8 +24,7 @@ export interface DialogConfig { export const ProxyItem: React.FC<{ proxyInfo: any; clasName?: string }> = ( props ) => { - const { name, code, exit = false } = props.proxyInfo; - + const { code, exit = false } = props.proxyInfo; return (
= (
@@ -69,7 +69,9 @@ export const ClearNodeDialog = ({ type: DialogConfig; }) => { const dispatch = useDispatch(); - const {} = useSelector((state: RootState) => state.web3Reducer); + const { maliciousNodeList, nodeDownList } = useSelector( + (state: RootState) => state.web3Reducer + ); const [isClear, setIsClear] = useState(false); const showDialog = (open: boolean) => { setOpen(open); @@ -78,16 +80,26 @@ export const ClearNodeDialog = ({ successHandle(); setIsClear(true); if (type.title === NODEDIALOGTYPE.ClearFailNode.title) { + nodeDownList.forEach((item) => { + dispatch(removeNodeDownList(item.name)); + eventBus.emit(eventTypes.NODE_REMOVE, item.name); + }); } else if (type.title === NODEDIALOGTYPE.ClearWargingNode.title) { + maliciousNodeList.forEach((item) => { + dispatch(removeMaliciousNodeList(item.name)); + eventBus.emit(eventTypes.NODE_REMOVE, item.name); + }); } // showDialog(false); }; const proxyList = useMemo(() => { - const newData = getRandomNodes(nodeList); - - return []; - }, [nodeList, open, isClear, type]); + if (type.title === NODEDIALOGTYPE.ClearFailNode.title) { + return nodeDownList; + } else { + return maliciousNodeList; + } + }, [nodeDownList, maliciousNodeList, type.title]); useEffect(() => { if (!open) { @@ -114,9 +126,11 @@ export const ClearNodeDialog = ({ >
{proxyList.length > 0 ? ( - proxyList.map((item) => { - return ; - }) + proxyList + .filter((item: any) => item?.name) + .map((item: any) => { + return ; + }) ) : (
{type.title === NODEDIALOGTYPE.ClearFailNode.title ? ( diff --git a/src/pages/new-home/index.tsx b/src/pages/new-home/index.tsx index 71408a4..9a04828 100644 --- a/src/pages/new-home/index.tsx +++ b/src/pages/new-home/index.tsx @@ -290,14 +290,14 @@ const NewHome = () => { }; useEffect(() => { blockChainApi.getLatestBlock().then((res) => { - console.log("res", res); + console.log("getLatestBlock res:", res); }); initData(); }, []); - useEffect(() => { - console.log(dataInfo, "awaidataInfodataInfotawait"); - }, [dataInfo]); + // useEffect(() => { + // console.log(dataInfo, "awaidataInfodataInfotawait"); + // }, [dataInfo]); return (
diff --git a/src/store/web3Slice.ts b/src/store/web3Slice.ts index a517dce..537ab4f 100644 --- a/src/store/web3Slice.ts +++ b/src/store/web3Slice.ts @@ -159,18 +159,18 @@ export const appSlice = createSlice({ reducers: { removeMaliciousNodeList: (state, action) => { state.maliciousNodeList = state.maliciousNodeList.filter( - (item) => item !== action.payload + (item) => item.name !== action.payload.name ); }, removeNodeDownList: (state, action) => { state.nodeDownList = state.nodeDownList.filter( - (item) => item !== action.payload + (item) => item.name !== action.payload.name ); }, setMaliciousNodeList: (state, action) => { // 判断当前节点是否已经存在 const maliciousNode = state.maliciousNodeList.find( - (item) => action.payload === item + (item) => action.payload.name === item.name ); if (!maliciousNode) { state.maliciousNodeList.push(action.payload); @@ -179,7 +179,7 @@ export const appSlice = createSlice({ setNodeDownList: (state, action) => { // 判断当前节点是否已经存在 const nodeDown = state.nodeDownList.find( - (item) => action.payload === item + (item) => action.payload.name === item.name ); if (!nodeDown) { state.nodeDownList.push(action.payload); @@ -196,7 +196,10 @@ export const appSlice = createSlice({ data: action.payload, }); } else { - proxy_info.proxies[0] = action.payload; + proxy_info.proxies[0] = { + ...proxy_info.proxies[0], + data: action.payload, + }; } state.proxy_info = proxy_info; }, diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index e5abdb9..0515019 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -4,6 +4,7 @@ interface ImportMetaEnv { // 定义你的环境变量,例如: readonly VITE_BASE_URL: string readonly VITE_BLOCK_URL: string + readonly VIET_EVENTS_WS_URL: string } interface ImportMeta {