368 lines
13 KiB
TypeScript
368 lines
13 KiB
TypeScript
import { useState } from "react";
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
import { WorldGeo } from "./components/world-geo";
|
|
import Web3BoxPng from "@/assets/image/home/web3-box.png";
|
|
import Web3Box2Png from "@/assets/image/home/web3-box2.png";
|
|
import web3BoxGif from "@/assets/gif/web3-box-bg.gif";
|
|
import VectorSlideSvg from "@/assets/svg/home/vector-solide.svg?react";
|
|
|
|
import { Apps, CONST_TOOLTIP_TYPE } from "@/pages/anti-forensics-forwarding";
|
|
import { cn } from "@/lib/utils";
|
|
import { commands } from "@/bindings";
|
|
|
|
import type { AppDispatch, RootState } from "@/store";
|
|
import "./index.scss";
|
|
import {
|
|
getPassAuthentication,
|
|
getTrafficObfuscation,
|
|
getNestedEncryption,
|
|
getDynamicRouteGeneration,
|
|
getApplicationDiversion,
|
|
} from "@/api/flying-line";
|
|
import { errorToast } from "@/components/GlobalToast";
|
|
import { toast } from "@/components/ui/use-toast";
|
|
import { disableProxy, enableProxy } from "@/store/serviceSlice";
|
|
|
|
export const DIALOGTYPE = {
|
|
ADDNode: {
|
|
title: "添加节点",
|
|
desc: "",
|
|
successText: "添加",
|
|
},
|
|
AddNetwork: {
|
|
title: "构建网络",
|
|
desc: "",
|
|
successText: "构建",
|
|
},
|
|
};
|
|
|
|
export const NODEDIALOGTYPE = {
|
|
ClearFailNode: {
|
|
title: "清除掉线节点",
|
|
desc: "",
|
|
successText: "清除",
|
|
},
|
|
ClearWargingNode: {
|
|
title: "恶意节点",
|
|
desc: "",
|
|
successText: "清除",
|
|
},
|
|
};
|
|
const NewHome = () => {
|
|
const dispatch = useDispatch<AppDispatch>();
|
|
const { web3List, web3List2 } = useSelector(
|
|
(state: RootState) => state.web3Reducer
|
|
);
|
|
const { isProxyEnabled, isCoreRunning } = useSelector(
|
|
(state: RootState) => state.serviceReducer
|
|
);
|
|
const [isProxyLoading, setIsProxyLoading] = useState(false);
|
|
|
|
const [selectedApp, setSelectedApp] = useState<any>(null);
|
|
|
|
const [tooltipClosed, setTooltipClosed] = useState(true);
|
|
|
|
const [tooltipType, setTooltipType] = useState(
|
|
CONST_TOOLTIP_TYPE.NESTED_ENCRYPTION.type
|
|
);
|
|
|
|
// 模拟日志数据
|
|
const [nestedEncryptionLogs, setNestedEncryptionLogs] = useState<string[]>(
|
|
[]
|
|
);
|
|
|
|
const [trafficObfuscationLogs, setTrafficObfuscationLogs] = useState<
|
|
string[]
|
|
>([]);
|
|
|
|
const newWeb3List = useMemo(() => {
|
|
// 展示最新的6个节点
|
|
return web3List.slice(-6);
|
|
}, [web3List]);
|
|
|
|
const handleClickApp = (item: any) => {
|
|
setSelectedApp(item);
|
|
};
|
|
|
|
const [dataInfo, setDataInfo] = useState<any>({
|
|
passAuthentication: {
|
|
type: "PASS_AUTHENTICATION",
|
|
name: "通信认证",
|
|
startPoint: "GL",
|
|
endPoint: "CA",
|
|
authenticationPoint: [
|
|
[-103.346771, 54.130366],
|
|
[-120.346771, 52.130366],
|
|
[-108.346771, 48.130366],
|
|
[-98.346771, 46.130366],
|
|
[-106.346771, 48.450366],
|
|
[-101.346771, 53.130366],
|
|
[-123.346771, 58.130366],
|
|
[-111.346771, 65.443366],
|
|
[-108.346771, 54.130366],
|
|
[-116.346771, 59.130366],
|
|
[-97.346771, 61.130366],
|
|
[-95.346771, 63.130366],
|
|
[-113.346771, 58.840366],
|
|
[-99.346771, 59.130366],
|
|
[-102.346771, 68.130366],
|
|
],
|
|
data: [
|
|
{
|
|
country_code: "gl",
|
|
ingress_country_code: "dz",
|
|
},
|
|
{
|
|
country_code: "br",
|
|
ingress_country_code: "dz",
|
|
},
|
|
{
|
|
country_code: "dz",
|
|
ingress_country_code: "ru",
|
|
},
|
|
{
|
|
country_code: "dz",
|
|
ingress_country_code: "cn",
|
|
},
|
|
{
|
|
country_code: "ru",
|
|
ingress_country_code: "za",
|
|
},
|
|
],
|
|
isLine: true,
|
|
},
|
|
trafficObfuscation: [],
|
|
nestedEncryption: [],
|
|
dynamicRouteGeneration: [],
|
|
applicationDiversion: [],
|
|
});
|
|
|
|
const appDiversion = useMemo(() => {
|
|
return Apps.map((item) => {
|
|
const findApp = dataInfo.applicationDiversion.find(
|
|
(appItem: any) => item.name === appItem.name
|
|
);
|
|
return {
|
|
...item,
|
|
...findApp,
|
|
};
|
|
});
|
|
}, [dataInfo.applicationDiversion]);
|
|
|
|
// 处理代理开关
|
|
const handleProxyToggle = useCallback(
|
|
async (isProxyEnabled: boolean, isCoreRunning: boolean) => {
|
|
// console.log(isProxyLoading, "isProxyLoadingisProxyLoading");
|
|
if (isProxyLoading) return;
|
|
try {
|
|
// 如果核心未运行,先启动核心
|
|
if (!isCoreRunning) {
|
|
await commands.startCore();
|
|
}
|
|
|
|
setIsProxyLoading(true);
|
|
// 切换代理状态
|
|
await dispatch(isProxyEnabled ? disableProxy() : enableProxy()).unwrap();
|
|
setIsProxyLoading(false);
|
|
console.log(`Proxy ${isProxyEnabled ? "关闭成功" : "开启成功"}`);
|
|
} catch (error) {
|
|
const errorMessage = isProxyEnabled
|
|
? "关闭代理失败!"
|
|
: "开启代理失败,请检查节点配置、或重新尝试开启";
|
|
errorToast(errorMessage, toast);
|
|
console.error("Proxy toggle failed:", error);
|
|
} finally {
|
|
setIsProxyLoading(false);
|
|
}
|
|
},
|
|
[dispatch, isProxyLoading, isCoreRunning, isProxyEnabled]
|
|
);
|
|
const initData = async () => {
|
|
const passAuthentication = await getPassAuthentication();
|
|
const trafficObfuscation = await getTrafficObfuscation();
|
|
const nestedEncryption = await getNestedEncryption();
|
|
const applicationDiversion = await getApplicationDiversion();
|
|
const dynamicRouteGeneration = await getDynamicRouteGeneration();
|
|
setNestedEncryptionLogs(nestedEncryption.logs);
|
|
setTrafficObfuscationLogs(trafficObfuscation.logs);
|
|
setDataInfo({
|
|
passAuthentication: passAuthentication.data,
|
|
trafficObfuscation: [trafficObfuscation.data],
|
|
nestedEncryption: [nestedEncryption.data],
|
|
applicationDiversion: applicationDiversion.data,
|
|
dynamicRouteGeneration: dynamicRouteGeneration.data,
|
|
});
|
|
};
|
|
useEffect(() => {
|
|
initData();
|
|
}, []);
|
|
|
|
// useEffect(() => {
|
|
// console.log(dataInfo, "awaidataInfodataInfotawait");
|
|
// }, [dataInfo]);
|
|
|
|
return (
|
|
<div className="decentralized w-full h-full flex flex-col relative">
|
|
<div className="box"></div>
|
|
<div className="w-full flex items-center justify-center relative">
|
|
{/* <img
|
|
className="w-[1693px] h-[271px] absolute top-[160px] left-[50%] translate-x-[-50%] z-10"
|
|
src={linePng}
|
|
alt=""
|
|
/> */}
|
|
{/* <div className="w-[90%] absolute top-0 left-0"></div> */}
|
|
<div className="w-[1693px] h-full flex items-center justify-center ">
|
|
<div className="w-[795px] flex items-center justify-end gap-10 z-[99]">
|
|
<div className="carousel-container !justify-end ">
|
|
{newWeb3List.map((item, index) => {
|
|
// 随机0-10的整数
|
|
const randomDelay = Math.floor(Math.random() * 35) * 1;
|
|
return (
|
|
<div
|
|
className="w-[105px] relative carousel-item"
|
|
key={`${item.id}-${index}`}
|
|
style={{
|
|
viewTransitionName: `web3-item-1-${index}`,
|
|
}}
|
|
>
|
|
<div className="w-[calc(100%-10px)] h-[calc(100%-10px)] absolute top-[6px] left-[8px] overflow-hidden">
|
|
<img
|
|
className={cn(
|
|
"!max-w-[186px] h-[160px] relative opacity-50 mix-blend-soft-light z-10"
|
|
)}
|
|
style={{
|
|
left: `${-30 - randomDelay}px`,
|
|
top: `${-30 - randomDelay}px`,
|
|
}}
|
|
src={web3BoxGif}
|
|
/>
|
|
</div>
|
|
<img src={Web3Box2Png} className="w-full h-full" />
|
|
{/* <div className="absolute bottom-[-170px] left-[55px] h-[160px] w-[2px] bg-gradient-to-bl to-[#4820C9]/0 from-[#7D82FF] web3-line"></div> */}
|
|
<div className="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center text-white pl-1.5 z-20">
|
|
{/* <div className="justify-start text-pink-600 text-2xl font-normal font-['Oswald'] absolute top-[-34px] left-[50%] translate-x-[-50%]">
|
|
{item.transactions}
|
|
</div> */}
|
|
{/* <div className="!text-xs">{item?.balanceToFixed} SOL</div> */}
|
|
<div className="!text-xs my-[6px]">#{item.height}</div>
|
|
<div className="!text-xs opacity-60 mb-[6px]">
|
|
{item.timerstamp}
|
|
</div>
|
|
<div className="!text-xs opacity-60">
|
|
{item.txs.length}次记录
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
<div className="w-fit mt-6 mx-[20px] flex-shrink-0">
|
|
<VectorSlideSvg />
|
|
</div>
|
|
<div className="w-[795px] h-full flex items-center justify-start gap-10 ">
|
|
<div className="carousel-container justify-start">
|
|
{web3List2.map((item, index) => {
|
|
const randomDelay = Math.floor(Math.random() * 35) * 1;
|
|
return (
|
|
<div
|
|
key={`${item.id}-${index}`}
|
|
className="w-[105px] relative carousel-item"
|
|
style={{
|
|
viewTransitionName: `web3-item-2-${index}`,
|
|
}}
|
|
>
|
|
<div className="w-[calc(100%-10px)] h-[calc(100%-10px)] absolute top-[6px] left-[8px] overflow-hidden">
|
|
<img
|
|
className="!max-w-[186px] h-[160px] relative left-[-30px] top-[-30px] opacity-50 mix-blend-soft-light z-10"
|
|
src={web3BoxGif}
|
|
/>
|
|
</div>
|
|
<img
|
|
src={Web3BoxPng}
|
|
style={{
|
|
left: `${-30 - randomDelay}px`,
|
|
top: `${-30 - randomDelay}px`,
|
|
}}
|
|
className="w-full h-full"
|
|
/>
|
|
<div className="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center text-white pl-1.5">
|
|
<div className="!text-xs my-[6px]">#{item.height}</div>
|
|
<div className="!text-xs opacity-60 mb-[6px]">
|
|
{item.timerstamp}
|
|
</div>
|
|
<div className="!text-xs opacity-60">
|
|
{item.numberTransactions}次记录
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="mt-2 w-full h-full flex-1">
|
|
<WorldGeo
|
|
dataInfo={dataInfo}
|
|
nestedEncryptionLogs={nestedEncryptionLogs}
|
|
trafficObfuscationLogs={trafficObfuscationLogs}
|
|
selectedApp={selectedApp}
|
|
tooltipType={tooltipType}
|
|
tooltipClosed={tooltipClosed}
|
|
setTooltipClosed={setTooltipClosed}
|
|
/>
|
|
</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="flex items-center justify-center w-[193px] h-[80px] cursor-pointer bg-[#1448F5] rounded-[40px] text-white text-lg font-medium"
|
|
onClick={() => {
|
|
handleProxyToggle(isProxyEnabled, isCoreRunning);
|
|
}}
|
|
>
|
|
{isProxyEnabled ? "关闭匿名服务" : "开启匿名服务"}
|
|
</div>
|
|
{/* <div
|
|
className="bt1 cursor-pointer"
|
|
onClick={() => {
|
|
setTooltipType(
|
|
CONST_TOOLTIP_TYPE.NESTED_ENCRYPTION.type
|
|
);
|
|
setTooltipClosed(true);
|
|
}}
|
|
>
|
|
嵌套加密
|
|
</div>
|
|
<div
|
|
className="bt1 cursor-pointer"
|
|
onClick={() => {
|
|
setTooltipType(
|
|
CONST_TOOLTIP_TYPE.TRAFFIC_OBFUSCATION.type
|
|
);
|
|
setTooltipClosed(true);
|
|
}}
|
|
>
|
|
流量混淆
|
|
</div> */}
|
|
{appDiversion.map((item) => {
|
|
return (
|
|
<div
|
|
key={item.name}
|
|
className="flex items-center justify-center w-16 h-16 relative rounded-[4.95px] shadow-[0px_0px_3.299999952316284px_0px_rgba(84,87,255,1.00)] outline outline-[0.50px] outline-offset-[-0.50px] outline-indigo-50/60 overflow-hidden"
|
|
onClick={() => handleClickApp(item)}
|
|
>
|
|
{selectedApp?.name === item?.name ? (
|
|
<item.activeIcon />
|
|
) : (
|
|
<item.icon />
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default NewHome;
|