feat: 新增131nodes 接口数据转 proxies 格式

This commit is contained in:
liyuanhu 2025-04-18 19:21:52 +08:00
parent ffabc1ccbe
commit d220b7b0f5
12 changed files with 274 additions and 222 deletions

4
.env
View File

@ -1,5 +1,5 @@
GRPC_ENDPOINT="156.229.167.121:9090" GRPC_ENDPOINT="10.66.66.234:9090"
COSMOS_ENDPOINT="http://156.229.167.121:26657" COSMOS_ENDPOINT="http://10.66.66.234:26657"
NODE_SECRET="aHVnZSBjb21wYW55IHBob25lIHdlc3QgcGxhY2Ugc2VtaW5hciBtaXJhY2xlIGxlbmQgbWFuZGF0ZSB0aGVuIGFkanVzdCBxdWl0IG1lYXQgY2hlYXAgbm9vZGxlIGNvdXBsZSBkZWZpbmUgbXVzY2xlIHB1bHNlIHNpc3RlciBwaWVjZSBkZXZpY2UgcHJpdmF0ZSBob29k" NODE_SECRET="aHVnZSBjb21wYW55IHBob25lIHdlc3QgcGxhY2Ugc2VtaW5hciBtaXJhY2xlIGxlbmQgbWFuZGF0ZSB0aGVuIGFkanVzdCBxdWl0IG1lYXQgY2hlYXAgbm9vZGxlIGNvdXBsZSBkZWZpbmUgbXVzY2xlIHB1bHNlIHNpc3RlciBwaWVjZSBkZXZpY2UgcHJpdmF0ZSBob29k"
IS_DEBUG="true" IS_DEBUG="true"
ACCOUNT_NAME="de1" ACCOUNT_NAME="de1"

View File

@ -1,64 +0,0 @@
let __unconfig_data;
let __unconfig_stub = function (data = {}) { __unconfig_data = data };
__unconfig_stub.default = (data = {}) => { __unconfig_data = data };
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import AutoImport from 'unplugin-auto-import/vite'
import svgr from 'vite-plugin-svgr'
import { CodeInspectorPlugin } from 'code-inspector-plugin'
import path from 'path'
// const host = process.env.TAURI_DEV_HOST;
const host = '127.0.0.1'
// https://vitejs.dev/config/
const __unconfig_default = defineConfig(async () => ({
plugins: [
react(),
AutoImport({
dts: './auto-imports.d.ts', //此文件配置保存后系统自动生成
imports: [
'react', // 自动导入 React
],
}),
svgr({ include: '**/*.svg?react' }),
CodeInspectorPlugin({
bundler: 'vite',
}),
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'package.json': path.resolve(__dirname, './package.json'),
},
},
build: {
sourcemap: true,
},
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
//
// 1. prevent vite from obscuring rust errors
clearScreen: false,
// 2. tauri expects a fixed port, fail if that port is not available
server: {
port: 1420,
strictPort: true,
host: host,
hmr: host
? {
protocol: 'ws',
host,
port: 1421,
}
: undefined,
watch: {
// 3. tell vite to ignore watching `src-tauri`
ignored: ['**/src-tauri/**'],
},
},
}))
if (typeof __unconfig_default === "function") __unconfig_default(...[{"command":"serve","mode":"development"}]);export default __unconfig_data;

View File

