import { BrowserWindow, ipcMain, screen, session } from "electron"; import http from 'http'; import https from 'https'; import os from 'os'; import { URL } from 'url'; import { ApiResponse, MenuItem, TagResolutionConfig, EIACDesktopApi, SpecialPUrlItem, FaultReportingResponse } from './EIAC_Desktop_Api'; const memoryCache = new Map(); export function initialize(): void { // Set cache ipcMain.handle('cache:set', (_event, key: string, value: any) => { memoryCache.set(key, value); }); // Get cache ipcMain.handle('cache:get', (_event, key: string) => { return memoryCache.get(key) ?? null; }); // Get menu cache ipcMain.handle('get-menu-cache', async () => menuData ?? await menuDataReadyPromise); // Get config cache ipcMain.handle('get-config-cache', async () => configData ?? await configDataReadyPromise); // Get zoom factor ipcMain.handle('get-zoom-factor-by-url', async (event, url: string) => { const display: Electron.Display = screen.getPrimaryDisplay(); const physicalSize: Electron.Size = { width: display.size.width * display.scaleFactor, height: display.size.height * display.scaleFactor }; const resolution: string = `${physicalSize.width}*${physicalSize.height}`; console.log('PhysicalResolution:', resolution); console.log(`Resolution: ${display.size.width}*${display.size.height}`); console.log(`ScaleFactor: ${display.scaleFactor}`); if (!configData) { await configDataReadyPromise; } if (configData.code != 200 || configData.status != 0) { console.error('Get config failed:', configData.msg + ', status: ' + configData.status + ', code: ' + configData.code); return display.scaleFactor; } const configList: TagResolutionConfig[] = configData.data; let config: TagResolutionConfig = configList.find(c => c.Resolutions.includes(resolution)); if (!config) { config = configList.find(c => c.Resolutions === '*'); } if (!config) { config = configList[0]; } console.log('Match Config:', config); let specialPUrl: SpecialPUrlItem = config.SpecialPUrl.find(s => s.SPUrl.toLowerCase() === url.toLowerCase()); if (!specialPUrl) { specialPUrl = config.SpecialPUrl.find(s => url.toLowerCase().startsWith(s.SPUrl.toLowerCase())); } console.log('specialPUrl:', specialPUrl); let percentage: number = typeof config.Percentage === 'string' ? parseInt(config.Percentage) : config.Percentage; if (specialPUrl) { percentage = typeof specialPUrl.SPPer === 'string' ? parseInt(specialPUrl.SPPer) : specialPUrl.SPPer; } console.log('Percentage:', percentage); return percentage; }); // Check if the URL is available ipcMain.handle('check-url-available', async (event, rawUrl: string) => { try { const url: URL = new URL(rawUrl); const lib = url.protocol === 'https:' ? https : http; return await new Promise((resolve) => { // 先用HEAD请求,如果遇到403或404,则再用GET请求再试一次(部分服务器可能禁止HEAD请求)。 const requestOptions: http.RequestOptions | https.RequestOptions = { hostname: url.hostname, port: url.port || undefined, path: url.pathname + url.search, headers: { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br', 'Connection': 'keep-alive', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' } }; const headReq = lib.request( { method: 'HEAD', timeout: 3000, ...requestOptions }, (res) => { console.log('check-url-available HEAD', url.toString(), res.statusCode, res.statusMessage); if (res.statusCode === 403 || res.statusCode === 404) { headReq.destroy() const getReq = lib.get({ method: 'GET', ...requestOptions }, (res) => { console.log('check-url-available GET', url.toString(), res.statusCode, res.statusMessage); resolve({ ok: true, status: res.statusCode, message: res.statusMessage }); getReq.destroy() }); getReq.on('error', (err) => { resolve({ ok: false, status: -1, message: err.message }); }); getReq.on('timeout', () => { resolve({ ok: false, status: -1, message: 'GET Timeout' }); getReq.destroy(); }); } else { resolve({ ok: true, status: res.statusCode, message: res.statusMessage }); headReq.destroy(); } } ); headReq.on('error', (err) => { resolve({ ok: false, status: -1, message: err.message }); }); headReq.on('timeout', () => { resolve({ ok: false, status: -1, message: 'HEAD Timeout' }); headReq.destroy(); }); headReq.end(); }) } catch (e) { return { ok: false, status: -1, message: 'Invalid URL' }; } }); // Set webview‘s cookie ipcMain.handle('set-webview-cookie', async (event, url: string, cookie: string) => { try { const parsedUrl = new URL(url); const cookies: Array<{ Key: string, Value: string }> = JSON.parse(cookie); for (const cookieItem of cookies) { await session.defaultSession.cookies.set({ url: url, name: cookieItem.Key.trim(), value: cookieItem.Value.trim(), domain: parsedUrl.hostname, path: '/', secure: parsedUrl.protocol === 'https:', httpOnly: true }); } return true; } catch (error) { console.error('设置cookie失败:', error); return false; } }); // Fault reporting ipcMain.handle('fault-reporting', async (event, url: string, message: string, status: number) => { console.log('Fault reporting:', url, message, status); const webContents = event.sender; const win = BrowserWindow.fromWebContents(webContents); if (!win) { throw new Error('Not found BrowserWindow'); } const base64 = await captureWindowAsBase64(win); console.log('base64:', base64); try { const response: ApiResponse = await EIACDesktopApi.Help.FaultReportingAsync({ Account: memoryCache.get('Account'), IP: getLocalIPAddress(), Url: url, ImgBase64: base64, Explain: `message: ${message}, status: ${status}` }); console.log('Fault reporting response:', response); if (response.code != 200 || response.status != 0) { console.error('故障上报失败:', response.msg + ', status: ' + response.status + ', code: ' + response.code); return { ok: false, status: -1, message: `故障上报失败: ${response.msg}, status: ${response.status}, code: ${response.code}` }; } return { ok: true, status: response.status, message: response.msg }; } catch (error) { console.error('故障上报失败:', error); return { ok: false, status: -1, message: error.message }; } }); } function getLocalIPAddress(): string | null { const interfaces = os.networkInterfaces(); for (const name of Object.keys(interfaces)) { for (const net of interfaces[name] || []) { // 排除 IPv6 和 127.0.0.1 内环地址 if (net.family === 'IPv4' && !net.internal) { return net.address; // 返回第一个非内网 IPv4 地址 } } } return null; // 没有找到 } async function captureWindowAsBase64(win: BrowserWindow): Promise { const image = await win.capturePage(); // 截图 const pngBuffer = image.toPNG(); // 转为 PNG Buffer const base64 = pngBuffer.toString('base64'); // 转为 Base64 字符串 return `data:image/png;base64,${base64}`; // 返回 DataURL } let menuData: ApiResponse = null; let menuDataReadyPromise: Promise>; let configData: ApiResponse = null; let configDataReadyPromise: Promise>; function getMenuAsync(): void { if (!menuDataReadyPromise) { menuDataReadyPromise = new Promise((resolve, reject) => { EIACDesktopApi.Menu.GetMenuAsync() .then(data => { menuData = data; resolve(data); }) .catch(err => { reject(err); }); }); } } function getConfigAsync(): void { if (!configDataReadyPromise) { configDataReadyPromise = new Promise((resolve, reject) => { EIACDesktopApi.Menu.GetConfigAsync() .then(data => { configData = data; resolve(data); }) .catch(err => { reject(err); }); }); } } export function preloadData(): void { getMenuAsync(); getConfigAsync(); }