import { getCapturePhotoYunSign, saveCapturePhoto } from "@/api/login"; import { HOST_FILE_HASH_MAP } from "@/constants/constants"; import { throttle, xor } from "lodash-es"; export function isElectron() { return typeof window.nodeRequire != "undefined"; } const fs: typeof import("fs") = isElectron() && window.nodeRequire("fs"); /** 执行本地exe文件 */ export function execLocal(exeName: string): Promise { if (!isElectron()) { logger({ pgu: "AUTO", cnl: ["local", "server"], dtl: "不在Electron中,调用 " + exeName + " 失败", }); throw new Error("不在Electron中,调用 fs 失败"); } if (import.meta.env.DEV) { return Promise.resolve(); } return new Promise((resolve) => { // eslint-disable-next-line window .nodeRequire("node-cmd") .get( "cmd /c chcp 65001>nul && " + exeName, async (err: Error, data: string, stderr: string) => { console.log(exeName, err, data); // 未免过多日志,此处后续可以关闭 logger({ pgu: "AUTO", cnl: ["console", "local", "server"], act: exeName + " called", ejn: JSON.stringify(err), dtl: data, ext: { stderr: JSON.stringify(stderr), absPath: exeName.includes(":"), }, }); // 如果相对路径没找到,则通过绝对路径来执行 if (!exeName.includes(":") && err) { const remote: typeof import("electron").remote = window.nodeRequire("electron").remote; const fs: typeof import("fs") = remote.require("fs"); const path: typeof import("path") = remote.require("path"); const [exePath, exeParams] = exeName.split(" "); const absPath = path.join( remote.app.getAppPath(), "../../", exePath ); if (fs.existsSync(absPath)) { try { await execLocal([absPath, exeParams].join(" ").trim()); } catch (error) { console.log("second try error", absPath); logger({ cnl: ["local", "server"], dtl: "second try error: " + absPath, }); } } } resolve(); } ); }); } /** 文件路径是否存在 */ export function fileExists(file: string): boolean { if (!isElectron()) { logger({ pgu: "AUTO", cnl: ["local", "server"], dtl: "不在Electron中,调用 fs 失败", }); throw new Error("不在Electron中,调用 fs 失败"); } return fs.existsSync(file); } /** 检测当前运行的进程中是否有“向日葵” */ export function nodeCheckRemoteDesktop(): boolean { logger({ pgu: "AUTO", cnl: ["local", "server"], key: "nodeCheckRemoteDesktop", }); // eslint-disable-next-line @typescript-eslint/no-unsafe-call const appList: string = window .nodeRequire("child_process") .execSync("cmd /c chcp 65001>nul && tasklist /FO CSV") .toString(); // console.debug(appList); const regex = new RegExp("sunloginclient|选择免安装运行,截图识别", "gi"); return !!(appList?.match(regex) || []).length; } let previousAppList: string[] = []; /** 将电脑运行的进程记录到日志 */ export function nodeCheckProcess(): void { try { // eslint-disable-next-line @typescript-eslint/no-unsafe-call const appListCP: string = window .nodeRequire("child_process") .execSync("cmd /c chcp 65001>nul && tasklist /FO CSV") .toString(); // 不能打印,electron-log 不能接收 // console.log(appList); let appList = appListCP.split("\r\n"); appList.shift(); appList = appList.map((v) => v.split(",")[0]); const xorRes = xor(previousAppList, appList); if (xorRes && xorRes.length > 0) { logger({ pgu: "AUTO", cnl: ["local", "server"], key: "nodeCheckProcess", dtl: xorRes.join(" ||| "), }); } previousAppList = appList; } catch (error) { logger({ cnl: ["console", "local", "server"], pgu: "AUTO", ejn: JSON.stringify(error), }); } } /** 检测当前程序包的完整性。 */ export function checkMainExe(): boolean { try { let iid: string = window.nodeRequire("process").pid; const cp: typeof import("child_process") = window.nodeRequire("child_process"); iid = cp .execSync( `cmd /c chcp 65001>nul && C:\\Windows\\System32\\wbem\\wmic process where ^(processid^=${iid}^) get parentprocessid /value` ) .toString(); iid = iid.replace("ParentProcessId=", "").trim(); console.log(iid); logger({ cnl: ["local", "console", "server"], key: "checkMainExe", dtl: `iid1: ${iid}`, }); iid = cp .execSync( `cmd /c chcp 65001>nul && C:\\Windows\\System32\\wbem\\wmic process where ^(processid^=${iid}^) get parentprocessid /value` ) .toString(); iid = iid.replace("ParentProcessId=", "").trim(); logger({ cnl: ["local", "console", "server"], key: "checkMainExe", dtl: `iid2: ${iid}`, }); const executablePathBuffer = cp.execSync( `cmd /c chcp 65001>nul && C:\\Windows\\System32\\wbem\\wmic process where ^(processid^=${iid}^) get executablepath /value` ); console.log(executablePathBuffer); const encoding = window.nodeRequire("encoding"); // eslint-disable-next-line @typescript-eslint/no-unsafe-call let executablePath: string = encoding .convert(executablePathBuffer, "utf8", "gbk") .toString(); logger({ cnl: ["local", "console", "server"], key: "checkMainExe", dtl: executablePath, }); executablePath = executablePath .replace("ExecutablePath=", "") .trim() .replace(/&/g, "&"); if (executablePath === eval(`proceess.env.PORTABLE_EXECUTABLE_FILE`)) { const crypto: typeof import("crypto") = window.nodeRequire("crypto"); const getHash = crypto .createHmac("sha256", "abcdefg") .update(fs.readFileSync(executablePath)) .digest("hex"); console.log("the hash: ", getHash); logger({ cnl: ["local", "console", "server"], key: "checkMainExe", dtl: `the hash: ${getHash}`, }); if (HOST_FILE_HASH_MAP.get(window.location.hostname) === getHash) { return true; } } // check filepath executablePath md5 } catch (error) { console.log(error); logger({ cnl: ["local", "console", "server"], key: "checkMainExe", ejn: JSON.stringify(error), }); } return false; } /** 初始化桌面抓拍 */ export function initScreenShot() { if (import.meta.env.DEV) return; if (!isElectron()) return; function handleStream(stream: MediaStream) { const video: HTMLVideoElement = document.querySelector("#ssVideo")!; video.srcObject = stream; video.onloadedmetadata = () => video.play(); } const electron: typeof import("electron") = window.nodeRequire("electron"); electron.desktopCapturer.getSources( { types: ["window", "screen"] }, (e: any, sources: { id: string; name: string }[]) => { console.log(e, sources); for (const source of sources) { console.log(source); if (source.name === "Entire screen") { try { navigator.mediaDevices .getUserMedia({ audio: false, video: { // @ts-expect-error 不确定是chrome/electron标准是否不一样,需要测试 mandatory: { chromeMediaSource: "desktop", chromeMediaSourceId: source.id, minWidth: 600, maxWidth: 600, minHeight: 480, maxHeight: 480, }, }, }) .then((stream) => handleStream(stream)) .catch((e) => logger({ cnl: ["local", "server"], pgu: "AUTO", act: "ss-failed", possibleError: e, }) ); } catch (err) { logger({ cnl: ["local", "server"], pgu: "AUTO", act: "ss-failed", ejn: JSON.stringify(err), }); } return; } } } ); } /** 保存当前屏幕截图 */ export async function getScreenShot({ cause = "ss-none" }) { const video: HTMLVideoElement = document.querySelector("#ssVideo")!; async function getSnapShot() { return new Promise((resolve, reject) => { if (video.readyState !== 4 || !(video.srcObject)?.active) { reject("desktop没有正常启用"); } video.pause(); const canvas = document.createElement("canvas"); canvas.width = 220; canvas.height = 165; const context = canvas.getContext("2d"); context?.drawImage(video, 0, 0, 220, 165); canvas.toBlob(resolve, "image/png", 0.95); }); } if (window.location.pathname.includes("/login")) return; const captureBlob = await getSnapShot(); const res = await getCapturePhotoYunSign({ fileSuffix: "png" }); try { await saveCapturePhoto(res.data.formUrl, res.data.formParams, { file: captureBlob, }); logger({ pgu: "AUTO", cnl: ["server"], dtl: "桌面抓拍保存成功", ext: { resultUrl: res.data.accessUrl, cause, }, }); } catch (error) { console.log(error); logger({ pgu: "AUTO", cnl: ["server"], dtl: "桌面抓拍失败", ejn: JSON.stringify(error), ext: { cause, }, }); } finally { video && video.play(); } } /** 在app resize时触发 */ export function registerOnResize() { const throttledResizeLog = throttle(() => { logger({ cnl: ["local", "server"], pgu: "AUTO", act: "registerOnResize", ext: { width: window.screen.width, height: window.screen.height, screenX: window.screen.availWidth, screenY: window.screen.availHeight, clientWidth: document.documentElement.clientWidth, clientHeight: document.documentElement.clientHeight, windowInnerWidth: window.innerWidth, windowInnerHeight: window.innerHeight, windowOuterWidth: window.outerWidth, windowOuterHeight: window.outerHeight, }, }); void getScreenShot({ cause: "ss-registerOnResize" }); }, 3000); window.onresize = throttledResizeLog; }