@ -9,6 +9,8 @@ import {
setProxiesList1, setProxiesList1,
setProxiesList2, setProxiesList2,
setProxiesLine, setProxiesLine,
setMaliciousNodeList,
setNodeDownList,
} from "@/store/web3Slice"; } from "@/store/web3Slice";
import type { AppDispatch, RootState } from "@/store"; import type { AppDispatch, RootState } from "@/store";
@ -32,9 +34,9 @@ function App() {
const { loadCoreConfig } = useCoreConfig(); const { loadCoreConfig } = useCoreConfig();
loadCoreConfig(); loadCoreConfig();
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const { web3List, web3List2, proxy_info, path_list } = useSelector( // const { } = useSelector(
(state: RootState) => state.web3Reducer // (state: RootState) => state.web3Reducer
); // );
const webSocketClient = useRef<WebSocketClient | null>(); const webSocketClient = useRef<WebSocketClient | null>();
const openWsTraffic = async () => { const openWsTraffic = async () => {
@ -60,10 +62,12 @@ function App() {
break; break;
case eventTypes.NODE_DOWN: case eventTypes.NODE_DOWN:
// 添加下线节点到store 里面 // 添加下线节点到store 里面
dispatch(setNodeDownList(msg.data.name));
console.log("节点下线"); console.log("节点下线");
break; break;
case eventTypes.MALICIOUS_NODE: case eventTypes.MALICIOUS_NODE:
// 添加恶意节点到store 里面 // 添加恶意节点到store 里面
dispatch(setMaliciousNodeList(msg.data.name));
console.log("检测到恶意节点"); console.log("检测到恶意节点");
break; break;
case eventTypes.NODE_INIT_COMPLATE: case eventTypes.NODE_INIT_COMPLATE:
@ -90,13 +94,13 @@ function App() {
console.log("节点预配置完成", data); console.log("节点预配置完成", data);
webSocketClient.current?.sendMessage(data); webSocketClient.current?.sendMessage(data);
}); });
eventBus.on(eventTypes.NODE_REMOVE, () => { eventBus.on(eventTypes.NODE_REMOVE, (data: any) => {
console.log("节点清除"); console.log("节点清除");
webSocketClient.current?.sendMessage({ webSocketClient.current?.sendMessage({
code: 0, code: 0,
event: eventTypes.NODE_REMOVE, event: eventTypes.NODE_REMOVE,
data: { data: {
name: "proxy-xxx", name: data,
}, },
}); });
}); });

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

View File

@ -1,21 +1,21 @@
import { useEffect } from 'react' import { useEffect } from "react";
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from "react-redux";
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from "uuid";
import { commands, ProxyNodeInfo } from '@/bindings' import { commands, ProxyNodeInfo } from "@/bindings";
import { app } from '@tauri-apps/api' import { app } from "@tauri-apps/api";
import { createCircuit } from '@/store/circuitSlice' import { createCircuit } from "@/store/circuitSlice";
import { setServiceStatus, getProxy, getVersion } from '@/store/serviceSlice' import { setServiceStatus, getProxy, getVersion } from "@/store/serviceSlice";
import { setNodes, setNodesError, clearNodes } from '@/store/nodesSlice' import { setNodes, setNodesError, clearNodes } from "@/store/nodesSlice";
// import {} from '@/store/web3Slice' import { setProxiesList1 } from "@/store/web3Slice";
// import { clearCircuitState } from '@/store/circuitSlice'; // import { clearCircuitState } from '@/store/circuitSlice';
import { AppDispatch, RootState } from '@/store' import { AppDispatch, RootState } from "@/store";
interface StartupCheckResult { interface StartupCheckResult {
success: boolean success: boolean;
error?: string error?: string;
data?: ProxyNodeInfo[] data?: ProxyNodeInfo[];
} }
// 重试函数:最多重试指定次数,每次等待指定时间 // 重试函数:最多重试指定次数,每次等待指定时间
@ -23,27 +23,27 @@ async function retry<T>(
operation: () => Promise<T>, operation: () => Promise<T>,
maxAttempts: number, maxAttempts: number,
delayMs: number, delayMs: number,
validate: (result: T) => boolean, validate: (result: T) => boolean
): Promise<T> { ): Promise<T> {
let lastError: Error | undefined let lastError: Error | undefined;
for (let attempt = 1; attempt <= maxAttempts; attempt++) { for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try { try {
const result = await operation() const result = await operation();
if (validate(result)) { if (validate(result)) {
return result return result;
} }
lastError = new Error(`Validation failed on attempt ${attempt}`) lastError = new Error(`Validation failed on attempt ${attempt}`);
} catch (error) { } catch (error) {
lastError = error instanceof Error ? error : new Error(String(error)) lastError = error instanceof Error ? error : new Error(String(error));
} }
if (attempt < maxAttempts) { if (attempt < maxAttempts) {
await new Promise((resolve) => setTimeout(resolve, delayMs)) await new Promise((resolve) => setTimeout(resolve, delayMs));
} }
} }
throw lastError || new Error('Operation failed after all attempts') throw lastError || new Error("Operation failed after all attempts");
} }
// 正常来说windows下和linux下使用包管理器更新时安装/卸载服务是足够的只有macOS下依赖启动检查来安装/卸载服务 // 正常来说windows下和linux下使用包管理器更新时安装/卸载服务是足够的只有macOS下依赖启动检查来安装/卸载服务
@ -51,44 +51,44 @@ async function retry<T>(
async function checkAndInstallService(): Promise<StartupCheckResult> { async function checkAndInstallService(): Promise<StartupCheckResult> {
try { try {
// 获取当前服务版本和期望版本 // 获取当前服务版本和期望版本
const versionResult = await commands.getServiceVersion() const versionResult = await commands.getServiceVersion();
const expectedVersion = await app.getVersion() const expectedVersion = await app.getVersion();
// 需要安装/重装的两种情况,分别是 // 需要安装/重装的两种情况,分别是
// 1. 服务不存在 // 1. 服务不存在
// 2. 服务存在但版本不匹配 // 2. 服务存在但版本不匹配
const serviceNotExists = versionResult.status === 'error' const serviceNotExists = versionResult.status === "error";
const versionMismatch = const versionMismatch =
!serviceNotExists && versionResult.data !== expectedVersion !serviceNotExists && versionResult.data !== expectedVersion;
if (serviceNotExists) { if (serviceNotExists) {
console.log('Service not found, installing...') console.log("Service not found, installing...");
const installResult = await commands.installService() const installResult = await commands.installService();
if (installResult.status === 'error') { if (installResult.status === "error") {
return { return {
success: false, success: false,
error: 'Failed to install service', error: "Failed to install service",
} };
} }
} else if (versionMismatch) { } else if (versionMismatch) {
console.log('Service version mismatch, uninstalling old service...') console.log("Service version mismatch, uninstalling old service...");
const uninstallResult = await commands.uninstallService() const uninstallResult = await commands.uninstallService();
if (uninstallResult.status === 'error') { if (uninstallResult.status === "error") {
return { return {
success: false, success: false,
error: 'Failed to uninstall old service', error: "Failed to uninstall old service",
} };
} }
await new Promise((resolve) => setTimeout(resolve, 5000)) await new Promise((resolve) => setTimeout(resolve, 5000));
console.log('Installing new service...') console.log("Installing new service...");
const installResult = await commands.installService() const installResult = await commands.installService();
if (installResult.status === 'error') { if (installResult.status === "error") {
return { return {
success: false, success: false,
error: 'Failed to install service', error: "Failed to install service",
} };
} }
} }
@ -101,41 +101,41 @@ async function checkAndInstallService(): Promise<StartupCheckResult> {
() => commands.getServiceVersion(), () => commands.getServiceVersion(),
10, 10,
1000, 1000,
(result) => result.status === 'ok' && result.data === expectedVersion, (result) => result.status === "ok" && result.data === expectedVersion
) );
} catch (error) { } catch (error) {
return { return {
success: false, success: false,
error: 'Service installation verification failed after retries', error: "Service installation verification failed after retries",
} };
} }
} }
return { success: true } return { success: true };
} catch (error) { } catch (error) {
return { return {
success: false, success: false,
error: error:
error instanceof Error error instanceof Error
? error.message ? error.message
: 'Unknown error during service check', : "Unknown error during service check",
} };
} }
} }
async function checkAndStartCore(): Promise<StartupCheckResult> { async function checkAndStartCore(): Promise<StartupCheckResult> {
try { try {
// 检查核心状态 // 检查核心状态
let coreStatus = await commands.getNodes() let coreStatus = await commands.getNodes();
// 如果核心未运行,尝试启动 // 如果核心未运行,尝试启动
if (coreStatus.status === 'error') { if (coreStatus.status === "error") {
console.log('Starting core...') console.log("Starting core...");
const startResult = await commands.restartCore() const startResult = await commands.restartCore();
if (startResult.status === 'error') { if (startResult.status === "error") {
return { return {
success: false, success: false,
error: 'Failed to start core', error: "Failed to start core",
} };
} }
// 等待并验证核心是否成功启动 // 等待并验证核心是否成功启动
@ -145,166 +145,177 @@ async function checkAndStartCore(): Promise<StartupCheckResult> {
() => commands.getNodes(), () => commands.getNodes(),
10, 10,
500, 500,
(result) => result.status === 'ok', (result) => result.status === "ok"
) );
} catch (error) { } catch (error) {
return { return {
success: false, success: false,
error: 'Core start verification failed after retries', error: "Core start verification failed after retries",
} };
} }
} }
return { return {
success: true, success: true,
data: coreStatus.status === 'ok' ? coreStatus.data : undefined, data: coreStatus.status === "ok" ? coreStatus.data : undefined,
} };
} catch (error) { } catch (error) {
return { return {
success: false, success: false,
error: error:
error instanceof Error error instanceof Error
? error.message ? error.message
: 'Unknown error during core check', : "Unknown error during core check",
} };
} }
} }
export function useStartupCheck() { export function useStartupCheck() {
const dispatch: AppDispatch = useDispatch() const dispatch: AppDispatch = useDispatch();
const { fallbackCircuit } = useSelector( const { fallbackCircuit } = useSelector(
(state: RootState) => state.circuitReducer, (state: RootState) => state.circuitReducer
) );
// 检查服务和核心状态,同时更新节点信息 // 检查服务和核心状态,同时更新节点信息
const checkStatus = async () => { const checkStatus = async () => {
try { try {
const [versionResult, nodesResult] = await Promise.all([ const [versionResult, nodesResult] = await Promise.all([
commands.getServiceVersion(), commands.getServiceVersion(),
commands.getNodes(), commands.getNodes(),
]) ]);
const suitableServiceVersion = await app.getVersion() const suitableServiceVersion = await app.getVersion();
const isServiceInstalled = const isServiceInstalled =
versionResult.status === 'ok' && versionResult.status === "ok" &&
versionResult.data === suitableServiceVersion versionResult.data === suitableServiceVersion;
const isCoreRunning = nodesResult.status === 'ok' const isCoreRunning = nodesResult.status === "ok";
// 更新服务状态 // 更新服务状态
dispatch( dispatch(
setServiceStatus({ setServiceStatus({
isServiceInstalled, isServiceInstalled,
isCoreRunning, isCoreRunning,
}), })
) );
// 更新节点信息 // 更新节点信息
// 只有当核心正在运行时才更新节点 // 只有当核心正在运行时才更新节点
if (isCoreRunning) { if (isCoreRunning) {
if (nodesResult.status === 'ok') { if (nodesResult.status === "ok") {
dispatch(setNodes(nodesResult.data)) console.log("触发了???");
const proxies:any[] = [];
nodesResult.data.forEach((node,index) => {
const { country_code } = node
if(nodesResult.data.length-1 === index){
return
}
proxies.push({ country_code: country_code,ingress_country_code: nodesResult.data[index + 1].country_code })
})
console.log(proxies,'proxiesproxiesproxies')
// dispatch(setProxiesList1())
dispatch(setNodes(nodesResult.data));
} else { } else {
dispatch(setNodesError('Failed to fetch nodes')) dispatch(setNodesError("Failed to fetch nodes"));
} }
} else { } else {
dispatch(clearNodes()) dispatch(clearNodes());
// dispatch(clearCircuitState()); // 当核心未运行时清空链路状态 // dispatch(clearCircuitState()); // 当核心未运行时清空链路状态
} }
} catch (error) { } catch (error) {
console.error('Status check failed:', error) console.error("Status check failed:", error);
dispatch( dispatch(
setServiceStatus({ setServiceStatus({
isServiceInstalled: false, isServiceInstalled: false,
isCoreRunning: false, isCoreRunning: false,
}), })
) );
dispatch(clearNodes()) dispatch(clearNodes());
} }
} };
useEffect(() => { useEffect(() => {
async function performStartupCheck() { async function performStartupCheck() {
try { try {
// 步骤1检查并安装服务 // 步骤1检查并安装服务
const serviceResult = await checkAndInstallService() const serviceResult = await checkAndInstallService();
if (!serviceResult.success) { if (!serviceResult.success) {
console.error('Service check failed:', serviceResult.error) console.error("Service check failed:", serviceResult.error);
await dispatch( await dispatch(
setServiceStatus({ setServiceStatus({
isServiceInstalled: false, isServiceInstalled: false,
isCoreRunning: false, isCoreRunning: false,
}), })
) );
await dispatch(clearNodes()) await dispatch(clearNodes());
return return;
} }
// 步骤2检查并启动核心 // 步骤2检查并启动核心
const coreResult = await checkAndStartCore() const coreResult = await checkAndStartCore();
if (!coreResult.success) { if (!coreResult.success) {
console.error('Core check failed:', coreResult.error) console.error("Core check failed:", coreResult.error);
await dispatch( await dispatch(
setServiceStatus({ setServiceStatus({
isServiceInstalled: true, isServiceInstalled: true,
isCoreRunning: false, isCoreRunning: false,
}), })
) );
await dispatch(clearNodes()) await dispatch(clearNodes());
return return;
} }
// 步骤3检查当前核心版本 // 步骤3检查当前核心版本
await dispatch(getVersion()) await dispatch(getVersion());
// 步骤4检查当前是否开启了代理 // 步骤4检查当前是否开启了代理
await dispatch(getProxy()) await dispatch(getProxy());
// 步骤5检查当前是否有默认链路并且保证状态是正常否则重新创建一个默认链路 // 步骤5检查当前是否有默认链路并且保证状态是正常否则重新创建一个默认链路
if (!fallbackCircuit || fallbackCircuit?.state === 'failed') { if (!fallbackCircuit || fallbackCircuit?.state === "failed") {
let inbound = '' let inbound = "";
let outbound = '' let outbound = "";
if (coreResult && coreResult.data && coreResult.data.length > 0) { if (coreResult && coreResult.data && coreResult.data.length > 0) {
inbound = coreResult.data[0].country_code || '' inbound = coreResult.data[0].country_code || "";
outbound = outbound =
coreResult.data coreResult.data
.filter((item) => item.exit && item.country_code !== inbound) .filter((item) => item.exit && item.country_code !== inbound)
?.slice(-1)?.[0].country_code || '' ?.slice(-1)?.[0].country_code || "";
// 如果没有这两个那么就跳过,证明核心运行了但是节点是空的,如果只有其中一个,那么证明总共节点就一个无法创建链路 // 如果没有这两个那么就跳过,证明核心运行了但是节点是空的,如果只有其中一个,那么证明总共节点就一个无法创建链路
if (inbound && outbound) { if (inbound && outbound) {
await dispatch( await dispatch(
createCircuit({ createCircuit({
uid: uuidv4(), uid: uuidv4(),
name: '系统默认链路', name: "系统默认链路",
inbound: inbound, inbound: inbound,
outbound: outbound, outbound: outbound,
multi_hop: 3, multi_hop: 3,
fallback: true, fallback: true,
rule_path: null, rule_path: null,
is_prefix: false, is_prefix: false,
}), })
).unwrap() ).unwrap();
} }
} }
} }
// //
// 执行一次完整的状态检查 // 执行一次完整的状态检查
await checkStatus() await checkStatus();
} catch (error) { } catch (error) {
console.error('Startup check failed:', error) console.error("Startup check failed:", error);
dispatch( dispatch(
setServiceStatus({ setServiceStatus({
isServiceInstalled: false, isServiceInstalled: false,
isCoreRunning: false, isCoreRunning: false,
}), })
) );
dispatch(clearNodes()) dispatch(clearNodes());
} }
} }
// 执行启动自检 // 执行启动自检
performStartupCheck() performStartupCheck();
// 每5秒检查一次状态 // 每5秒检查一次状态
const intervalId = setInterval(checkStatus, 5000) const intervalId = setInterval(checkStatus, 5000);
return () => clearInterval(intervalId) return () => clearInterval(intervalId);
}, []) }, []);
} }

