332 lines
9.4 KiB
TypeScript
332 lines
9.4 KiB
TypeScript
import { useEffect, useRef } from "react";
|
|
import dayjs from "dayjs";
|
|
import { useStartupCheck } from "@/hooks/useStartupCheck";
|
|
import { usePreventDefault } from "@/hooks/usePreventDefaultEventListener";
|
|
import { useGlobalShortcut } from "@/hooks/useGlobalShortcut";
|
|
import { useRecreateTheCircuit } from "@/hooks/useRecreateTheCircuit";
|
|
import { useCoreConfig } from "@/hooks/useCoreConfig";
|
|
import { useDispatch } from "react-redux";
|
|
import {
|
|
setMaliciousNodeList,
|
|
setNodeDownList,
|
|
setWeb3List,
|
|
setWeb3List2,
|
|
} from "@/store/web3Slice";
|
|
import type { AppDispatch } from "@/store";
|
|
import eventBus, { eventTypes } from "@/utils/eventBus";
|
|
import { WebSocketClient } from "@/utils/webSocketClientV2";
|
|
import { blockChainApi } from "@/api/block";
|
|
import { getRandomCountryKey } from "@/data";
|
|
import Titlebar from "@/components/Titlebar";
|
|
import Layout from "@/layout";
|
|
import Tray from "@/components/Tray";
|
|
|
|
function App() {
|
|
// 执行启动自检
|
|
useStartupCheck();
|
|
// 阻止默认事件
|
|
usePreventDefault();
|
|
// 注册全局快捷方式
|
|
useGlobalShortcut();
|
|
// 当核心启动时重新创建电路
|
|
useRecreateTheCircuit();
|
|
// 读取配置,若文件不存在则持久化到本地 `%APPDATA%/com.paw.paw-gui`
|
|
const { loadCoreConfig } = useCoreConfig();
|
|
loadCoreConfig();
|
|
|
|
const dispatch = useDispatch<AppDispatch>();
|
|
const eventsWsRef = useRef<WebSocketClient | null>(null);
|
|
const blockchainTimerRef = useRef<NodeJS.Timeout | null>(null);
|
|
const wsInitializedRef = useRef<boolean>(false);
|
|
|
|
// 处理 WebSocket 消息
|
|
const handleWebSocketMessage = (msg: any) => {
|
|
try {
|
|
const msgData = msg ? JSON.parse(msg.data) : {};
|
|
console.log("Received WebSocket message:", msgData);
|
|
|
|
if (msgData?.code === 0) {
|
|
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:
|
|
if (eventsWsRef.current?.isWebSocketConnected()) {
|
|
eventsWsRef.current.sendMessage(
|
|
JSON.stringify({
|
|
...msgData,
|
|
event: eventTypes.NODE_ADD,
|
|
})
|
|
);
|
|
console.log("节点预配置完成:", {
|
|
...msgData,
|
|
event: eventTypes.NODE_ADD,
|
|
});
|
|
}
|
|
break;
|
|
|
|
case eventTypes.NODE_REMOVE:
|
|
console.log("节点清除");
|
|
break;
|
|
|
|
case eventTypes.NODE_ADD:
|
|
console.log("添加节点");
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("Error processing WebSocket message:", error);
|
|
}
|
|
};
|
|
|
|
// 初始化 WebSocket 连接
|
|
const initWebSocket = async () => {
|
|
if (eventsWsRef.current) return;
|
|
|
|
console.log("Initializing WebSocket connection...");
|
|
|
|
eventsWsRef.current = new WebSocketClient(
|
|
"ws://47.82.97.10:8080/events",
|
|
{},
|
|
{
|
|
autoReconnect: true,
|
|
maxReconnectAttempts: 10,
|
|
reconnectDelay: 2000,
|
|
}
|
|
);
|
|
|
|
const connected = await eventsWsRef.current.connect();
|
|
|
|
if (connected) {
|
|
console.log("WebSocket connected successfully");
|
|
eventsWsRef.current.addListener(handleWebSocketMessage);
|
|
wsInitializedRef.current = true;
|
|
} else {
|
|
console.error("Failed to establish WebSocket connection");
|
|
}
|
|
};
|
|
|
|
// 设置事件总线监听器
|
|
const setupEventBusListeners = () => {
|
|
console.log("执行了没")
|
|
// 添加节点
|
|
eventBus.on(eventTypes.NODE_ADD, (data: any) => {
|
|
console.log("添加节点", data);
|
|
|
|
if (eventsWsRef.current?.isWebSocketConnected()) {
|
|
const timestamp = dayjs().unix();
|
|
const params = {
|
|
code: 0,
|
|
event: eventTypes.NODE_ADD,
|
|
data: data,
|
|
timestamp,
|
|
};
|
|
eventsWsRef.current.sendMessage(JSON.stringify(params));
|
|
} else {
|
|
console.warn("WebSocket not connected, can't send NODE_ADD message");
|
|
}
|
|
});
|
|
|
|
// 节点预配置事件
|
|
eventBus.on(eventTypes.NODE_INIT, (data: any) => {
|
|
console.log("节点预配置", data);
|
|
|
|
if (eventsWsRef.current?.isWebSocketConnected()) {
|
|
const timestamp = dayjs().unix();
|
|
const params = {
|
|
code: 0,
|
|
event: "node_init",
|
|
data: {},
|
|
timestamp: timestamp,
|
|
};
|
|
eventsWsRef.current.sendMessage(JSON.stringify(params));
|
|
} else {
|
|
console.warn("WebSocket not connected, can't send NODE_INIT message");
|
|
}
|
|
});
|
|
|
|
// 节点清除事件
|
|
eventBus.on(eventTypes.NODE_REMOVE, (data: any) => {
|
|
console.log("节点清除", data);
|
|
|
|
if (eventsWsRef.current?.isWebSocketConnected()) {
|
|
const timestamp = dayjs().unix();
|
|
const params = {
|
|
code: 0,
|
|
event: eventTypes.NODE_REMOVE,
|
|
data: {
|
|
name: data,
|
|
},
|
|
timestamp,
|
|
};
|
|
eventsWsRef.current.sendMessage(JSON.stringify(params));
|
|
} else {
|
|
console.warn("WebSocket not connected, can't send NODE_REMOVE message");
|
|
}
|
|
});
|
|
};
|
|
|
|
// 清理事件总线监听器
|
|
const cleanupEventBusListeners = () => {
|
|
eventBus.off(eventTypes.NODE_INIT);
|
|
eventBus.off(eventTypes.NODE_REMOVE);
|
|
};
|
|
|
|
// 启动区块链数据轮询
|
|
const startBlockchainPolling = () => {
|
|
// 立即执行一次
|
|
fetchBlockchainData();
|
|
|
|
// 设置定时器定期获取数据
|
|
blockchainTimerRef.current = setInterval(fetchBlockchainData, 5000);
|
|
};
|
|
|
|
// 获取区块链数据
|
|
const fetchBlockchainData = async () => {
|
|
try {
|
|
const blockRes = await blockChainApi.getLatestBlock();
|
|
const height = blockRes.data.block.last_commit.height;
|
|
|
|
const txsRes = await blockChainApi.getTxsByBlock(height);
|
|
const timer = txsRes.data.block.header.time;
|
|
const txs = txsRes.data.txs;
|
|
|
|
const balance = txs.reduce((acc: number, item: any) => {
|
|
const blance =
|
|
item.auth_info.fee.gas_limit *
|
|
Number(blockChainApi.blockConfig.minimum_gas_price);
|
|
return acc + blance;
|
|
}, 0);
|
|
|
|
const item = {
|
|
height: height,
|
|
txs: txs,
|
|
timerstamp: dayjs(timer).format("HH:mm:ss"),
|
|
balance,
|
|
balanceToFixed: Number(balance).toFixed(3),
|
|
};
|
|
|
|
dispatch(setWeb3List(item));
|
|
} catch (error) {
|
|
console.error("Error fetching blockchain data:", error);
|
|
}
|
|
};
|
|
|
|
// 初始化所有服务
|
|
const initializeServices = async () => {
|
|
// 初始化示例数据
|
|
dispatch(
|
|
setWeb3List2([
|
|
{
|
|
id: "6",
|
|
name: "Cardano Wallet",
|
|
payType: "ADA",
|
|
status: "active",
|
|
createdAt: 1737436420,
|
|
upDatedAt: 5,
|
|
balance: "65",
|
|
address:
|
|
"addr1qxck6ztj8lrxd0j2jz8f7tznzfu9wqv9qrplrh3r9eq8g9n0n3anjy2a4x54kd2sort3qvnc7mct82krlnpnxvl7v3sxmrv3f",
|
|
privateKey:
|
|
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
|
|
publicKey:
|
|
"addr1qxck6ztj8lrxd0j2jz8f7tznzfu9wqv9qrplrh3r9eq8g9n0n3anjy2a4x54kd2sort3qvnc7mct82krlnpnxvl7v3sxmrv3f",
|
|
numberTransactions: "35",
|
|
transactions: 1,
|
|
txs: [],
|
|
height: 1204,
|
|
timerstamp: dayjs().format("HH:mm:ss"),
|
|
balanceToFixed: 0,
|
|
},
|
|
])
|
|
);
|
|
|
|
// 初始化 WebSocket
|
|
await initWebSocket();
|
|
|
|
// 设置事件总线
|
|
setupEventBusListeners();
|
|
|
|
// 启动区块链数据轮询
|
|
startBlockchainPolling();
|
|
};
|
|
|
|
// 清理所有资源
|
|
const cleanupServices = () => {
|
|
// 清理区块链数据轮询
|
|
if (blockchainTimerRef.current) {
|
|
clearInterval(blockchainTimerRef.current);
|
|
blockchainTimerRef.current = null;
|
|
}
|
|
|
|
// 清理事件总线
|
|
cleanupEventBusListeners();
|
|
|
|
// 关闭 WebSocket 连接
|
|
if (eventsWsRef.current) {
|
|
eventsWsRef.current.disconnect();
|
|
eventsWsRef.current = null;
|
|
}
|
|
|
|
wsInitializedRef.current = false;
|
|
};
|
|
|
|
useEffect(() => {
|
|
// 延迟初始化以确保组件完全挂载
|
|
const initTimer = setTimeout(() => {
|
|
initializeServices();
|
|
}, 1000);
|
|
|
|
// 组件卸载时清理资源
|
|
return () => {
|
|
clearTimeout(initTimer);
|
|
cleanupServices();
|
|
console.log("App component unmounted, all services cleaned up");
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<main>
|
|
<Titlebar />
|
|
<Layout />
|
|
<Tray />
|
|
</main>
|
|
);
|
|
}
|
|
|
|
export default App;
|