From 85036de5c31c9e09832adacccd8c2de85142c988 Mon Sep 17 00:00:00 2001 From: Allen Date: Sat, 17 May 2025 20:42:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A3=80=E6=B5=8BURL?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E6=9C=89=E6=95=88=E6=96=B9=E6=B3=95=EF=BC=8C?= =?UTF-8?q?=E4=BD=BF=E5=85=B6=E6=9B=B4=E8=B4=B4=E8=BF=91=E6=B5=8F=E8=A7=88?= =?UTF-8?q?=E5=99=A8=E8=AE=BF=E9=97=AE=E6=95=88=E6=9E=9C=EF=BC=8C=E4=BB=A5?= =?UTF-8?q?=E5=8F=8A=E5=BD=93HEAD=E8=AF=B7=E6=B1=82=E5=90=8E=E6=94=B6?= =?UTF-8?q?=E5=88=B0403=E5=93=8D=E5=BA=94=E6=97=B6=EF=BC=8C=E5=86=8D?= =?UTF-8?q?=E7=94=A8GET=E8=AF=B7=E6=B1=82=E4=B8=80=E6=AC=A1=EF=BC=88?= =?UTF-8?q?=E4=B9=9F=E8=AE=B8Web=E6=9C=8D=E5=8A=A1=E5=99=A8=E7=A6=81?= =?UTF-8?q?=E6=AD=A2=E4=BA=86HEAD=E8=AF=B7=E6=B1=82=EF=BC=89=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/EIAC_Desktop_Api.ts | 4 +-- src/main.ts | 67 ++++++++++++++++++++++++++++++++--------- src/preload.ts | 45 +++++++++++++++++++++------ src/types/electron.d.ts | 35 +++++++++++++++++++++ 4 files changed, 125 insertions(+), 26 deletions(-) diff --git a/src/EIAC_Desktop_Api.ts b/src/EIAC_Desktop_Api.ts index c1ac81a..3179c8e 100644 --- a/src/EIAC_Desktop_Api.ts +++ b/src/EIAC_Desktop_Api.ts @@ -216,7 +216,7 @@ export interface TagResolutionConfig { /** * 缩放比例 */ - Percentage: number; + Percentage: string | number; /** * 特殊页面 */ @@ -234,7 +234,7 @@ export interface SpecialPUrlItem { /** * 特殊页面缩放比例 */ - SPPer: number; + SPPer: string | number; } /** diff --git a/src/main.ts b/src/main.ts index 5f5bc13..4a45ec0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,6 +5,8 @@ import * as http from 'http'; import * as https from 'https'; import { URL } from 'url'; +const isDevelopment = process.env.NODE_ENV === 'development'; + // Ensure only one instance is running const gotTheLock = app.requestSingleInstanceLock(); if (!gotTheLock) { @@ -32,34 +34,64 @@ ipcMain.handle('get-primary-display', () => screen.getPrimaryDisplay()); // Check if the URL is available ipcMain.handle('check-url-available', async (event, rawUrl: string) => { try { - const url = new URL(rawUrl); + const url: URL = new URL(rawUrl); const lib = url.protocol === 'https:' ? https : http; return await new Promise((resolve) => { - const req = lib.request( + // 先用HEAD请求,如果遇到403,则再用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', - hostname: url.hostname, - port: url.port || undefined, - path: url.pathname + url.search, timeout: 3000, + ...requestOptions }, (res) => { - resolve({ ok: true, status: res.statusCode }) - req.destroy() + console.log('check-url-available HEAD', url.toString(), res.statusCode, res.statusMessage); + if (res.statusCode === 403) { + 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, error: err.message }); + }); + getReq.on('timeout', () => { + resolve({ ok: false, error: 'Timeout' }); + getReq.destroy(); + }); + } else { + resolve({ ok: true, status: res.statusCode, message: res.statusMessage }); + headReq.destroy(); + } } ); - req.on('error', (err) => { - resolve({ ok: false, error: err.message }) + headReq.on('error', (err) => { + resolve({ ok: false, error: err.message }); }); - req.on('timeout', () => { - req.destroy() - resolve({ ok: false, error: 'Timeout' }) + headReq.on('timeout', () => { + resolve({ ok: false, error: 'Timeout' }); + headReq.destroy(); }); - req.end(); + headReq.end(); }) } catch (e) { return { ok: false, error: 'Invalid URL' }; @@ -141,7 +173,9 @@ const createWindow = () => { win.webContents.on('did-attach-webview', (event, webContents) => { webContents.setWindowOpenHandler((details) => { - console.log('webview-new-window', webContents, details); + if (isDevelopment) { + console.log('webview-new-window', webContents, details); + } win.webContents.send('webview-new-window', webContents.id, details.url); return { action: 'deny' }; }); @@ -172,6 +206,9 @@ const createWindow = () => { } }; globalShortcut.register("CommandOrControl+Shift+I", handleDevTools); + if (isDevelopment) { + win.webContents.openDevTools(); + } // and load the login.html of the app. if (MAIN_WINDOW_VITE_DEV_SERVER_URL) { @@ -185,7 +222,7 @@ const createWindow = () => { { label: '显示窗口', click: () => win.show() }, { label: '退出程序', click: () => app.exit() } ]); - const iconPath = process.env.NODE_ENV === 'development' ? path.join(__dirname, '../../assets/tray.png') : path.join(process.resourcesPath, 'assets', 'tray.png'); + const iconPath = isDevelopment ? path.join(__dirname, '../../assets/tray.png') : path.join(process.resourcesPath, 'assets', 'tray.png'); const tray = new Tray(iconPath); tray.setToolTip('中国电信-工作台'); tray.setContextMenu(contextMenu); diff --git a/src/preload.ts b/src/preload.ts index e41c1c6..34b8b5a 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -4,36 +4,63 @@ import { contextBridge, ipcRenderer } from 'electron'; contextBridge.exposeInMainWorld('electronAPI', { - // 打开新标签页 + /** + * 在新标签页打开URL + * @param callback 回调函数,参数为webContentId和url。其中webContentId是请求打开URL的webview的id。 + */ onOpenTab: (callback: (webContentId: number, url: string) => void) => { ipcRenderer.on('webview-new-window', (_event, webContentId, url) => callback(webContentId, url)); }, - // 获取当前屏幕的缩放比例和分辨率 - getPrimaryDisplay: () => ipcRenderer.invoke('get-primary-display'), + /** + * 获取当前屏幕的缩放比例和分辨率 + * @returns 缩放比例和分辨率 + */ + getPrimaryDisplay: () => ipcRenderer.invoke('get-primary-display') as Promise, - // 检查URL是否可用 + /** + * 检查URL是否可用 + * @param url 要检查的URL + * @returns 是否可用 + */ checkUrlAvailable: (url: string) => ipcRenderer.invoke('check-url-available', url), - // 设置webview的cookie + /** + * 设置webview的cookie + * @param url 要设置cookie的URL + * @param cookie cookie字符串 + */ setWebviewCookie: (url: string, cookie: string) => ipcRenderer.invoke('set-webview-cookie', url, cookie), - // 设置 sessionStorage + /** + * 按键将值设置到sessionStorage + * @param key 键 + * @param value 值 + */ setSessionStorage: (key: string, value: string) => { window.sessionStorage.setItem(key, value); }, - // 获取 sessionStorage + /** + * 从sessionStorage中获取指定键的值 + * @param key 键 + * @returns 值 + */ getSessionStorage: (key: string) => { return window.sessionStorage.getItem(key); }, - // 删除 sessionStorage + /** + * 从sessionStorage中删除指定键的值 + * @param key 键 + */ removeSessionStorage: (key: string) => { window.sessionStorage.removeItem(key); }, - // 清空 sessionStorage + /** + * 清空sessionStorage + */ clearSessionStorage: () => { window.sessionStorage.clear(); } diff --git a/src/types/electron.d.ts b/src/types/electron.d.ts index 3295f1a..e9947ce 100644 --- a/src/types/electron.d.ts +++ b/src/types/electron.d.ts @@ -1,11 +1,46 @@ export interface ElectronAPI { + /** + * 在新标签页打开URL + * @param callback 回调函数,参数为webContentId和url。其中webContentId是请求打开URL的webview的id。 + */ onOpenTab: (callback: (webContentId: number, url: string) => void) => void; + /** + * 获取当前屏幕的缩放比例和分辨率 + * @returns 缩放比例和分辨率 + */ getPrimaryDisplay: () => Promise; + /** + * 检查URL是否可用 + * @param url 要检查的URL + * @returns 是否可用 + */ checkUrlAvailable: (url: string) => Promise<{ ok: boolean; status: number; error?: string }>; + /** + * 设置webview的cookie + * @param url 要设置cookie的URL + * @param cookie cookie字符串 + */ setWebviewCookie: (url: string, cookie: string) => Promise; + /** + * 按键将值设置到sessionStorage + * @param key 键 + * @param value 值 + */ setSessionStorage: (key: string, value: string) => void; + /** + * 从sessionStorage中获取指定键的值 + * @param key 键 + * @returns 值 + */ getSessionStorage: (key: string) => string | null; + /** + * 从sessionStorage中删除指定键的值 + * @param key 键 + */ removeSessionStorage: (key: string) => void; + /** + * 清空sessionStorage + */ clearSessionStorage: () => void; }