View File

@ -93,7 +93,7 @@
outline-offset: -0.46px; outline-offset: -0.46px;
backdrop-filter: blur(5.50px); backdrop-filter: blur(5.50px);
.close-icon { .close-icon , .close-icon2 {
width: 16px; width: 16px;
height: 16px; height: 16px;
position: absolute; position: absolute;

View File

@ -10,8 +10,10 @@ import NotFailNodeIcon from "@/assets/svg/common/not-fail-node.svg?react";
import NotWarningNodeIcon from "@/assets/svg/common/not-warning-node.svg?react"; import NotWarningNodeIcon from "@/assets/svg/common/not-warning-node.svg?react";
import { NODEDIALOGTYPE } from "../../index"; import { NODEDIALOGTYPE } from "../../index";
import { cn, getUrl } from "@/lib/utils"; import { cn, getUrl } from "@/lib/utils";
import eventBus, { eventTypes } from "@/utils/eventBus";
import { AppDispatch, RootState } from "@/store"; import { AppDispatch, RootState } from "@/store";
import {removeMaliciousNodeList,removeNodeDownList} from '@/store/web3Slice'
import { isTimestampPlusTenMinutesBeforeNow } from "@/utils/tools"; import { isTimestampPlusTenMinutesBeforeNow } from "@/utils/tools";
export interface DialogConfig { export interface DialogConfig {
@ -69,7 +71,9 @@ export const ClearNodeDialog = ({
type: DialogConfig; type: DialogConfig;
}) => { }) => {
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const {} = useSelector((state: RootState) => state.web3Reducer); const { maliciousNodeList, nodeDownList } = useSelector(
(state: RootState) => state.web3Reducer
);
const [isClear, setIsClear] = useState(false); const [isClear, setIsClear] = useState(false);
const showDialog = (open: boolean) => { const showDialog = (open: boolean) => {
setOpen(open); setOpen(open);
@ -78,15 +82,26 @@ export const ClearNodeDialog = ({
successHandle(); successHandle();
setIsClear(true); setIsClear(true);
if (type.title === NODEDIALOGTYPE.ClearFailNode.title) { 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) { } else if (type.title === NODEDIALOGTYPE.ClearWargingNode.title) {
maliciousNodeList.forEach((item)=>{
dispatch(removeMaliciousNodeList(item.name));
eventBus.emit(eventTypes.NODE_REMOVE, item.name);
})
} }
// showDialog(false); // showDialog(false);
}; };
const proxyList = useMemo(() => { const proxyList = useMemo(() => {
// 随机 2-9条 nodelist里面的数据 if (type.title === NODEDIALOGTYPE.ClearFailNode.title) {
return []; return nodeDownList;
}, [nodeList, open, isClear, type]); } else {
return maliciousNodeList;
}
}, [nodeDownList, maliciousNodeList, type.title]);
useEffect(() => { useEffect(() => {
if (!open) { if (!open) {
@ -112,7 +127,7 @@ export const ClearNodeDialog = ({
} }
> >
<div className="flex flex-wrap gap-3"> <div className="flex flex-wrap gap-3">
{proxyList.length > 0 ? ( {proxyList?.length > 0 ? (
proxyList.map((item) => { proxyList.map((item) => {
return <ProxyItem proxyInfo={item} key={item.name} />; return <ProxyItem proxyInfo={item} key={item.name} />;
}) })

View File

@ -51,7 +51,7 @@ export const NODEDIALOGTYPE = {
}; };
const DecentralizedElasticNetwork = () => { const DecentralizedElasticNetwork = () => {
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const { web3List, web3List2, proxy_info, path_list } = useSelector( const { web3List, web3List2, proxy_info, path_list, } = useSelector(
(state: RootState) => state.web3Reducer (state: RootState) => state.web3Reducer
); );

View File

@ -181,20 +181,20 @@ function Home() {
} }
} }
// 如果代理未启用且链路未就绪,创建默认链路 // 如果代理未启用且链路未就绪,创建默认链路
if (!isProxyEnabled && !isCircuitReady) { // if (!isProxyEnabled && !isCircuitReady) {
await dispatch( // await dispatch(
createCircuit({ // createCircuit({
uid: uuidv4(), // uid: uuidv4(),
name: '系统默认链路', // name: '系统默认链路',
inbound: countries[0], // inbound: countries[0],
outbound: exitCountries[exitCountries.length - 1], // outbound: exitCountries[exitCountries.length - 1],
multi_hop: 3, // multi_hop: 3,
fallback: true, // fallback: true,
rule_path: null, // rule_path: null,
is_prefix: false, // is_prefix: false,
}), // }),
).unwrap() // ).unwrap()
} // }
setIsProxyLoading(true) setIsProxyLoading(true)

View File

@ -106,6 +106,7 @@ export const WorldGeo = memo(
}, [dataInfo, selectedApp]); }, [dataInfo, selectedApp]);
// 创建自定义提示框DOM元素 // 创建自定义提示框DOM元素
const createCustomTooltip = () => { const createCustomTooltip = () => {
console.log("createCustomTooltip")
// 如果已经存在自定义提示框,则移除它 // 如果已经存在自定义提示框,则移除它
if (document.getElementById("custom-fixed-tooltip")) { if (document.getElementById("custom-fixed-tooltip")) {
document.getElementById("custom-fixed-tooltip")?.remove(); document.getElementById("custom-fixed-tooltip")?.remove();
@ -206,7 +207,7 @@ export const WorldGeo = memo(
<div class="tip-box"> <div class="tip-box">
<div> <div>
<div class="label" style="color: white; font-weight: bold;"></div> <div class="label" style="color: white; font-weight: bold;"></div>
<img class="close-icon" src="${getUrl( <img class="close-icon2" src="${getUrl(
"svg/Xwhite.svg" "svg/Xwhite.svg"
)}" alt="" )}" alt=""
style="cursor: pointer; " /> style="cursor: pointer; " />
@ -224,11 +225,11 @@ export const WorldGeo = memo(
document.body.appendChild(tooltip); document.body.appendChild(tooltip);
customTooltip2Ref.current = tooltip; customTooltip2Ref.current = tooltip;
// 添加关闭按钮事件 // 添加关闭按钮事件
const closeButton = tooltip.querySelector(".close-icon"); const closeButton = tooltip.querySelector(".close-icon2");
if (closeButton) { if (closeButton) {
closeButton.addEventListener("click", () => { closeButton.addEventListener("click", () => {
setTooltipClosed(false); setTooltipClosed(false);
tooltip.remove(); customTooltip2Ref.current?.remove();
customTooltip2Ref.current = null; customTooltip2Ref.current = null;
}); });
} }
@ -1142,11 +1143,6 @@ export const WorldGeo = memo(
if (tooltipClosed) { if (tooltipClosed) {
createCustomTooltip(); createCustomTooltip();
createCustomTooltip2(); createCustomTooltip2();
} else {
customTooltipRef.current?.remove();
customTooltip2Ref.current?.remove();
customTooltipRef.current = null;
customTooltip2Ref.current = null;
} }
return () => { return () => {
customTooltipRef.current?.remove(); customTooltipRef.current?.remove();

View File

@ -7,11 +7,13 @@ import { WorldGeo } from "./components/world-geo";
import Web3BoxPng from "@/assets/image/home/web3-box.png"; import Web3BoxPng from "@/assets/image/home/web3-box.png";
import Web3Box2Png from "@/assets/image/home/web3-box2.png"; import Web3Box2Png from "@/assets/image/home/web3-box2.png";
import OpenProxyPng from "@/assets/image/home/open-proxy.png"; import OpenProxyPng from "@/assets/image/home/open-proxy.png";
import CloseProxyPng from "@/assets/image/home/close-proxy.png";
import web3BoxGif from "@/assets/gif/web3-box-bg.gif"; import web3BoxGif from "@/assets/gif/web3-box-bg.gif";
import VectorSlideSvg from "@/assets/svg/home/vector-solide.svg?react"; import VectorSlideSvg from "@/assets/svg/home/vector-solide.svg?react";
import { Apps, CONST_TOOLTIP_TYPE } from "@/pages/anti-forensics-forwarding"; import { Apps, CONST_TOOLTIP_TYPE } from "@/pages/anti-forensics-forwarding";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { commands } from "@/bindings";
import { import {
setProxyInfoProxies, setProxyInfoProxies,
@ -30,6 +32,9 @@ import {
getApplicationDiversion, getApplicationDiversion,
} from "@/api/flying-line"; } from "@/api/flying-line";
import { blockChainApi } from "@/api/block"; import { blockChainApi } from "@/api/block";
import { errorToast } from "@/components/GlobalToast";
import { toast } from "@/components/ui/use-toast";
import { disableProxy, enableProxy } from "@/store/serviceSlice";
export const DIALOGTYPE = { export const DIALOGTYPE = {
ADDNode: { ADDNode: {
@ -61,7 +66,10 @@ const NewHome = () => {
const { web3List, web3List2 } = useSelector( const { web3List, web3List2 } = useSelector(
(state: RootState) => state.web3Reducer (state: RootState) => state.web3Reducer
); );
const { isProxyEnabled, isCoreRunning } = useSelector(
(state: RootState) => state.serviceReducer
);
const [isProxyLoading, setIsProxyLoading] = useState(false);
const [form] = Form.useForm(); const [form] = Form.useForm();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [openNode, setOpenNode] = useState(false); const [openNode, setOpenNode] = useState(false);
@ -226,6 +234,45 @@ const NewHome = () => {
}); });
}, [dataInfo.applicationDiversion]); }, [dataInfo.applicationDiversion]);
// 处理代理开关
const handleProxyToggle = async (
isProxyEnabled: boolean,
isCoreRunning: boolean
) => {
if (isProxyLoading) return;
try {
// 如果核心未运行,先启动核心
if (!isCoreRunning) {
await commands.startCore();
}
// 这里要先轮询去判断是否启动成功,如果启动失败,则抛出异常
let isGetNodesResult = await commands.getNodes();
let count = 0;
while (isGetNodesResult.status !== "ok") {
isGetNodesResult = await commands.getNodes();
count++;
// 如果50次都没有开启成功核心那么启动失败
if (count > 50) {
throw new Error("启动核心失败");
}
}
setIsProxyLoading(true);
// 切换代理状态
await dispatch(isProxyEnabled ? disableProxy() : enableProxy()).unwrap();
} catch (error) {
console.log(error, "error");
const errorMessage = isProxyEnabled
? "关闭代理失败!"
: "开启代理失败,请检查节点配置、或重新尝试开启";
errorToast(errorMessage, toast);
console.error("Proxy toggle failed:", error);
} finally {
setIsProxyLoading(false);
}
};
const initData = async () => { const initData = async () => {
const passAuthentication = await getPassAuthentication(); const passAuthentication = await getPassAuthentication();
const trafficObfuscation = await getTrafficObfuscation(); const trafficObfuscation = await getTrafficObfuscation();
@ -365,9 +412,12 @@ const NewHome = () => {
</div> </div>
<div className="absolute bottom-[10px] left-[50%] translate-x-[-50%] w-[calc(100%-51px)] p-6 inline-flex justify-start items-center gap-10"> <div className="absolute bottom-[10px] left-[50%] translate-x-[-50%] w-[calc(100%-51px)] p-6 inline-flex justify-start items-center gap-10">
<img <img
src={OpenProxyPng} src={isProxyEnabled ? CloseProxyPng : OpenProxyPng}
className="w-[193px] h-[90px] cursor-pointer" className="w-[193px] h-[90px] cursor-pointer"
alt="" alt=""
onClick={() => {
handleProxyToggle(isProxyEnabled, isCoreRunning);
}}
/> />
{/* <div {/* <div
className="bt1 cursor-pointer" className="bt1 cursor-pointer"

View File

@ -24,6 +24,8 @@ interface Iweb3Slice {
path_list: any; path_list: any;
proxy_info: any; proxy_info: any;
isLine: boolean; isLine: boolean;
maliciousNodeList: any[]; // 恶意节点
nodeDownList: any[]; // 节点下线
} }
// 随机生成 0-100 之间的数字,保留一位小数或整数 // 随机生成 0-100 之间的数字,保留一位小数或整数
@ -147,21 +149,56 @@ const initialState: Iweb3Slice = {
name: "newHomeProxies", name: "newHomeProxies",
}, },
], ],
maliciousNodeList: [], // 恶意节点
nodeDownList: [], // 节点下线
}; };
export const appSlice = createSlice({ export const appSlice = createSlice({
name: "web3", name: "web3",
initialState, initialState,
reducers: { reducers: {
setProxiesList1: (state) => { removeMaliciousNodeList: (state, action) => {
state.maliciousNodeList = state.maliciousNodeList.filter(
(item) => item !== action.payload
);
},
removeNodeDownList: (state, action) => {
state.nodeDownList = state.nodeDownList.filter(
(item) => item !== action.payload
);
},
setMaliciousNodeList: (state, action) => {
// 判断当前节点是否已经存在
const maliciousNode = state.maliciousNodeList.find(
(item) => action.payload === item
);
if (!maliciousNode) {
state.maliciousNodeList.push(action.payload);
}
},
setNodeDownList: (state, action) => {
// 判断当前节点是否已经存在
const nodeDown = state.nodeDownList.find(
(item) => action.payload === item
);
if (!nodeDown) {
state.nodeDownList.push(action.payload);
}
},
setProxiesList1: (state, action) => {
// state.proxy_info.prox // state.proxy_info.prox
// 判断是否已经存在 // 判断是否已经存在
const proxies = state.proxy_info.proxies.find( const proxy_info = state.proxy_info;
(item: any) => item.name === "data1" if (proxy_info.proxies.length === 0) {
); proxy_info.proxies.push({
if (!proxies) { name: "data1",
state.proxy_info.proxies.push(data1); isLine: false,
data: action.payload,
});
} else {
proxy_info.proxies[0] = action.payload;
} }
state.proxy_info = proxy_info;
}, },
setProxiesList2: (state) => { setProxiesList2: (state) => {
// state.proxy_info.prox // state.proxy_info.prox
@ -233,7 +270,6 @@ export const appSlice = createSlice({
// 更新状态 // 更新状态
state.web3List = [...state.web3List, ...newWallets]; state.web3List = [...state.web3List, ...newWallets];
console.log(state.web3List, "state.web3List");
} }
}, },
setIsLine: (state, action) => { setIsLine: (state, action) => {
@ -265,6 +301,10 @@ export const appSlice = createSlice({
}); });
export const { export const {
removeMaliciousNodeList,
removeNodeDownList,
setMaliciousNodeList,
setNodeDownList,
setWeb3List, setWeb3List,
setWeb3List2, setWeb3List2,
setProxyInfoProxies, setProxyInfoProxies,