diff --git a/app/main/filePath.js b/app/main/filePath.js index 8dc7a63345..070bd8e60a 100644 --- a/app/main/filePath.js +++ b/app/main/filePath.js @@ -12,8 +12,6 @@ const osHome = os.homedir() /** 软件关联数据文件夹名 */ const projectName = "yakit-projects" -console.log(`software: ${appPath}\n osHome: ${osHome}\n projectName: ${projectName}`) - /** 软件关联数据路径设置逻辑 Start */ // 数据文件夹路径 let project_path = "" @@ -51,7 +49,12 @@ try { * 如果获取项目关联文件夹路径错误时,将自动设置为系统用户下面(容灾处理) */ const YakitProjectPath = project_path || osHomeProjectPath + +console.log(`---------- Global-Path Start ----------`) +console.log(`software-path: ${appPath}`) +console.log(`os-home-path: ${osHome}`) console.log(`yakit-projects-path: ${YakitProjectPath}`) +console.log(`---------- Global-Path End ----------`) /** 引擎文件夹路径 */ const yaklangEngineDir = path.join(YakitProjectPath, "yak-engine") diff --git a/app/main/handlers/upgradeUtil.js b/app/main/handlers/upgradeUtil.js index 4657d5e4f1..1f5d37aa2e 100644 --- a/app/main/handlers/upgradeUtil.js +++ b/app/main/handlers/upgradeUtil.js @@ -7,6 +7,7 @@ const fs = require("fs"); const https = require("https"); const requestProgress = require("request-progress"); const request = require("request"); +const EventEmitter = require('events'); const zip = require('node-stream-zip'); const { @@ -87,8 +88,7 @@ function saveSecret(name, host, port, tls, password, caPem) { } authMeta.push({ - host, port, tls, password, caPem, - name: name || `${host}:${port}`, + host, port, tls, password, caPem, name: name || `${host}:${port}`, }) saveAllSecret([...authMeta]) }; @@ -103,11 +103,9 @@ const saveAllSecret = (authInfos) => { } - const authFileStr = JSON.stringify( - [...authInfos.filter((v, i, arr) => { - return arr.findIndex(origin => origin.name === v.name) === i - })] - ); + const authFileStr = JSON.stringify([...authInfos.filter((v, i, arr) => { + return arr.findIndex(origin => origin.name === v.name) === i + })]); fs.writeFileSync(remoteLinkFile, new Buffer(authFileStr, "utf8")) }; @@ -180,11 +178,9 @@ const getYakitPlatform = () => { } module.exports = { - getLatestYakLocalEngine, - initial: async () => { + getLatestYakLocalEngine, initial: async () => { return await initMkbaseDir(); - }, - register: (win, getClient) => { + }, register: (win, getClient) => { ipcMain.handle("save-yakit-remote-auth", async (e, params) => { let {name, host, port, tls, caPem, password} = params; name = name || `${host}:${port}` @@ -262,45 +258,118 @@ module.exports = { return await asyncQueryLatestYakitEngineVersion(params) }) + + class YakVersionEmitter extends EventEmitter { + } + + const yakVersionEmitter = new YakVersionEmitter(); + let isFetchingVersion = false; + let latestVersionCache = null; + // asyncQueryLatestYakEngineVersion wrapper const asyncGetCurrentLatestYakVersion = (params) => { return new Promise((resolve, reject) => { - try { - childProcess.execFile(getLatestYakLocalEngine(), ["-v"], (err, stdout) => { - if (err) { + console.info("start to fetch YAK-VERSION") + if (latestVersionCache) { + console.info("YAK-VERSION: fetch cache: " + `${latestVersionCache}`) + resolve(latestVersionCache) + return; + } + + console.info("YAK-VERSION: mount version") + yakVersionEmitter.once('version', (err, version) => { + if (err) { + + diagnosingYakVersion().catch(err => { + console.info("YAK-VERSION(DIAG): fetch error: " + `${err}`) reject(err) - return - } - // const version = stdout.replaceAll("yak version ()", "").trim(); - const version = /.*?yak(\.exe)?\s+version\s+([^\s]+)/.exec(stdout)[2]; - if (!version) { - if (err) { - reject(err) - } else { - reject("[unknown reason] cannot fetch yak version (yak -v)") - } - } else { - resolve(version) - } - }) - } catch (e) { - reject(e) + }).then(() => { + console.info("YAK-VERSION: fetch error: " + `${err}`) + reject(err) + }) + } else { + console.info("YAK-VERSION: hit version: " + `${version}`) + resolve(version); + } + }) + if (isFetchingVersion) { + console.info("YAK-VERSION is executing...") + return; } + + console.info("YAK-VERSION process is executing...") + isFetchingVersion = true; + childProcess.execFile(getLatestYakLocalEngine(), ["-v"], (err, stdout) => { + console.info(stdout) + if (err) { + yakVersionEmitter.emit('version', err, null); + isFetchingVersion = false + return; + } + // const version = stdout.replaceAll("yak version ()", "").trim(); + const match = /.*?yak(\.exe)?\s+version\s+(\S+)/.exec(stdout); + const version = match && match[2]; + if (!version) { + const error = new Error("[unknown reason] cannot fetch yak version (yak -v)"); + yakVersionEmitter.emit('version', error, null); + isFetchingVersion = false; + } else { + latestVersionCache = version; + yakVersionEmitter.emit('version', null, version); + isFetchingVersion = false + } + }) }) } ipcMain.handle("get-current-yak", async (e, params) => { return await asyncGetCurrentLatestYakVersion(params) }) + const diagnosingYakVersion = () => new Promise((resolve, reject) => { + const commandPath = getLatestYakLocalEngine() + fs.access(commandPath, fs.constants.X_OK, err => { + if (err) { + if (err.code === 'ENOENT') { + reject(new Error(`命令未找到: ${commandPath}`)); + } else if (err.code === 'EACCES') { + reject(new Error(`命令无法执行(无权限): ${commandPath}`)); + } else { + reject(new Error(`命令无法执行: ${commandPath}`)); + } + return; + } + + childProcess.execFile(commandPath, ['-v'], {timeout: 20000}, (error, stdout, stderr) => { + if (error) { + let errorMessage = `命令执行失败: ${error.message}\nStdout: ${stdout}\nStderr: ${stderr}`; + if (error.code === 'ENOENT') { + errorMessage = `无法执行命令,引擎未找到: ${commandPath}\nStderr: ${stderr}`; + } else if (error.killed) { + errorMessage = `引擎启动被系统强制终止,可能的原因为内存占用过多或系统退出或安全防护软件: ${commandPath}\nStderr: ${stderr}`; + } else if (error.signal) { + errorMessage = `引擎由于信号而终止: ${error.signal}\nStderr: ${stderr}`; + } else if (error.code === 'ETIMEDOUT') { + errorMessage = `命令执行超时,进程遭遇未知问题,需要用户在命令行中执行引擎调试: ${commandPath}\nStdout: ${stdout}\nStderr: ${stderr}`; + } + + reject(new Error(errorMessage)); + return; + } + + resolve(stdout); + }) + }) + }) + ipcMain.handle('diagnosing-yak-version', async (e, params) => { + return diagnosingYakVersion() + }) + /** 获取Yakit Yaklang本地版本号 操作系统 架构 */ ipcMain.handle("fetch-local-basic-info", async (e) => { const localYakit = app.getVersion() const localYaklang = await asyncGetCurrentLatestYakVersion() return { - system:process.platform, - arch:process.arch, - localYakit, - localYaklang + system: process.platform, arch: process.arch, localYakit, localYaklang } }) @@ -342,17 +411,17 @@ module.exports = { const pattern = /[\u4e00-\u9fa5]/g; // 匹配中文字符的正则表达式 const matches = str.match(pattern); // 找到所有中文字符的匹配项 if (matches) { - for (const match of matches) { - const encodedMatch = encodeURIComponent(match); - str = str.replace(match, encodedMatch); - } + for (const match of matches) { + const encodedMatch = encodeURIComponent(match); + str = str.replace(match, encodedMatch); + } } return str; } - const downloadYakitByDownloadUrl = (resolve,reject,downloadUrl) => { + const downloadYakitByDownloadUrl = (resolve, reject, downloadUrl) => { // 可能存在中文的下载文件夹,就判断下Downloads文件夹是否存在,不存在则新建一个 - if(!fs.existsSync(yakitInstallDir)) fs.mkdirSync(yakitInstallDir, {recursive: true}) + if (!fs.existsSync(yakitInstallDir)) fs.mkdirSync(yakitInstallDir, {recursive: true}) const dest = path.join(yakitInstallDir, path.basename(downloadUrl)); try { fs.unlinkSync(dest) @@ -361,13 +430,12 @@ module.exports = { } // https://github.com/IndigoUnited/node-request-progress // The options argument is optional so you can omit it - requestProgress( - request(encodeChineseCharacters(downloadUrl)), { - // throttle: 2000, // Throttle the progress event to 2000ms, defaults to 1000ms - // delay: 1000, // Only start to emit after 1000ms delay, defaults to 0ms - // lengthHeader: 'x-transfer-length' // Length header to use, defaults to content-length - }) - .on("response",function (resp){ + requestProgress(request(encodeChineseCharacters(downloadUrl)), { + // throttle: 2000, // Throttle the progress event to 2000ms, defaults to 1000ms + // delay: 1000, // Only start to emit after 1000ms delay, defaults to 0ms + // lengthHeader: 'x-transfer-length' // Length header to use, defaults to content-length + }) + .on("response", function (resp) { if (resp.statusCode === 404) { reject("暂无最新安装包") } @@ -391,7 +459,7 @@ module.exports = { version = version.substr(1) } const downloadUrl = getYakitDownloadUrl(version, isEnterprise); - downloadYakitByDownloadUrl(resolve,reject,downloadUrl) + downloadYakitByDownloadUrl(resolve, reject, downloadUrl) }) } @@ -401,12 +469,12 @@ module.exports = { ipcMain.handle("download-enpriTrace-latest-yakit", async (e, url) => { return await new Promise((resolve, reject) => { - downloadYakitByDownloadUrl(resolve,reject,url) - }) + downloadYakitByDownloadUrl(resolve, reject, url) + }) }) ipcMain.handle("update-enpritrace-info", async () => { - return await {version:getYakitPlatform()} + return await {version: getYakitPlatform()} }) ipcMain.handle("get-windows-install-dir", async (e) => { @@ -446,18 +514,13 @@ module.exports = { } - childProcess.exec( - isWindows ? - `copy "${origin}" "${dest}"` - : `cp "${origin}" "${dest}" && chmod +x "${dest}"`, - err => { - if (err) { - reject(err) - return - } - resolve() + childProcess.exec(isWindows ? `copy "${origin}" "${dest}"` : `cp "${origin}" "${dest}" && chmod +x "${dest}"`, err => { + if (err) { + reject(err) + return } - ) + resolve() + }) }) } @@ -490,8 +553,7 @@ module.exports = { console.info("Start to Extract yak.zip") const zipHandler = new zip({ - file: loadExtraFilePath(path.join("bins", "yak.zip")), - storeEntries: true, + file: loadExtraFilePath(path.join("bins", "yak.zip")), storeEntries: true, }) console.info("Start to Extract yak.zip: Set `ready`") zipHandler.on("ready", () => { @@ -572,8 +634,7 @@ module.exports = { // 获取内置引擎版本 ipcMain.handle("GetBuildInEngineVersion" - /*"IsBinsExisted"*/, - async (e) => { + /*"IsBinsExisted"*/, async (e) => { const yakZipName = path.join("bins", "yak.zip") if (!fs.existsSync(loadExtraFilePath(yakZipName))) { throw Error(`Cannot found yak.zip, bins: ${loadExtraFilePath(yakZipName)}`) diff --git a/app/renderer/src/main/src/NewApp.tsx b/app/renderer/src/main/src/NewApp.tsx index 233eaea8ad..113d84ab66 100644 --- a/app/renderer/src/main/src/NewApp.tsx +++ b/app/renderer/src/main/src/NewApp.tsx @@ -1,10 +1,6 @@ -import React, {useRef, useEffect, useState, Suspense, lazy} from "react" +import {useRef, useEffect, useState, Suspense, lazy} from "react" // by types import {failed, warn, yakitFailed} from "./utils/notification" -import {useHotkeys} from "react-hotkeys-hook" -import {getCompletions} from "./utils/monacoSpec/yakCompletionSchema" -import {showModal} from "./utils/showModal" -import {YakCodeEditor} from "./utils/editors" import {getRemoteValue, setRemoteValue} from "./utils/kv" import {useGetState, useMemoizedFn} from "ahooks" import {NetWorkApi} from "./services/fetch" @@ -19,7 +15,6 @@ import styles from "./app.module.scss" import {coordinate} from "./pages/globalVariable" import {remoteOperation} from "./pages/dynamicControl/DynamicControl" import {useTemporaryProjectStore} from "./store/temporaryProject" -import {ProjectDescription} from "./pages/softwareSettings/ProjectManage" import {useRunNodeStore} from "./store/runNode" /** 快捷键目录 */ @@ -71,8 +66,8 @@ function NewApp() { /** 私有域是否设置成功 */ const [onlineProfileStatus, setOnlineProfileStatus] = useState(false) + // 错误信息收集监听逻辑 useEffect(() => { - // 错误信息收集监听逻辑 const unhandledrejectionError = (e) => { const content = e?.reason?.stack || "" if (content) ipcRenderer.invoke("render-error-log", content) @@ -95,21 +90,25 @@ function NewApp() { // 全局记录鼠标坐标位置(为右键菜单提供定位) const coordinateTimer = useRef(null) + const handleMouseMove = useMemoizedFn((e: MouseEvent) => { + const {screenX, screenY, clientX, clientY, pageX, pageY} = e + if (coordinateTimer.current) { + clearTimeout(coordinateTimer.current) + coordinateTimer.current = null + } + coordinateTimer.current = setTimeout(() => { + coordinate.screenX = screenX + coordinate.screenY = screenY + coordinate.clientX = clientX + coordinate.clientY = clientY + coordinate.pageX = pageX + coordinate.pageY = pageY + }, 50) + }) useEffect(() => { - document.onmousemove = (e) => { - const {screenX, screenY, clientX, clientY, pageX, pageY} = e - if (coordinateTimer.current) { - clearTimeout(coordinateTimer.current) - coordinateTimer.current = null - } - coordinateTimer.current = setTimeout(() => { - coordinate.screenX = screenX - coordinate.screenY = screenY - coordinate.clientX = clientX - coordinate.clientY = clientY - coordinate.pageX = pageX - coordinate.pageY = pageY - }, 50) + document.addEventListener("mousemove", handleMouseMove) + return () => { + document.removeEventListener("mousemove", handleMouseMove) } }, []) @@ -151,20 +150,6 @@ function NewApp() { .catch(() => {}) }, []) - /** 全局拦截快捷键(补全内容) */ - useHotkeys("alt+a", (e) => { - const a = getCompletions() - showModal({ - title: "补全内容", - width: "100%", - content: ( -
- -
- ) - }) - }) - // 全局监听登录状态 const {userInfo, setStoreUserInfo} = useStore() @@ -258,22 +243,20 @@ function NewApp() { * 拦截软件全局快捷键[(win:ctrl|mac:command) + 26字母] * 通过 InterceptKeyword 变量进行拦截控制 */ - useEffect(() => { - let originEvent = window.onkeydown - window.onkeydown = (ev) => { - let code = ev.code - - // 屏蔽当前事件 - if ((ev.metaKey || ev.ctrlKey) && InterceptKeyword.includes(code)) { - return false - } - - // @ts-ignore - originEvent && originEvent(ev) - return + const handlekey = useMemoizedFn((ev: KeyboardEvent) => { + let code = ev.code + // 屏蔽当前事件 + if ((ev.metaKey || ev.ctrlKey) && InterceptKeyword.includes(code)) { + ev.stopPropagation() + ev.preventDefault() + return false } + return + }) + useEffect(() => { + document.addEventListener("keydown", handlekey) return () => { - window.onkeydown = originEvent + document.removeEventListener("keydown", handlekey) } }, []) diff --git a/app/renderer/src/main/src/components/baseTemplate/BaseTags.tsx b/app/renderer/src/main/src/components/baseTemplate/BaseTags.tsx index 8c2966f7bd..5da0d679ea 100644 --- a/app/renderer/src/main/src/components/baseTemplate/BaseTags.tsx +++ b/app/renderer/src/main/src/components/baseTemplate/BaseTags.tsx @@ -1,8 +1,6 @@ -import React, {useMemo, useEffect, useLayoutEffect, useRef, useState} from "react" -import {Select, Button, Tag, TagProps, SelectProps, Tooltip, Space, Checkbox} from "antd" -import {useGetState, useDebounce, useThrottle} from "ahooks" -import {useHotkeys} from "react-hotkeys-hook" -import {} from "@ant-design/icons" +import React, {useMemo, useEffect, useRef, useState} from "react" +import {Tag, TagProps, Tooltip} from "antd" +import {useGetState} from "ahooks" import "./BaseTags.scss" import classNames from "classnames" @@ -41,11 +39,10 @@ export const TagsList: React.FC = React.memo((props) => { // 计算当前项后宽度 let nowWidth = countWidth + nowItemWidth // 如不是最后一项,则添加...宽度计算 - if(i = React.memo((props) => { ))} -
+
= React.memo((props) => {
) }) -interface HighPowerListProps { - dataSource: DataObjProps[] - checkedList: string[] - cacheScrollTop: number - handleCheckChange: (e: any, v: any) => void - virtualListHeight?: number -} - -// 虚拟列表 -export const HighPowerList: React.FC = (props) => { - const {dataSource, checkedList, handleCheckChange, virtualListHeight = 200, cacheScrollTop} = props - /* - * 每项数据的dom元素高度为30px - * 实际项目可能每项高度不一样 - * */ - const itemH = 30 - // 计算总高度 - const totalH = dataSource.length * itemH + "px" - const [data, setData] = useState([]) // 可视区域数据 - const [totalHeight, setTotalHeight] = useState(totalH) // 长列表总高度 列表中每一项数据高度总和 - const [transform, setTransform] = useState("") - const cacheScroll = useRef(0) - const listRef = useRef(null) - useEffect(() => { - updateViewContent(cacheScrollTop) - }, []) - - const handleScroll = (e) => { - /* - * 获取scrollTop - * 此属性可以获取或者设置对象的最顶部到对象在当前窗口显示的范围内的顶边的距离 - * 也就是元素滚动条被向下拉动的距离 - * */ - updateViewContent(e.target.scrollTop) - } - - const updateViewContent = (scrollTop = 0) => { - cacheScroll.current = scrollTop - // 计算可视区域里能放几个元素 - const viewCount = Math.ceil(virtualListHeight / itemH) - // 计算可视区域开始的索引 - const start = Math.floor(scrollTop / itemH) - // 计算可视区域结束索引 - const end = start + viewCount - // 截取可视区域数据 - const viewData = dataSource.slice(start, end) - // 控制滚动条滑动位置 - listRef.current.scrollTop = scrollTop - setData(viewData) - setTransform(`translate3d(0, ${start * itemH}px, 0)`) - } - - return ( -
-
-
- {data.map((item, idx) => ( - handleCheckChange(e, cacheScroll.current)} - checked={checkedList.includes(item.value)} - > - {item.label} - - ))} -
-
- ) -} - -// Tags下拉框组件 -interface DataObjProps { - label: string - value: string -} -export interface TagsFilterProps extends SelectProps { - data: string[] | DataObjProps[] - defaultData?: string[] - isShowAllCheck?: boolean - submitValue: (v: string[]) => void -} -const {Option} = Select - -export const TagsFilter: React.FC = (props) => { - const {data, isShowAllCheck = false, defaultData = [], submitValue, ...otherProps} = props - const dataSource: DataObjProps[] = data.map((item) => { - if (typeof item === "string") { - return {label: item, value: item} - } - return item - }) - const [checkedList, setCheckedList, getCheckList] = useGetState(defaultData) - const [indeterminate, setIndeterminate] = useState(true) - const [checkAll, setCheckAll] = useState(false) - // 当前选中项 - const [checkItem, setCheckItem, getCheckItem] = useGetState("") - // 受控模式控制浮层 - const [open, setOpen] = useState(false) - // 缓存滚轮定位 - const [cacheScrollTop, setCacheScrollTop] = useState(0) - useEffect(() => { - if (!open) { - setCacheScrollTop(0) - } - }, [open]) - // 键盘移动 - const goAnchor = (e) => { - const nowCheckList = getCheckList() - const nowCheckItem = getCheckItem() - const {code} = e - const isLeft = code === "ArrowLeft" - const isRight = code === "ArrowRight" - // 业务代码 - let index = nowCheckList.indexOf(nowCheckItem) - let newIndex - if (isLeft) { - newIndex = index - 1 < 0 ? 0 : index - 1 - } - if (isRight) { - newIndex = index + 1 === nowCheckList.length ? nowCheckList.length - 1 : index + 1 - } - // js锚点跳转 - let element = document.getElementById(nowCheckList[newIndex]) - setCheckItem(nowCheckList[newIndex]) - if (element) { - element.scrollIntoView({behavior: "smooth"}) - } - } - - useEffect(() => { - document.addEventListener("keydown", (e) => goAnchor(e)) - return () => { - document.removeEventListener("keydown", goAnchor) - } - }, []) - - const handleChange = (value: string[]) => { - setCheckItem("") - setCheckedList([]) - setCheckAll(false) - setIndeterminate(false) - } - // 保留数组中非重复数据 - const filterNonUnique = (arr) => arr.filter((i) => arr.indexOf(i) === arr.lastIndexOf(i)) - const handleCheckChange = (e, value) => { - let list = [...getCheckList(), e.target.value] - // 定位选中项 - if (e.target.checked) { - setCheckItem(e.target.value) - } - // 取消选中则去除重复项 - if (!e.target.checked) { - if (e.target.value === checkItem) { - setCheckItem("") - } - list = filterNonUnique(list) - } - setCacheScrollTop(value) - setCheckedList(list) - setIndeterminate(!!list.length && list.length < dataSource.length) - setCheckAll(list.length === dataSource.length) - } - - const handleCheckAllChange = (e) => { - const checkAllData: string[] = dataSource.map((item) => item.value) - setCheckedList(e.target.checked ? checkAllData : []) - setIndeterminate(false) - setCheckAll(e.target.checked) - } - - const CheckboxOptions = () => { - return ( -
- {isShowAllCheck && ( - - 全选 - - )} - -
- ) - } - - const tagRender = (p) => { - const {label, value, closable} = p - const onPreventMouseDown = (event: React.MouseEvent) => { - event.preventDefault() - event.stopPropagation() - setCheckItem(value) - // js锚点跳转 - let element = document.getElementById(value) - if (element) { - element.scrollIntoView({behavior: "smooth"}) - } - } - const onClose = () => { - let newArr = checkedList.filter((item) => item !== value) - // 删除选中项 - if (checkItem === value) { - setCheckItem("") - } - if (newArr.length) { - setIndeterminate(true) - } else { - setIndeterminate(false) - } - setCheckedList(newArr) - setCheckAll(false) - } - return ( - - {label} - - ) - } - - const resetBtn = () => { - setCheckedList(defaultData) - setIndeterminate(!!defaultData.length && defaultData.length < dataSource.length) - setCheckAll(defaultData.length === dataSource.length) - } - - const submitBtn = () => { - submitValue(checkedList) - setOpen(false) - } - return ( -
- -
- ) -} - -// UI组件测试用例 -export const Test: React.FC = () => { - const [tagList, setTagList] = useState([]) - const data = ["Apple", "Pear", "Orange"] - let newData: string[] = [] - for (let i = 0; i < 10; i++) { - data.map((item) => { - newData.push(`${item}${i}`) - }) - } - return ( -
- { - setTagList(value) - }} - // isShowAllCheck={true} - style={{width: "200px"}} - /> -
- -
-
- -
-
- -
-
- ) -} \ No newline at end of file diff --git a/app/renderer/src/main/src/components/basics/YakitLoading.tsx b/app/renderer/src/main/src/components/basics/YakitLoading.tsx index bd3461c342..ebd46f715a 100644 --- a/app/renderer/src/main/src/components/basics/YakitLoading.tsx +++ b/app/renderer/src/main/src/components/basics/YakitLoading.tsx @@ -15,9 +15,9 @@ import {InstallEngine, QuestionModal} from "../layout/update/InstallEngine" import classNames from "classnames" import styles from "./yakitLoading.module.scss" import {getReleaseEditionName, isCommunityEdition, isEnpriTrace, isEnpriTraceAgent} from "@/utils/envfile" -import { DynamicStatusProps } from "@/store" -import yakitSE from "@/assets/yakitSE.png"; -import yakitEE from "@/assets/yakitEE.png"; +import {DynamicStatusProps} from "@/store" +import yakitSE from "@/assets/yakitSE.png" +import yakitEE from "@/assets/yakitEE.png" const {ipcRenderer} = window.require("electron") @@ -45,8 +45,8 @@ const LoadingTitle: string[] = [ "你的鼠标,掌控世界!——Chelth" ] -export const EngineModeVerbose = (m: YaklangEngineMode,n?:DynamicStatusProps) => { - if(n&&n.isDynamicStatus){ +export const EngineModeVerbose = (m: YaklangEngineMode, n?: DynamicStatusProps) => { + if (n && n.isDynamicStatus) { return "控制模式" } switch (m) { @@ -69,6 +69,9 @@ export interface YakitLoadingProp { /** 是否完成初始化 */ loading: boolean + /** 软件检查日志 */ + checkLog: string[] + currentYakit: string latestYakit: string setLatestYakit: (val: string) => any @@ -82,7 +85,7 @@ export interface YakitLoadingProp { setShowEngineLog: (flag: boolean) => any connectControl: () => any - setRunRemote:(v:boolean) => void + setRunRemote: (v: boolean) => void } export const YakitLoading: React.FC = (props) => { @@ -101,7 +104,8 @@ export const YakitLoading: React.FC = (props) => { setShowEngineLog, onEngineModeChange, connectControl, - setRunRemote + setRunRemote, + checkLog } = props const [restartLoading, setRestartLoading] = useState(false) @@ -200,7 +204,7 @@ export const YakitLoading: React.FC = (props) => { getEngineReady() > 0 && !!readyTime.current ) { - if(!isCommunityEdition()) return false + if (!isCommunityEdition()) return false setTimeout(() => { clearInterval(readyTime.current) }, 300) @@ -214,7 +218,7 @@ export const YakitLoading: React.FC = (props) => { getEngineReady() > 0 && !!readyTime.current ) { - if(!isCommunityEdition()) return false + if (!isCommunityEdition()) return false setTimeout(() => { clearInterval(readyTime.current) }, 300) @@ -233,7 +237,7 @@ export const YakitLoading: React.FC = (props) => { ipcRenderer .invoke("start-local-yaklang-engine", { port: props.localPort, - isEnpriTraceAgent:isEnpriTraceAgent() + isEnpriTraceAgent: isEnpriTraceAgent() }) .then(() => { outputToWelcomeConsole("手动引擎启动成功!") @@ -284,13 +288,13 @@ export const YakitLoading: React.FC = (props) => { }, 1000) }) - const [isRefresh,setRefresh] = useState(false) - useEffect(()=>{ + const [isRefresh, setRefresh] = useState(false) + useEffect(() => { // 自动远程连接 - if(yakitStatus === "control-remote"){ - connectControl() + if (yakitStatus === "control-remote") { + connectControl() } - },[isRefresh]) + }, [isRefresh]) const refresh = () => { setRefresh(!isRefresh) @@ -298,26 +302,26 @@ export const YakitLoading: React.FC = (props) => { const goBack = () => { ipcRenderer - .invoke("start-local-yaklang-engine", { - port: props.localPort, - isEnpriTraceAgent:isEnpriTraceAgent() - }) - .then(() => { - outputToWelcomeConsole("手动引擎启动成功!") - if (onEngineModeChange) { - onEngineModeChange("local", true) - setRunRemote(false) - } - }) - .catch((e) => { - outputToWelcomeConsole("手动引擎启动失败!") - outputToWelcomeConsole(`失败原因:${e}`) - }) + .invoke("start-local-yaklang-engine", { + port: props.localPort, + isEnpriTraceAgent: isEnpriTraceAgent() + }) + .then(() => { + outputToWelcomeConsole("手动引擎启动成功!") + if (onEngineModeChange) { + onEngineModeChange("local", true) + setRunRemote(false) + } + }) + .catch((e) => { + outputToWelcomeConsole("手动引擎启动失败!") + outputToWelcomeConsole(`失败原因:${e}`) + }) } const hintTitle = useMemo(() => { if (loading) { - return
软件加载中 ...
+ return
软件启动中 ...
} if (yakitStatus === "ready") { if (__engineReady > 0) { @@ -357,30 +361,47 @@ export const YakitLoading: React.FC = (props) => { }, [yakitStatus, loading, __engineReady, readyTime.current, __showLog, logTime.current]) const btns = useMemo(() => { - if(yakitStatus === "control-remote"){ - return <> - - 刷新 - - - 返回 - - + if (yakitStatus === "control-remote") { + return ( + <> + + 刷新 + + + 返回 + + + ) } if (yakitStatus === "ready") { if (__engineReady > 0) { return ( <> - - 跳过倒计时 - - - { - selectEngineMode(engineMode === "local" ? "remote": "local") - changeMode() - }}> - 切换为{engineMode === "local" ? "远程": "本地"}模式 - + {checkLog.length === 0 && ( + <> + + 跳过倒计时 + + + { + selectEngineMode(engineMode === "local" ? "remote" : "local") + changeMode() + }} + > + 切换为{engineMode === "local" ? "远程" : "本地"}模式 + + + )} ) } @@ -397,11 +418,17 @@ export const YakitLoading: React.FC = (props) => { 手动连接引擎 - { - selectEngineMode(engineMode === "local" ? "remote": "local") - changeMode() - }}> - 切换为{engineMode === "local" ? "远程": "本地"}模式 + { + selectEngineMode(engineMode === "local" ? "remote" : "local") + changeMode() + }} + > + 切换为{engineMode === "local" ? "远程" : "本地"}模式 = (props) => { 手动连接引擎 - { - selectEngineMode(engineMode === "local" ? "remote": "local") - changeMode() - }}> - 切换为{engineMode === "local" ? "远程": "本地"}模式 + { + selectEngineMode(engineMode === "local" ? "remote" : "local") + changeMode() + }} + > + 切换为{engineMode === "local" ? "远程" : "本地"}模式 = (props) => { 手动连接引擎 - { - selectEngineMode(engineMode === "local" ? "remote": "local") - changeMode() - }}> - 切换为{engineMode === "local" ? "远程": "本地"}模式 + { + selectEngineMode(engineMode === "local" ? "remote" : "local") + changeMode() + }} + > + 切换为{engineMode === "local" ? "远程" : "本地"}模式 ) @@ -490,11 +529,17 @@ export const YakitLoading: React.FC = (props) => { 手动连接引擎 - { - selectEngineMode(engineMode === "local" ? "remote": "local") - changeMode() - }}> - 切换为{engineMode === "local" ? "远程": "本地"}模式 + { + selectEngineMode(engineMode === "local" ? "remote" : "local") + changeMode() + }} + > + 切换为{engineMode === "local" ? "远程" : "本地"}模式 = (props) => { manuallyStartEngine, showEngineLog, restartLoading, - engineMode + engineMode, + checkLog ]) /** 加载页随机宣传语 */ const loadingTitle = useMemo(() => LoadingTitle[Math.floor(Math.random() * (LoadingTitle.length - 0)) + 0], []) /** Title */ - const Title = useMemo(() => yakitStatus==="control-remote"?"远程控制中 ...":`欢迎使用 ${getReleaseEditionName()}`, []) - + const Title = useMemo( + () => (yakitStatus === "control-remote" ? "远程控制中 ..." : `欢迎使用 ${getReleaseEditionName()}`), + [] + ) + return (
@@ -543,35 +592,46 @@ export const YakitLoading: React.FC = (props) => {
{/* 社区版 - 启动Logo */} - {isCommunityEdition() &&
-
-
- + {isCommunityEdition() && ( +
+
+
+ +
+
+
+
-
- -
-
} + )} {/* 企业版 - 启动Logo */} - { - isEnpriTrace()&&
-
- 暂无图片 + {isEnpriTrace() && ( +
+
+ 暂无图片 +
-
- } + )} {/* 便携版 - 启动Logo */} - { - isEnpriTraceAgent()&&
-
- 暂无图片 + {isEnpriTraceAgent() && ( +
+
+ 暂无图片 +
-
- } + )}
{hintTitle} + { +
+
+ {checkLog.map((item, index) => { + return
{item}
+ })} +
+
+ }
{btns}
@@ -607,19 +667,19 @@ export const YakitLoading: React.FC = (props) => { ) } -interface YakitControlLoadingProp{ +interface YakitControlLoadingProp { localPort: number onEngineModeChange: (mode: YaklangEngineMode, keepalive?: boolean) => any onSubmit: () => any } export const YakitControlLoading: React.FC = (props) => { - const {localPort,onEngineModeChange,onSubmit} = props - const [isRefresh,setRefresh] = useState(false) - useEffect(()=>{ + const {localPort, onEngineModeChange, onSubmit} = props + const [isRefresh, setRefresh] = useState(false) + useEffect(() => { // 自动远程连接 onSubmit() - },[isRefresh]) + }, [isRefresh]) const refresh = () => { setRefresh(!isRefresh) @@ -627,53 +687,55 @@ export const YakitControlLoading: React.FC = (props) => const goBack = () => { ipcRenderer - .invoke("start-local-yaklang-engine", { - port: localPort, - isEnpriTraceAgent:isEnpriTraceAgent() - }) - .then(() => { - outputToWelcomeConsole("手动引擎启动成功!") - if (onEngineModeChange) { - onEngineModeChange("local", true) - } - }) - .catch((e) => { - outputToWelcomeConsole("手动引擎启动失败!") - outputToWelcomeConsole(`失败原因:${e}`) - }) + .invoke("start-local-yaklang-engine", { + port: localPort, + isEnpriTraceAgent: isEnpriTraceAgent() + }) + .then(() => { + outputToWelcomeConsole("手动引擎启动成功!") + if (onEngineModeChange) { + onEngineModeChange("local", true) + } + }) + .catch((e) => { + outputToWelcomeConsole("手动引擎启动失败!") + outputToWelcomeConsole(`失败原因:${e}`) + }) } - return
-
-
-
-
远程控制中 ...
-
+ return ( +
+
+
+
+
远程控制中 ...
+
-
-
-
- +
+
+
+ +
+
+
+ +
-
-
- -
-
-
- {/*
远程控制中 ...
*/} -
- - 刷新 - - - 返回 - +
+ {/*
远程控制中 ...
*/} +
+ + 刷新 + + + 返回 + +
+
-
-
+ ) } interface DownloadYaklangProps { system: YakitSystem @@ -712,7 +774,7 @@ const DownloadYaklang: React.FC = React.memo((props) => { ipcRenderer .invoke("fetch-latest-yaklang-version") - .then((data: string) => latestVersion.current = data) + .then((data: string) => (latestVersion.current = data)) .catch((e: any) => { if (isBreakRef.current) return failed(`获取引擎最新版本失败 ${e}`) diff --git a/app/renderer/src/main/src/components/layout/LocalEngineCheck/LocalEngineCheck.tsx b/app/renderer/src/main/src/components/layout/LocalEngineCheck/LocalEngineCheck.tsx new file mode 100644 index 0000000000..7e220a1c70 --- /dev/null +++ b/app/renderer/src/main/src/components/layout/LocalEngineCheck/LocalEngineCheck.tsx @@ -0,0 +1,7 @@ +import React from "react" + +/** 本地引擎自检启动 */ + +export const LocalEngineCheck: React.FC = (props) => { + return <> +} diff --git a/app/renderer/src/main/src/components/layout/UILayout.tsx b/app/renderer/src/main/src/components/layout/UILayout.tsx index ce70c7db6d..8790e0659a 100644 --- a/app/renderer/src/main/src/components/layout/UILayout.tsx +++ b/app/renderer/src/main/src/components/layout/UILayout.tsx @@ -67,8 +67,9 @@ import yakitCattle from "@/assets/yakitCattle.png" import {NetWorkApi} from "@/services/fetch" import {useTemporaryProjectStore} from "@/store/temporaryProject" import emiter from "@/utils/eventBus/eventBus" - +import {saveFuzzerCache, usePageInfo} from "@/store/pageInfo" import classNames from "classnames" + import styles from "./uiLayout.module.scss" import {YakitSelect} from "../yakitUI/YakitSelect/YakitSelect" @@ -113,7 +114,7 @@ const UILayout: React.FC = (props) => { const [runRemote, setRunRemote] = useState(false) /** 认证信息 */ - const [credential, setCredential, getCredential] = useGetState({ + const [credential, setCredential] = useState({ Host: "127.0.0.1", IsTLS: false, Password: "", @@ -125,35 +126,269 @@ const UILayout: React.FC = (props) => { /** 数据库权限由usestate改为useref(数据不影响渲染) */ const databaseError = useRef(false) - /* 内置二进制文件的话,需要通过自检 */ - useEffect(() => { + /** 本地引擎自检输出日志 */ + const [checkLog, setCheckLog] = useState([]) + const updateCheckLog = useMemoizedFn((conten: string) => { + setCheckLog((arr) => arr.concat([conten])) + }) + + /** + * 插件漏洞信息库自检 + */ + const handleBuiltInCheck = useMemoizedFn(() => { ipcRenderer - .invoke("GetBuildInEngineVersion") - .then((e) => { - if (e !== "") { - outputToWelcomeConsole(`引擎内置自检成功!内置引擎:${e}`) + .invoke("InitCVEDatabase") + .then(() => { + info("漏洞信息库自检完成") + }) + .catch((e) => { + info(`漏洞信息库检查错误:${e}`) + }) + }) + /** + * 获取上次本地连接引擎的端口缓存 + * 获取上次连接引擎的模式缓存 + */ + const handleLinkEngineInfo = useMemoizedFn(() => { + getLocalValue(LocalGV.YaklangEnginePort) + .then((portRaw) => { + const port = parseInt(portRaw) + if (!port) { + getRandomLocalEnginePort((p) => setLocalPort(p)) } else { - outputToWelcomeConsole(`引擎内置自检:无内置引擎标识 ${e}`) + setLocalPort(port) } }) - .catch((e) => { - outputToWelcomeConsole(`引擎内置自检:无内置引擎: ${e}`) + .catch(() => { + getRandomLocalEnginePort((p) => setLocalPort(p)) + }) + + updateCheckLog("获取上次连接引擎的模式") + getLocalValue(LocalGV.YaklangEngineMode).then((val: YaklangEngineMode) => { + switch (val) { + case "remote": + updateCheckLog("获取连接模式成功——远程模式") + setCheckLog([]) + setEngineMode("remote") + cacheEngineMode.current = "remote" + setLoading(false) + return + case "local": + updateCheckLog("获取连接模式成功——本地模式") + handleCheckEngineAndDataBase() + return + default: + updateCheckLog("获取连接模式失败,自动选择默认(本地)模式") + handleCheckEngineAndDataBase() + return + } + }) + }) + /** + * 检查引擎是否存在 + * 检查数据库权限是否正常 + */ + const handleCheckEngineAndDataBase = useMemoizedFn(() => { + updateCheckLog("开始识别引擎是否已安装...") + ipcRenderer + .invoke("is-yaklang-engine-installed") + .then((flag: boolean) => { + if (flag) { + updateCheckLog("引擎已安装") + } else { + updateCheckLog("引擎未安装") + } + isEngineInstalled.current = flag }) .finally(() => { - info("开始检查漏洞信息库") - ipcRenderer - .invoke("InitCVEDatabase") - .then(() => { - info("漏洞信息库自检完成") - }) - .catch((e) => { - info(`漏洞信息库检查错误:${e}`) - }) + if (!isEngineInstalled.current) { + setEngineMode(undefined) + updateCheckLog("由于引擎未安装,准备弹出安装提示框") + getCacheEngineMode() + setTimeout(() => { + setYakitStatus("install") + cacheYakitStatus.current = "install" + setLoading(false) + }, 2000) + return + } else { + updateCheckLog("已安装引擎,开始检查数据库权限是否正常") + /** 引擎已安装的情况下,优先检查数据库权限 */ + ipcRenderer + .invoke("check-local-database") + .then((e) => { + databaseError.current = e === "not allow to write" + }) + .finally(() => { + // 这里只有两种状态,数据库(有|无)权限情况 + if (databaseError.current && getSystem() !== "Windows_NT") { + updateCheckLog("数据库权限错误,开始进行调整操作(非WIN系统检查)") + setYakitStatus("database") + cacheYakitStatus.current = "database" + } else { + updateCheckLog("数据库权限无问题") + handleFetchYakitAndYaklangVersion() + } + }) + } }) + }) + /** + * 获取yaklang本地版本和最新版本 + * 获取yakit本地版本和最新版本 + */ + const handleFetchYakitAndYaklangVersion = useMemoizedFn(() => { + getLocalValue(LocalGV.NoAutobootLatestVersionCheck).then((val: boolean) => { + updateCheckLog("开始检查引擎文件相关状态") + ipcRenderer.invoke("fetch-yakit-version").then((data: string) => { + if (!val) setCurrentYakit(data) + }) + ipcRenderer.invoke("fetch-latest-yakit-version").then((data: string) => { + if (!val) { + isEnpriTraceAgent() ? setLatestYakit("") : setLatestYakit(data) + } + }) + + ipcRenderer + .invoke("get-current-yak") + .then((data: string) => { + if (!val) setCurrentYaklang(data) + updateCheckLog(`获取引擎版本号:${data}`) + setTimeout(() => { + getCacheEngineMode() + setLoading(false) + }, 1000) + }) + .catch((e) => { + updateCheckLog(`获取引擎失败:${e}`) + }) + .finally(() => { + setTimeout(() => { + getCacheEngineMode() + setLoading(false) + }, 1000) + }) + ipcRenderer + .invoke("fetch-latest-yaklang-version") + .then((data: string) => { + if (!val) setLatestYaklang(data) + }) + .catch((err) => {}) + }) + }) + + useEffect(() => { + setLoading(true) + ipcRenderer.invoke("is-dev").then((flag: boolean) => (isDev.current = !!flag)) + ipcRenderer.invoke("fetch-system-name").then((type: YakitSystem) => setSystem(type)) + + handleBuiltInCheck() + handleLinkEngineInfo() }, []) - const getCacheEngineMode = useMemoizedFn(() => { - setEngineMode(undefined) + useEffect(() => { + if (engineLink) setCheckLog([]) + }, [engineLink]) + + /** + * 检查是否有内置引擎 + * 检查漏洞信息库 + */ + // useEffect(() => { + // ipcRenderer + // .invoke("GetBuildInEngineVersion") + // .then((e) => { + // if (e !== "") { + // outputToWelcomeConsole(`引擎内置自检成功!内置引擎:${e}`) + // } else { + // outputToWelcomeConsole(`引擎内置自检:无内置引擎标识 ${e}`) + // } + // }) + // .catch((e) => { + // outputToWelcomeConsole(`引擎内置自检:无内置引擎: ${e}`) + // }) + // .finally(() => { + // ipcRenderer + // .invoke("InitCVEDatabase") + // .then(() => { + // info("漏洞信息库自检完成") + // }) + // .catch((e) => { + // info(`漏洞信息库检查错误:${e}`) + // }) + // }) + // }, []) + + /** + * 获取yaklang引擎是否安装的状态 + * - 判断上次使用引擎的状态,如果有使用,这判断是否可以启动引擎进入软件界面(未安装状态只限远程可以进入软件界面) + * 1) 如果有使用,引擎未安装,则只限远程状态可以连接进入界面 + * 2) 如果有使用,引擎已安装,则正常连接上次使用状态的引擎 + */ + // useEffect(() => { + // outputToWelcomeConsole("识别引擎是否已安装...") + // updateCheckLog("识别引擎是否已安装...") + // ipcRenderer + // .invoke("is-yaklang-engine-installed") + // .then((flag: boolean) => { + // if (flag) { + // outputToWelcomeConsole("引擎已安装") + // updateCheckLog("引擎已安装") + // } else { + // outputToWelcomeConsole("引擎未安装") + // updateCheckLog("引擎未安装") + // } + // isEngineInstalled.current = flag + // }) + // .finally(() => { + // if (!isEngineInstalled.current) { + // setEngineMode(undefined) + // outputToWelcomeConsole("由于引擎未安装,仅开启远程模式或用户需安装核心引擎") + // updateCheckLog("由于引擎未安装,仅开启远程模式或用户需安装核心引擎") + // getCacheEngineMode() + // setTimeout(() => { + // setYakitStatus("install") + // cacheYakitStatus.current = "install" + // setLoading(false) + // }, 300) + // return + // } else { + // outputToWelcomeConsole("已安装引擎,开始检查数据库权限是否正常") + // updateCheckLog("已安装引擎,开始检查数据库权限是否正常") + // /** 引擎已安装的情况下,优先检查数据库权限 */ + // ipcRenderer + // .invoke("check-local-database") + // .then((e) => { + // if (e === "not allow to write") outputToWelcomeConsole("数据库权限错误,开始进行调整操作") + // databaseError.current = e === "not allow to write" + // }) + // .finally(() => { + // // 这里只有两种状态,数据库(有|无)权限情况 + // if (databaseError.current && getSystem() !== "Windows_NT") { + // setYakitStatus("database") + // cacheYakitStatus.current = "database" + // } else getCacheEngineMode() + // setLoading(false) + // }) + // } + // }) + + // getLocalValue(LocalGV.YaklangEnginePort) + // .then((portRaw) => { + // const port = parseInt(portRaw) + // if (!port) { + // getRandomLocalEnginePort((p) => setLocalPort(p)) + // } else { + // setLocalPort(port) + // } + // }) + // .catch(() => { + // getRandomLocalEnginePort((p) => setLocalPort(p)) + // }) + // }, []) + + const getCacheEngineMode = useMemoizedFn((isSet?: boolean) => { + if (!isSet) setEngineMode(undefined) getLocalValue(LocalGV.YaklangEngineMode).then((val: YaklangEngineMode) => { if (val) info(`加载上次引擎模式:${val}`) switch (val) { @@ -184,35 +419,35 @@ const UILayout: React.FC = (props) => { * 3、获取yakit本地版本和最新版本 * 4、获取yaklang本地版本和最新版本 */ - useEffect(() => { - setLoading(true) - ipcRenderer.invoke("is-dev").then((flag: boolean) => (isDev.current = !!flag)) - ipcRenderer.invoke("fetch-system-name").then((type: YakitSystem) => setSystem(type)) - - getLocalValue(LocalGV.NoAutobootLatestVersionCheck).then((val: boolean) => { - if (!val) { - ipcRenderer.invoke("fetch-yakit-version").then((data: string) => { - setCurrentYakit(data) - }) - ipcRenderer.invoke("fetch-latest-yakit-version").then((data: string) => { - isEnpriTraceAgent() ? setLatestYakit("") : setLatestYakit(data) - }) - - ipcRenderer - .invoke("get-current-yak") - .then((data: string) => { - setCurrentYaklang(data) - }) - .catch(() => {}) - ipcRenderer - .invoke("fetch-latest-yaklang-version") - .then((data: string) => { - setLatestYaklang(data) - }) - .catch((err) => {}) - } - }) - }, []) + // useEffect(() => { + // setLoading(true) + // ipcRenderer.invoke("is-dev").then((flag: boolean) => (isDev.current = !!flag)) + // ipcRenderer.invoke("fetch-system-name").then((type: YakitSystem) => setSystem(type)) + + // getLocalValue(LocalGV.NoAutobootLatestVersionCheck).then((val: boolean) => { + // if (!val) { + // ipcRenderer.invoke("fetch-yakit-version").then((data: string) => { + // setCurrentYakit(data) + // }) + // ipcRenderer.invoke("fetch-latest-yakit-version").then((data: string) => { + // isEnpriTraceAgent() ? setLatestYakit("") : setLatestYakit(data) + // }) + + // ipcRenderer + // .invoke("get-current-yak") + // .then((data: string) => { + // setCurrentYaklang(data) + // }) + // .catch(() => {}) + // ipcRenderer + // .invoke("fetch-latest-yaklang-version") + // .then((data: string) => { + // setLatestYaklang(data) + // }) + // .catch((err) => {}) + // } + // }) + // }, []) useEffect(() => { const id = setInterval(() => { @@ -246,7 +481,10 @@ const UILayout: React.FC = (props) => { if (databaseError.current && getSystem() !== "Windows_NT") { setYakitStatus("database") cacheYakitStatus.current = "database" - } else getCacheEngineMode() + } else { + if (cacheEngineMode.current === "remote") return + getCacheEngineMode(true) + } }) } }) @@ -266,69 +504,6 @@ const UILayout: React.FC = (props) => { } }, [engineLink]) - /** - * 获取yaklang引擎是否安装的状态 - * - 判断上次使用引擎的状态,如果有使用,这判断是否可以启动引擎进入软件界面(未安装状态只限远程可以进入软件界面) - * 1) 如果有使用,引擎未安装,则只限远程状态可以连接进入界面 - * 2) 如果有使用,引擎已安装,则正常连接上次使用状态的引擎 - */ - useEffect(() => { - outputToWelcomeConsole("识别引擎是否已安装...") - ipcRenderer - .invoke("is-yaklang-engine-installed") - .then((flag: boolean) => { - if (flag) { - outputToWelcomeConsole("引擎已安装") - } else { - outputToWelcomeConsole("引擎未安装") - } - isEngineInstalled.current = flag - }) - .finally(() => { - if (!isEngineInstalled.current) { - setEngineMode(undefined) - outputToWelcomeConsole("由于引擎未安装,仅开启远程模式或用户需安装核心引擎") - getCacheEngineMode() - setTimeout(() => { - setYakitStatus("install") - cacheYakitStatus.current = "install" - setLoading(false) - }, 300) - return - } else { - outputToWelcomeConsole("已安装引擎,开始检查数据库权限是否正常") - /** 引擎已安装的情况下,优先检查数据库权限 */ - ipcRenderer - .invoke("check-local-database") - .then((e) => { - if (e === "not allow to write") outputToWelcomeConsole("数据库权限错误,开始进行调整操作") - databaseError.current = e === "not allow to write" - }) - .finally(() => { - // 这里只有两种状态,数据库(有|无)权限情况 - if (databaseError.current && getSystem() !== "Windows_NT") { - setYakitStatus("database") - cacheYakitStatus.current = "database" - } else getCacheEngineMode() - setLoading(false) - }) - } - }) - - getLocalValue(LocalGV.YaklangEnginePort) - .then((portRaw) => { - const port = parseInt(portRaw) - if (!port) { - getRandomLocalEnginePort((p) => setLocalPort(p)) - } else { - setLocalPort(port) - } - }) - .catch(() => { - getRandomLocalEnginePort((p) => setLocalPort(p)) - }) - }, []) - /** * 根据引擎状态处理不同的方式 * 这儿并不直接控制啥时候发起连接,只是设置好对应的连接参数即可 @@ -428,7 +603,6 @@ const UILayout: React.FC = (props) => { const runControlRemote = useMemoizedFn((v: string, baseUrl: string) => { try { const resultObj: ResultObjProps = JSON.parse(v) - console.log("runControlRemote", resultObj, baseUrl) // 缓存远程控制参数 setDynamicStatus({...dynamicStatus, baseUrl, ...resultObj}) @@ -510,6 +684,7 @@ const UILayout: React.FC = (props) => { switch (type) { case "install": case "update": + if (checkLog.length > 0 && type === "install") setCheckLog([]) setYakitStatus("") cacheYakitStatus.current = "" getCacheEngineMode() @@ -869,14 +1044,16 @@ const UILayout: React.FC = (props) => { const [isJudgeLicense, setJudgeLicense] = useState(isEnterpriseEdition()) const [_, setLocalInfo, getLocalInfo] = useGetState() useEffect(() => { - // 获取操作系统、架构、Yakit 版本、Yak 版本 - ipcRenderer - .invoke("fetch-local-basic-info") - .then((data: LocalInfoProps) => { - setLocalInfo(data) - }) - .catch(() => {}) - }, []) + if (engineLink) { + // 获取操作系统、架构、Yakit 版本、Yak 版本 + ipcRenderer + .invoke("fetch-local-basic-info") + .then((data: LocalInfoProps) => { + setLocalInfo(data) + }) + .catch(() => {}) + } + }, [engineLink]) useEffect(() => { // 用户退出 - 验证license=>展示企业登录 ipcRenderer.on("again-judge-license-login", () => { @@ -969,15 +1146,39 @@ const UILayout: React.FC = (props) => { ) }, [screenRecorderInfo]) + // fuzzer-tab页数据订阅事件 + const unFuzzerCacheData = useRef(null) + /** chat-cs 功能逻辑 */ const [showChatCS, setShowChatCS] = useState(true) useEffect(() => { - getRemoteValue(RemoteGV.KnowChatCS) - .then((value: any) => { - if (!value) return - else setShowChatCS(false) - }) - .catch(() => {}) + if (engineLink) { + getRemoteValue(RemoteGV.KnowChatCS) + .then((value: any) => { + if (!value) return + else setShowChatCS(false) + }) + .catch(() => {}) + + // 开启fuzzer-tab页内数据的订阅事件 + if (unFuzzerCacheData.current) { + unFuzzerCacheData.current() + } + unFuzzerCacheData.current = usePageInfo.subscribe( + (state) => state.pages.get("httpFuzzer") || [], + (selectedState, previousSelectedState) => { + saveFuzzerCache(selectedState) + } + ) + + return () => { + // 注销fuzzer-tab页内数据的订阅事件 + if (unFuzzerCacheData.current) { + unFuzzerCacheData.current() + unFuzzerCacheData.current = null + } + } + } }, [engineLink]) const onChatCS = useMemoizedFn(() => { setShowChatCS(false) @@ -1271,6 +1472,7 @@ const UILayout: React.FC = (props) => { )} {!engineLink && !isRemoteEngine && ( = (props) => { onEngineModeChange={changeEngineMode} engineNotInstalled={!isEngineInstalled.current} oncancel={() => { + setLocalValue(LocalGV.YaklangEngineMode, "local") setEngineMode(undefined) cacheEngineMode.current = undefined - setYakitStatus("install") - cacheYakitStatus.current = "install" + if (isEngineInstalled.current) { + setEngineMode("local") + cacheEngineMode.current = "local" + } else { + handleLinkEngineInfo() + } }} /> )} diff --git a/app/renderer/src/main/src/components/yakitUI/YakitEditor/YakitEditor.tsx b/app/renderer/src/main/src/components/yakitUI/YakitEditor/YakitEditor.tsx index 08cf95f45f..4a468bd7ce 100644 --- a/app/renderer/src/main/src/components/yakitUI/YakitEditor/YakitEditor.tsx +++ b/app/renderer/src/main/src/components/yakitUI/YakitEditor/YakitEditor.tsx @@ -731,6 +731,7 @@ export const YakitEditor: React.FC = React.memo((props) => { (e) => true, (e) => { e.stopPropagation() + e.preventDefault() const filterKey = [16, 17, 18, 93] if (filterKey.includes(e.keyCode)) return diff --git a/app/renderer/src/main/src/index.tsx b/app/renderer/src/main/src/index.tsx index 2dcb6da466..5e5f8dd845 100644 --- a/app/renderer/src/main/src/index.tsx +++ b/app/renderer/src/main/src/index.tsx @@ -5,7 +5,7 @@ import "./index.css" import NewApp from "./NewApp" import {HTML5Backend} from "react-dnd-html5-backend" import {DndProvider} from "react-dnd" -import {createRoot} from "react-dom/client" +// import {createRoot} from "react-dom/client" import "./yakitUI.scss" import "./theme/yakit.scss" import "./yakitLib.scss" diff --git a/app/renderer/src/main/src/pages/layout/mainOperatorContent/MainOperatorContent.tsx b/app/renderer/src/main/src/pages/layout/mainOperatorContent/MainOperatorContent.tsx index 81510c442a..2e2312f177 100644 --- a/app/renderer/src/main/src/pages/layout/mainOperatorContent/MainOperatorContent.tsx +++ b/app/renderer/src/main/src/pages/layout/mainOperatorContent/MainOperatorContent.tsx @@ -16,7 +16,7 @@ import { GroupRightClickShowContentProps, OperateGroup, DroppableCloneProps, - SubTabsProps, + SubTabsProps } from "./MainOperatorContentType" import styles from "./MainOperatorContent.module.scss" import { @@ -49,7 +49,7 @@ import {YakitSecondaryConfirmProps, useSubscribeClose} from "@/store/tabSubscrib import {YakitModalConfirm, showYakitModal} from "@/components/yakitUI/YakitModal/YakitModalConfirm" import {defaultUserInfo} from "@/pages/MainOperator" import {useStore} from "@/store" -import {getLocalValue, getRemoteProjectValue, getRemoteValue, setRemoteValue} from "@/utils/kv" +import {getRemoteProjectValue, getRemoteValue, setRemoteValue} from "@/utils/kv" import {UnfinishedBatchTask, UnfinishedSimpleDetectBatchTask} from "@/pages/invoker/batch/UnfinishedBatchTaskList" import {GroupCount, QueryYakScriptsResponse} from "@/pages/invoker/schema" import {showModal} from "@/utils/showModal" @@ -95,8 +95,8 @@ import {startupDuplexConn, closeDuplexConn} from "@/utils/duplex/duplex" import cloneDeep from "lodash/cloneDeep" import {onToManageGroup} from "@/pages/securityTool/yakPoC/YakPoC" import {defPluginBatchExecuteExtraFormValue} from "@/pages/plugins/pluginBatchExecutor/pluginBatchExecutor" -import { apiFetchQueryYakScriptGroupLocal } from "@/pages/plugins/utils" -import { PluginGroupType } from "@/pages/plugins/group/PluginGroups" +import {apiFetchQueryYakScriptGroupLocal} from "@/pages/plugins/utils" +import {PluginGroupType} from "@/pages/plugins/group/PluginGroups" const TabRenameModalContent = React.lazy(() => import("./TabRenameModalContent")) const PageItem = React.lazy(() => import("./renderSubPage/RenderSubPage")) @@ -315,19 +315,26 @@ export const MainOperatorContent: React.FC = React.mem const [loading, setLoading] = useState(false) - const {setPagesData, setSelectGroupId, addPagesDataCache, pages, clearAllData, updatePagesDataCacheById,getCurrentSelectPageId} = - usePageInfo( - (s) => ({ - setPagesData: s.setPagesData, - setSelectGroupId: s.setSelectGroupId, - addPagesDataCache: s.addPagesDataCache, - pages: s.pages, - clearAllData: s.clearAllData, - updatePagesDataCacheById: s.updatePagesDataCacheById, - getCurrentSelectPageId:s.getCurrentSelectPageId - }), - shallow - ) + const { + setPagesData, + setSelectGroupId, + addPagesDataCache, + pages, + clearAllData, + updatePagesDataCacheById, + getCurrentSelectPageId + } = usePageInfo( + (s) => ({ + setPagesData: s.setPagesData, + setSelectGroupId: s.setSelectGroupId, + addPagesDataCache: s.addPagesDataCache, + pages: s.pages, + clearAllData: s.clearAllData, + updatePagesDataCacheById: s.updatePagesDataCacheById, + getCurrentSelectPageId: s.getCurrentSelectPageId + }), + shallow + ) // tab数据 const [pageCache, setPageCache, getPageCache] = useGetState(_.cloneDeepWith(getInitPageCache()) || []) @@ -563,8 +570,8 @@ export const MainOperatorContent: React.FC = React.mem openMenuPage({route: YakitRoute.Plugin_Store}) }) - const pluginGroup = useMemoizedFn((data: { pluginGroupType: PluginGroupType }) => { - const { pluginGroupType = "local" } = data || {} + const pluginGroup = useMemoizedFn((data: {pluginGroupType: PluginGroupType}) => { + const {pluginGroupType = "local"} = data || {} openMenuPage( {route: YakitRoute.Plugin_Groups}, { @@ -635,7 +642,7 @@ export const MainOperatorContent: React.FC = React.mem if (type === "**diagnose-network") openMenuPage({route: YakitRoute.Beta_DiagnoseNetwork}) if (type === "**config-network") openMenuPage({route: YakitRoute.Beta_ConfigNetwork}) if (type === "**beta-debug-traffic-analize") openMenuPage({route: YakitRoute.Beta_DebugTrafficAnalize}) - if (type === "**webshell-manager") openMenuPage({ route: YakitRoute.Beta_WebShellManager }) + if (type === "**webshell-manager") openMenuPage({route: YakitRoute.Beta_WebShellManager}) if (type === "**webshell-opt") addWebShellOpt(data) if (type === "open-plugin-store") { @@ -661,11 +668,14 @@ export const MainOperatorContent: React.FC = React.mem openMenuPage({route: YakitRoute.DNSLog}) } if (type === YakitRoute.BatchExecutorPage) { - openMenuPage({route: YakitRoute.BatchExecutorPage},{ - pageParams:{ - ...data + openMenuPage( + {route: YakitRoute.BatchExecutorPage}, + { + pageParams: { + ...data + } } - }) + ) } console.info("send to tab: ", type) }) @@ -683,7 +693,7 @@ export const MainOperatorContent: React.FC = React.mem { pageParams: { id: Id, - webshellInfo: res, + webshellInfo: res }, hideAdd: true } @@ -712,19 +722,21 @@ export const MainOperatorContent: React.FC = React.mem } }) /** websocket fuzzer 和 Fuzzer 类似 */ - const addWebsocketFuzzer = useMemoizedFn((res: { tls: boolean; request: Uint8Array, openFlag?: boolean, toServer?: Uint8Array }) => { - openMenuPage( - {route: YakitRoute.WebsocketFuzzer}, - { - openFlag: res.openFlag, - pageParams: { - wsToServer: res.toServer, - wsRequest: res.request, - wsTls: res.tls + const addWebsocketFuzzer = useMemoizedFn( + (res: {tls: boolean; request: Uint8Array; openFlag?: boolean; toServer?: Uint8Array}) => { + openMenuPage( + {route: YakitRoute.WebsocketFuzzer}, + { + openFlag: res.openFlag, + pageParams: { + wsToServer: res.toServer, + wsRequest: res.request, + wsTls: res.tls + } } - } - ) - }) + ) + } + ) /** 数据对比*/ const addDataCompare = useMemoizedFn((res: {leftData: string; rightData: string}) => { openMenuPage( @@ -793,8 +805,8 @@ export const MainOperatorContent: React.FC = React.mem } ) setBugTestValue("") - setBugUrl("") - } + setBugUrl("") + } }) const addYakRunning = useMemoizedFn((res: any) => { const {name = "", code = ""} = res || {} @@ -920,10 +932,8 @@ export const MainOperatorContent: React.FC = React.mem }, []) /** ---------- 远程关闭一级页面 end ---------- */ - /** - * @name 全局功能快捷键 - */ - const documentKeyDown = useMemoizedFn((e: any) => { + /** ---------- @name 全局功能快捷键 Start ---------- */ + const documentKeyDown = useMemoizedFn((e: KeyboardEvent) => { // ctrl/command + w 关闭当前页面 e.stopPropagation() if (e.code === "KeyW" && (e.ctrlKey || e.metaKey)) { @@ -946,8 +956,12 @@ export const MainOperatorContent: React.FC = React.mem } }) useEffect(() => { - document.onkeydown = documentKeyDown + document.addEventListener("keydown", documentKeyDown) + return () => { + document.removeEventListener("keydown", documentKeyDown) + } }, []) + /** ---------- @name 全局功能快捷键 End ---------- */ /** ---------- 操作系统 start ---------- */ // 系统类型 @@ -1049,7 +1063,7 @@ export const MainOperatorContent: React.FC = React.mem // 请勿随意调整执行顺序,先加页面的数据,再新增页面,以便于设置页面初始值 switch (route) { case YakitRoute.HTTPFuzzer: - addFuzzerList(node.id, node, order) + addFuzzerList(node.id, node, order) break case YakitRoute.Space_Engine: onSetSpaceEngineData(node, order) @@ -1070,7 +1084,7 @@ export const MainOperatorContent: React.FC = React.mem switch (route) { case YakitRoute.HTTPFuzzer: - addFuzzerList(node.id, node, 1) + addFuzzerList(node.id, node, 1) break case YakitRoute.Space_Engine: onSetSpaceEngineData(node, 1) @@ -1368,8 +1382,8 @@ export const MainOperatorContent: React.FC = React.mem const tabName = routeKeyToLabel.get(key) || menuName let pageNodeInfo: PageProps = { ...cloneDeep(defPage), - currentSelectPageId:getCurrentSelectPageId(YakitRoute.HTTPFuzzer)||'', - routeKey: YakitRoute.HTTPFuzzer, + currentSelectPageId: getCurrentSelectPageId(YakitRoute.HTTPFuzzer) || "", + routeKey: YakitRoute.HTTPFuzzer } let multipleNodeListLength: number = 0 const multipleNodeList: MultipleNodeInfo[] = cache.filter((ele) => ele.groupId === "0") @@ -1577,7 +1591,7 @@ export const MainOperatorContent: React.FC = React.mem let pageNodeInfo: PageProps = { ...cloneDeep(defPage), pageList: oldPageList || [], - routeKey: YakitRoute.HTTPFuzzer, + routeKey: YakitRoute.HTTPFuzzer } pageNodeInfo.pageList = [...pageNodeInfo.pageList, ...pageList] @@ -1735,7 +1749,7 @@ export const MainOperatorContent: React.FC = React.mem const TabContent: React.FC = React.memo((props) => { const {currentTabKey, setCurrentTabKey, onRemove, pageCache, setPageCache, openMultipleMenuPage} = props /** ---------- 拖拽排序 start ---------- */ - const onDragEnd = useMemoizedFn((result: DropResult,provided: ResponderProvided) => { + const onDragEnd = useMemoizedFn((result: DropResult, provided: ResponderProvided) => { if (!result.destination) { return } @@ -2233,7 +2247,7 @@ const SubTabs: React.FC = React.memo( const [scroll, setScroll] = useState({ scrollLeft: 0, scrollBottom: 0, - scrollRight: 0 + scrollRight: 0 }) const [closeGroupTip, setCloseGroupTip] = useState(true) // 关闭组的时候是否还需要弹窗提示,默认是要弹窗的;如果用户选择了不再提示,后续则就不需要再弹出提示框 @@ -2329,17 +2343,17 @@ const SubTabs: React.FC = React.memo( } if (selectSubMenu.id !== "0") { if (selectSubMenu.groupId === "0") { - if(currentTabKey === YakitRoute.HTTPFuzzer) setType("config") + if (currentTabKey === YakitRoute.HTTPFuzzer) setType("config") removeCurrentSelectGroupId(currentTabKey) } else { - if(currentTabKey === YakitRoute.HTTPFuzzer){ - addFuzzerSequenceList({ - groupId: selectSubMenu.groupId - }) + if (currentTabKey === YakitRoute.HTTPFuzzer) { + addFuzzerSequenceList({ + groupId: selectSubMenu.groupId + }) } setSelectGroupId(currentTabKey, selectSubMenu.groupId) } - setCurrentSelectPageId(currentTabKey,selectSubMenu.id) + setCurrentSelectPageId(currentTabKey, selectSubMenu.id) } }, [selectSubMenu]) useLongPress( @@ -2543,7 +2557,7 @@ const SubTabs: React.FC = React.memo( if (currentTabKey === YakitRoute.HTTPFuzzer) { addFuzzerSequenceList({groupId: combineItem.id}) } - onAddGroupsAndThenSort(combineItem, subPage,currentTabKey) + onAddGroupsAndThenSort(combineItem, subPage, currentTabKey) }) /**@description 组内向组外合并 */ @@ -2558,7 +2572,7 @@ const SubTabs: React.FC = React.memo( if (droppableId.includes("group") === combineDraggableId.includes("group")) { // 不能正常拖拽的时候,两个组之间的拖拽交互失效 // 所以执行movingBetweenDifferentGroups,不走合并的逻辑 - const newResult:DropResult = { + const newResult: DropResult = { combine: null, destination: { droppableId: combineDraggableId, @@ -2612,7 +2626,7 @@ const SubTabs: React.FC = React.memo( }) } onUpdatePageCache(subPage) - onAddGroupsAndThenSort(combineItem, subPage,currentTabKey) + onAddGroupsAndThenSort(combineItem, subPage, currentTabKey) if (currentTabKey === YakitRoute.HTTPFuzzer) { addFuzzerSequenceList({ groupId: combineItem.id @@ -2713,7 +2727,7 @@ const SubTabs: React.FC = React.memo( }) } onUpdatePageCache(subPage) - onUpdateSorting(subPage,currentTabKey) + onUpdateSorting(subPage, currentTabKey) }) /** @description 组内向组外移动 */ @@ -2755,7 +2769,7 @@ const SubTabs: React.FC = React.memo( if (currentTabKey === YakitRoute.HTTPFuzzer) { removeWithinGroupDataById(sourceItem.groupId, newSourceItem.id) } - onUpdateSorting(subPage,currentTabKey) + onUpdateSorting(subPage, currentTabKey) }) /** @description 组外向组内移动 */ const moveOutOfGroupAndInGroup = useMemoizedFn((result: DropResult) => { @@ -2797,7 +2811,7 @@ const SubTabs: React.FC = React.memo( // 将拖拽的item从来源地中删除 subPage.splice(sourceIndex, 1) onUpdatePageCache(subPage) - onUpdateSorting(subPage,currentTabKey) + onUpdateSorting(subPage, currentTabKey) }) /** 更新pageCache和subPage,保证二级新开tab后顺序不变 */ const onUpdatePageCache = useMemoizedFn((subMenuList: MultipleNodeInfo[]) => { @@ -2924,7 +2938,7 @@ const SubTabs: React.FC = React.memo( } } onUpdatePageCache([...subPage]) - onUpdateSorting(subPage,currentTabKey) + onUpdateSorting(subPage, currentTabKey) }) /** * @description 页面节点的右键点击事件 @@ -3035,10 +3049,10 @@ const SubTabs: React.FC = React.memo( onUpdatePageCache(subPage) } } - const updateItem = { - ...item, - verbose: val - } + const updateItem = { + ...item, + verbose: val + } onUpdatePageName(currentTabKey, updateItem) m.destroy() }} @@ -3053,7 +3067,7 @@ const SubTabs: React.FC = React.memo( if (!current) return current.pageName = param.verbose updatePagesDataCacheById(currentTabKey, current) - emiter.emit('secondMenuTabDataChange','') + emiter.emit("secondMenuTabDataChange", "") }) /**将页面添加到新建组 */ const onNewGroup = useMemoizedFn((item: MultipleNodeInfo) => { @@ -3096,7 +3110,7 @@ const SubTabs: React.FC = React.memo( if (currentTabKey === YakitRoute.HTTPFuzzer) { onUpdateFuzzerSequenceCacheData(item) } - onAddGroupsAndThenSort(newGroup, subPage,currentTabKey) + onAddGroupsAndThenSort(newGroup, subPage, currentTabKey) onUpdatePageCache([...subPage]) }) /**将标签页移动到组 */ @@ -3137,7 +3151,7 @@ const SubTabs: React.FC = React.memo( if (currentTabKey === YakitRoute.HTTPFuzzer) { onUpdateFuzzerSequenceCacheData(item) } - onUpdateSorting(subPage,currentTabKey) + onUpdateSorting(subPage, currentTabKey) }) /**从组中移出 */ const onRemoveFromGroup = useMemoizedFn((item: MultipleNodeInfo) => { @@ -3170,7 +3184,7 @@ const SubTabs: React.FC = React.memo( if (currentTabKey === YakitRoute.HTTPFuzzer) { onUpdateFuzzerSequenceCacheData(item) } - onUpdateSorting(subPage,currentTabKey) + onUpdateSorting(subPage, currentTabKey) }) /**更新全局变量中得序列缓存数据 */ const onUpdateFuzzerSequenceCacheData = useMemoizedFn((item: MultipleNodeInfo) => { @@ -3203,15 +3217,12 @@ const SubTabs: React.FC = React.memo( const newSubPage: MultipleNodeInfo[] = [item] onSetSelectSubMenu(item) onUpdatePageCache(newSubPage) - const current: PageNodeItemProps | undefined = queryPagesDataById( - currentTabKey, - item.id - ) + const current: PageNodeItemProps | undefined = queryPagesDataById(currentTabKey, item.id) if (current) { const pages: PageProps = { ...cloneDeep(defPage), pageList: [{...current, sortFieId: 1}], - routeKey: currentTabKey, + routeKey: currentTabKey } setPagesData(currentTabKey, pages) } @@ -3241,10 +3252,7 @@ const SubTabs: React.FC = React.memo( onSetSelectSubMenu(item) onUpdatePageCache(subPage) //更新fuzzer缓存 - const current: PageNodeItemProps | undefined = queryPagesDataById( - currentTabKey, - item.id - ) + const current: PageNodeItemProps | undefined = queryPagesDataById(currentTabKey, item.id) if (current) { const groupList = [{...current, sortFieId: 1}] setPageNodeInfoByPageGroupId(currentTabKey, item.groupId, groupList) @@ -3312,7 +3320,7 @@ const SubTabs: React.FC = React.memo( groupId: current.id }) } - onUpdateSorting(subPage,currentTabKey) + onUpdateSorting(subPage, currentTabKey) }) /**更新两个item的排序字段 */ const onExchangeOrderPages = useMemoizedFn( @@ -3327,14 +3335,8 @@ const SubTabs: React.FC = React.memo( sortFieId: number } ) => { - const currentSource: PageNodeItemProps | undefined = queryPagesDataById( - routeKey, - source.id - ) - const currentDestination: PageNodeItemProps | undefined = queryPagesDataById( - routeKey, - destination.id - ) + const currentSource: PageNodeItemProps | undefined = queryPagesDataById(routeKey, source.id) + const currentDestination: PageNodeItemProps | undefined = queryPagesDataById(routeKey, destination.id) if (currentSource) { updatePagesDataCacheById(routeKey, { ...currentSource, @@ -3348,29 +3350,31 @@ const SubTabs: React.FC = React.memo( }) } setTimeout(() => { - emiter.emit('secondMenuTabDataChange','') - }, 200); + emiter.emit("secondMenuTabDataChange", "") + }, 200) } ) /**先添加新组,再onUpdateSorting */ - const onAddGroupsAndThenSort = useMemoizedFn((newGroup:MultipleNodeInfo, subPage:MultipleNodeInfo[],currentRouteKey:string) => { - // 先添加新组,再onUpdateSorting - const newPageGroupNode: PageNodeItemProps = { - id: `${randomString(8)}-${newGroup.sortFieId}`, - routeKey: currentRouteKey, - pageGroupId: "0", - pageId: newGroup.id, - pageName: newGroup.verbose, - pageParamsInfo: {}, - sortFieId: newGroup.sortFieId, - expand: newGroup.expand, - color: newGroup.color + const onAddGroupsAndThenSort = useMemoizedFn( + (newGroup: MultipleNodeInfo, subPage: MultipleNodeInfo[], currentRouteKey: string) => { + // 先添加新组,再onUpdateSorting + const newPageGroupNode: PageNodeItemProps = { + id: `${randomString(8)}-${newGroup.sortFieId}`, + routeKey: currentRouteKey, + pageGroupId: "0", + pageId: newGroup.id, + pageName: newGroup.verbose, + pageParamsInfo: {}, + sortFieId: newGroup.sortFieId, + expand: newGroup.expand, + color: newGroup.color + } + addPagesDataCache(currentRouteKey, newPageGroupNode) + onUpdateSorting(subPage, currentRouteKey) } - addPagesDataCache(currentRouteKey, newPageGroupNode) - onUpdateSorting(subPage,currentRouteKey) - }) + ) /**更新排序和组内的页面所属组id 所有二级菜单的排序 */ - const onUpdateSorting = useMemoizedFn((subPage: MultipleNodeInfo[],currentRouteKey:string) => { + const onUpdateSorting = useMemoizedFn((subPage: MultipleNodeInfo[], currentRouteKey: string) => { const pageList: PageNodeItemProps[] = [] subPage.forEach((ele, index) => { if (ele.groupChildren && ele.groupChildren.length > 0) { @@ -3391,7 +3395,7 @@ const SubTabs: React.FC = React.memo( const pages: PageProps = { ...cloneDeep(defPage), pageList: pageList, - routeKey: currentRouteKey, + routeKey: currentRouteKey } setPagesData(currentRouteKey, pages) }) @@ -3425,7 +3429,7 @@ const SubTabs: React.FC = React.memo( onUpdateSelectSubPage(groupItem) subPage.splice(index, 1) onUpdatePageCache([...subPage]) - onUpdateSorting(subPage,currentTabKey) + onUpdateSorting(subPage, currentTabKey) if (currentTabKey === YakitRoute.HTTPFuzzer) { removeFuzzerSequenceList({ groupId: groupItem.id @@ -3444,12 +3448,9 @@ const SubTabs: React.FC = React.memo( const newPage = [{...groupItem}] onSetSelectSubMenu(groupItem) onUpdatePageCache(newPage) - const currentGroupList: PageNodeItemProps[] = getPagesDataByGroupId( - currentTabKey, - groupItem.id - ) + const currentGroupList: PageNodeItemProps[] = getPagesDataByGroupId(currentTabKey, groupItem.id) const currentGroupItem: PageNodeItemProps | undefined = queryPagesDataById( - currentTabKey, + currentTabKey, groupItem.id ) @@ -3459,7 +3460,7 @@ const SubTabs: React.FC = React.memo( ...cloneDeep(defPage), pageList: [...newPageList, {...currentGroupItem, sortFieId: 1}], routeKey: currentTabKey, - singleNode: false, + singleNode: false } setPagesData(currentTabKey, pageNodeInfo) } @@ -3531,7 +3532,7 @@ const SubTabs: React.FC = React.memo( pageName: groupItem.verbose } updatePagesDataCacheById(currentTabKey, newCurrentGroup) - emiter.emit('secondMenuTabDataChange','') + emiter.emit("secondMenuTabDataChange", "") } }) const onDragStart = useMemoizedFn((result: DragStart, provided: ResponderProvided) => { @@ -4099,7 +4100,7 @@ const onModalSecondaryConfirm = (props?: YakitSecondaryConfirmProps) => { type: "white", onCancelText: "不保存", onOkText: "保存", - keyboard:false, + keyboard: false, zIndex: 1010, ...(props || {}), onOk: () => { diff --git a/app/renderer/src/main/src/store/pageInfo.ts b/app/renderer/src/main/src/store/pageInfo.ts index 3a90d8cded..c9cef1e17e 100644 --- a/app/renderer/src/main/src/store/pageInfo.ts +++ b/app/renderer/src/main/src/store/pageInfo.ts @@ -20,7 +20,7 @@ export interface PageProps { pageList: PageNodeItemProps[] routeKey: string singleNode: boolean - currentSelectPageId:string + currentSelectPageId: string } export interface PageNodeItemProps { @@ -134,7 +134,7 @@ interface PageInfoStoreProps { /**只保留routeKey的数据,删除除此routeKey之外的数据 */ clearOtherDataByRoute: (routeKey: string) => void /** 设置当前激活的页面id */ - setCurrentSelectPageId: (routeKey: string,pageId:string) => void + setCurrentSelectPageId: (routeKey: string, pageId: string) => void /** 获取当前激活的页面id */ getCurrentSelectPageId: (routeKey: string) => string } @@ -142,7 +142,7 @@ export const defPage: PageProps = { pageList: [], routeKey: "", singleNode: false, - currentSelectPageId:'' + currentSelectPageId: "" } export const usePageInfo = createWithEqualityFn()( subscribeWithSelector( @@ -293,19 +293,19 @@ export const usePageInfo = createWithEqualityFn()( selectGroupId: new Map().set(key, newSelectGroupId) }) }, - setCurrentSelectPageId: (key,pageId) => { + setCurrentSelectPageId: (key, pageId) => { const newPages = get().pages const currentPage = newPages.get(key) || cloneDeep(defPage) - newPages.set(key,{ + newPages.set(key, { ...currentPage, - currentSelectPageId:pageId + currentSelectPageId: pageId }) set({ ...get(), - pages: newPages + pages: newPages }) }, - getCurrentSelectPageId:(key)=>{ + getCurrentSelectPageId: (key) => { const {pages} = get() const current = pages.get(key) || cloneDeep(defPage) return current.currentSelectPageId @@ -350,55 +350,96 @@ export const usePageInfo = createWithEqualityFn()( ), Object.is ) -try { - /** - * @description 打开软化后这个订阅会一直存在,直到关闭软件;后续再看看优化方法 - */ - const unFuzzerCacheData = usePageInfo.subscribe( - // (state) => state.pages.get(YakitRoute.HTTPFuzzer) || [], - (state) => state.pages.get("httpFuzzer") || [], // 因为循环引用导致开发环境热加载YakitRoute.HTTPFuzzer为undefined - (selectedState, previousSelectedState) => { - saveFuzzerCache(selectedState) - } - ) - const saveFuzzerCache = debounce( - (selectedState: PageProps) => { - try { - const {pageList = []} = selectedState || { - pageList: [] - } - const cache = pageList.map((ele) => { - const advancedConfigValue = - ele.pageParamsInfo?.webFuzzerPageInfo?.advancedConfigValue || defaultAdvancedConfigValue - return { - groupChildren: [], - groupId: ele.pageGroupId, - id: ele.pageId, - pageParams: { - actualHost: advancedConfigValue.actualHost || "", - id: ele.pageId, - isHttps: advancedConfigValue.isHttps, - request: ele.pageParamsInfo?.webFuzzerPageInfo?.request || defaultPostTemplate, - params: advancedConfigValue.params, - extractors: advancedConfigValue.extractors - }, - sortFieId: ele.sortFieId, - verbose: ele.pageName, - expand: ele.expand, - color: ele.color - } - }) - // console.log("saveFuzzerCache", cache) - // console.table(pageList) - setRemoteProjectValue(RemoteGV.FuzzerCache, JSON.stringify(cache)).catch((error) => {}) - } catch (error) { - yakitNotify("error", "webFuzzer缓存数据失败:" + error) +export const saveFuzzerCache = debounce( + (selectedState: PageProps) => { + try { + const {pageList = []} = selectedState || { + pageList: [] } - }, - 500, - {leading: true} - ) -} catch (error) { - yakitNotify("error", "page-info缓存数据错误:" + error) -} + const cache = pageList.map((ele) => { + const advancedConfigValue = + ele.pageParamsInfo?.webFuzzerPageInfo?.advancedConfigValue || defaultAdvancedConfigValue + return { + groupChildren: [], + groupId: ele.pageGroupId, + id: ele.pageId, + pageParams: { + actualHost: advancedConfigValue.actualHost || "", + id: ele.pageId, + isHttps: advancedConfigValue.isHttps, + request: ele.pageParamsInfo?.webFuzzerPageInfo?.request || defaultPostTemplate, + params: advancedConfigValue.params, + extractors: advancedConfigValue.extractors + }, + sortFieId: ele.sortFieId, + verbose: ele.pageName, + expand: ele.expand, + color: ele.color + } + }) + setRemoteProjectValue(RemoteGV.FuzzerCache, JSON.stringify(cache)).catch((error) => {}) + } catch (error) { + yakitNotify("error", "webFuzzer缓存数据失败:" + error) + } + }, + 500, + {leading: true} +) + +/** + * 下面注释的代码含义 + * fuzzer-tab页内数据的订阅事件,订阅数据包括request、请求参数、序列组配置信息等 + * 注释原因: + * 软件打开后,这个订阅就会启动,导致还没连接引擎时就请求引擎相关接口,导致控制台报错 + */ +// try { +// const unFuzzerCacheData = usePageInfo.subscribe( +// // (state) => state.pages.get(YakitRoute.HTTPFuzzer) || [], +// (state) => state.pages.get("httpFuzzer") || [], // 因为循环引用导致开发环境热加载YakitRoute.HTTPFuzzer为undefined +// (selectedState, previousSelectedState) => { +// saveFuzzerCache(selectedState) +// } +// ) + +// const saveFuzzerCache = debounce( +// (selectedState: PageProps) => { +// try { +// const {pageList = []} = selectedState || { +// pageList: [] +// } +// const cache = pageList.map((ele) => { +// const advancedConfigValue = +// ele.pageParamsInfo?.webFuzzerPageInfo?.advancedConfigValue || defaultAdvancedConfigValue +// return { +// groupChildren: [], +// groupId: ele.pageGroupId, +// id: ele.pageId, +// pageParams: { +// actualHost: advancedConfigValue.actualHost || "", +// id: ele.pageId, +// isHttps: advancedConfigValue.isHttps, +// request: ele.pageParamsInfo?.webFuzzerPageInfo?.request || defaultPostTemplate, +// params: advancedConfigValue.params, +// extractors: advancedConfigValue.extractors +// }, +// sortFieId: ele.sortFieId, +// verbose: ele.pageName, +// expand: ele.expand, +// color: ele.color +// } +// }) +// // console.log("saveFuzzerCache", cache) +// // console.table(pageList) +// console.log("cache", cache) +// setRemoteProjectValue(RemoteGV.FuzzerCache, JSON.stringify(cache)).catch((error) => {}) +// } catch (error) { +// yakitNotify("error", "webFuzzer缓存数据失败:" + error) +// } +// }, +// 500, +// {leading: true} +// ) +// } catch (error) { +// yakitNotify("error", "page-info缓存数据错误:" + error) +// }