From b3abad7faeb87c86bce2c08e37ce4783b0f65506 Mon Sep 17 00:00:00 2001 From: test Date: Sat, 7 Mar 2026 19:03:29 +0800 Subject: [PATCH] feat(playwright): enable Playwright in production builds with custom browser path Move Playwright from dev to production dependencies and implement robust browser management for company deployment environment: - Move playwright and playwright-core to dependencies - Set PLAYWRIGHT_BROWSERS_PATH to user data directory before imports - Add startup validation for Chromium browser with friendly error dialog - Update build scripts to skip browser download during build process - Configure electron-builder to unpack Playwright for native module access - Add deployment documentation for browser setup in restricted environments This allows manual browser installation in company environments where direct downloads are blocked during build. Co-Authored-By: Claude Sonnet 4.5 --- BROWSER_DEPLOYMENT.md | 176 +++++++++++++++++++++++++++++++++++++++ BROWSER_VERSIONS.md | 96 +++++++++++++++++++++ PLAYWRIGHT_DEPLOYMENT.md | 101 ++++++++++++++++++++++ electron-builder.yml | 6 ++ electron.vite.config.ts | 8 +- package-lock.json | 5 +- package.json | 7 +- src/main/index.ts | 59 ++++++++++++- 8 files changed, 444 insertions(+), 14 deletions(-) create mode 100644 BROWSER_DEPLOYMENT.md create mode 100644 BROWSER_VERSIONS.md create mode 100644 PLAYWRIGHT_DEPLOYMENT.md diff --git a/BROWSER_DEPLOYMENT.md b/BROWSER_DEPLOYMENT.md new file mode 100644 index 0000000..019a81a --- /dev/null +++ b/BROWSER_DEPLOYMENT.md @@ -0,0 +1,176 @@ +# Playwright 浏览器部署指南 + +## 概述 + +本文档为开发人员提供 ERPAuto 应用程序中 Playwright Chromium 浏览器的部署指南。文档说明如何将浏览器文件从开发机复制到目标用户机器,确保应用程序能够正常运行浏览器自动化任务。 + +**重要提示**:部署前请先阅读 [BROWSER_VERSIONS.md](./BROWSER_VERSIONS.md) 了解版本信息。 + +## 目标路径 + +浏览器文件必须部署在以下路径: + +``` +%APPDATA%\erpauto\ms-playwright\chromium-1208\ +``` + +完整展开路径示例(Windows): + +``` +C:\Users\<用户名>\AppData\Roaming\erpauto\ms-playwright\chromium-1208\ +``` + +## 目录结构 + +部署完成后,目标目录结构应如下所示: + +``` +%APPDATA%\erpauto\ms-playwright\ +└── chromium-1208/ + └── chrome-win/ + ├── chrome.exe # 浏览器可执行文件 + ├── chrome_dll.dll + ├── resources/ + ├── locales/ + └── ... # 其他浏览器文件 +``` + +**关键文件**:`chrome-win/chrome.exe` 必须存在,否则浏览器无法启动。 + +## 部署步骤 + +### 步骤 1:在开发机上准备 + +1. 确保开发机已安装正确版本的 Playwright: + + ```bash + npm install playwright@1.58.2 + ``` + +2. 下载 Chromium 浏览器: + + ```bash + npx playwright install chromium + ``` + +3. 定位开发机上的浏览器缓存目录: + + ``` + C:\Users\<开发机用户名>\AppData\Local\ms-playwright\chromium-1208 + ``` + +### 步骤 2:复制文件 + +1. **复制整个浏览器目录** + - 将开发机上的 `chromium-1208` 目录完整复制 + - 不要只复制部分文件,确保所有子目录和文件都包含在内 + +2. **粘贴到目标路径** + - 在目标机器上创建目录:`%APPDATA%\erpauto\ms-playwright\` + - 将 `chromium-1208` 目录粘贴到该路径下 + +3. **验证文件完整性** + - 确认目标路径存在:`%APPDATA%\erpauto\ms-playwright\chromium-1208\chrome-win\chrome.exe` + - 检查文件大小约为 280MB + +### 步骤 3:配置环境变量(可选) + +如需确保应用程序使用正确的浏览器路径,可设置以下环境变量: + +```batch +set PLAYWRIGHT_BROWSERS_PATH=%APPDATA%\erpauto\ms-playwright +``` + +或在应用程序代码中设置: + +```javascript +process.env.PLAYWRIGHT_BROWSERS_PATH = path.join(app.getPath('userData'), 'ms-playwright') +``` + +## 验证步骤 + +部署完成后,执行以下验证步骤: + +### 验证 1:检查目录结构 + +在目标机器上运行: + +```batch +dir %APPDATA%\erpauto\ms-playwright\chromium-1208\chrome-win\chrome.exe +``` + +应显示文件存在。 + +### 验证 2:启动浏览器测试 + +运行 ERPAuto 应用程序,执行以下操作: + +1. 登录应用程序 +2. 进入「数据提取」页面 +3. 输入一个有效订单号 +4. 点击「开始提取」 +5. 观察浏览器是否正常启动并执行任务 + +### 验证 3:检查日志 + +查看应用程序日志,确认没有浏览器相关的错误信息: + +- 无 "browser not found" 错误 +- 无 "chromium revision not found" 错误 +- 无 "PLAYWRIGHT_BROWSERS_PATH" 相关警告 + +## 故障排查 + +### 问题 1:浏览器无法启动 + +**症状**:应用程序报错,提示找不到浏览器或启动失败。 + +**解决方案**: + +1. 确认修订号匹配(必须是 1208) +2. 检查 `chrome-win/chrome.exe` 文件是否存在 +3. 验证 `PLAYWRIGHT_BROWSERS_PATH` 环境变量设置正确 +4. 确认目标机器具有相同的 Playwright 版本(1.58.2) + +### 问题 2:版本不匹配错误 + +**症状**:应用程序启动时报出版本冲突错误。 + +**解决方案**: + +1. 检查 `package.json` 中的 Playwright 版本是否为 1.58.2 +2. 确认复制的 Chromium 修订号为 1208 +3. 参考 [BROWSER_VERSIONS.md](./BROWSER_VERSIONS.md) 核对所有版本信息 + +### 问题 3:权限不足 + +**症状**:无法写入或读取浏览器目录。 + +**解决方案**: + +1. 确保目标目录具有适当的读写权限 +2. 以管理员身份运行应用程序进行测试 +3. 检查防病毒软件是否阻止了浏览器执行 + +### 问题 4:路径错误 + +**症状**:应用程序在错误的位置查找浏览器文件。 + +**解决方案**: + +1. 确认 `%APPDATA%` 环境变量指向正确的用户目录 +2. 检查应用程序是否正确解析了 `userData` 路径 +3. 在代码中硬编码浏览器路径进行调试 + +## 注意事项 + +- **仅部署 Chromium**:ERPAuto 只需要 Chromium 浏览器,不需要 Firefox 或 WebKit +- **版本一致性**:开发机和目标机器的 Playwright 版本必须一致 +- **修订号匹配**:Chromium 修订号(1208)必须完全匹配,否则可能出现兼容性问题 +- **文件完整性**:复制时确保所有文件完整,损坏的浏览器文件会导致启动失败 +- **网络隔离环境**:目标机器如果无法访问互联网,必须提前部署浏览器文件,因为无法自动下载 + +## 参考文档 + +- [BROWSER_VERSIONS.md](./BROWSER_VERSIONS.md) - 版本信息和目录结构详情 +- [README.md](./README.md) - 项目总体说明 diff --git a/BROWSER_VERSIONS.md b/BROWSER_VERSIONS.md new file mode 100644 index 0000000..fa8b7d3 --- /dev/null +++ b/BROWSER_VERSIONS.md @@ -0,0 +1,96 @@ +# Playwright 浏览器版本信息 + +本文档记录 ERPAuto 项目使用的 Playwright 浏览器版本和部署信息。 + +## 版本信息 + +| 组件 | 版本号 | +| --------------- | ------------ | +| Playwright | 1.58.2 | +| Chromium | 145.0.7632.6 | +| Chromium 修订号 | 1208 | + +## 浏览器目录结构 + +Playwright 将浏览器文件缓存在以下位置: + +### Windows 开发环境 + +``` +C:\Users\<用户名>\AppData\Local\ms-playwright\ +└── chromium-1208/ + └── chrome-win/ + ├── chrome.exe + └── ... +``` + +### 目标部署环境 + +``` +%APPDATA%\erpauto\ms-playwright\ +└── chromium-1208/ + └── chrome-win/ + ├── chrome.exe + └── ... +``` + +## 部署指南 + +### 开发环境准备 + +1. 安装 Playwright 1.58.2 + + ```bash + npm install playwright@1.58.2 + ``` + +2. 下载 Chromium 浏览器 + ```bash + npx playwright install chromium + ``` + +### 浏览器文件复制步骤 + +1. **定位源目录** + - 开发机上找到 Playwright 浏览器缓存目录 + - 默认路径:`C:\Users\<用户名>\AppData\Local\ms-playwright\chromium-1208` + +2. **复制浏览器文件** + - 将整个 `chromium-1208` 目录复制到部署目标 + - 目标路径:`%APPDATA%\erpauto\ms-playwright\chromium-1208` + +3. **验证目录结构** + - 确认目标路径包含 `chrome-win/chrome.exe` + - 确保所有子文件完整复制 + +### 环境变量配置 + +如需要自定义浏览器路径,可设置环境变量: + +```bash +# Windows +set PLAYWRIGHT_BROWSERS_PATH=%APPDATA%\erpauto\ms-playwright +``` + +## 注意事项 + +- 仅包含 Chromium 浏览器(Firefox 和 WebKit 不需要) +- 浏览器文件体积约为 280MB +- 部署时确保目标机器具有相同的 Playwright 版本(1.58.2) +- 修订号必须匹配(1208),否则可能出现兼容性问题 + +## 故障排查 + +### 浏览器无法启动 + +1. 检查修订号是否匹配(1208) +2. 确认 `chrome-win/chrome.exe` 文件存在 +3. 验证 PLAYWRIGHT_BROWSERS_PATH 环境变量设置 + +### 版本不匹配错误 + +确保以下版本一致: + +- package.json 中的 Playwright 版本 +- 下载的 Chromium 修订号 +- browsers.json 中定义版本号 diff --git a/PLAYWRIGHT_DEPLOYMENT.md b/PLAYWRIGHT_DEPLOYMENT.md new file mode 100644 index 0000000..3e36a68 --- /dev/null +++ b/PLAYWRIGHT_DEPLOYMENT.md @@ -0,0 +1,101 @@ +# Playwright 部署说明 + +## 浏览器路径问题修复 + +### 问题描述 + +应用启动时提示"浏览器文件未找到",但浏览器文件已经放置在正确路径下。 + +**原因**:Playwright 1.48+ 改变了浏览器目录命名规则: + +- **旧格式**:`chromium-win32/chrome.exe` +- **新格式**:`chromium-1208/chrome-win64/chrome.exe`(revision 号可能不同) + +### 解决方案 + +代码已更新为自动检测新旧两种格式,并显示当前目录内容以便调试。 + +## 快速部署 + +### 方法 1:使用 Playwright CLI(推荐) + +```bash +# 在应用目录运行 +npx playwright install chromium +``` + +浏览器会自动下载并安装到正确位置: + +- **用户数据目录**:`%APPDATA%\erpauto\ms-playwright\` +- **完整路径**:`C:\Users\pengq\AppData\Roaming\erpauto\ms-playwright\chromium-\chrome-win64\chrome.exe` + +### 方法 2:手动复制 + +如果你已经有 Playwright 浏览器文件,可以复制到应用的用户数据目录: + +1. 找到现有浏览器文件(通常在 `%USERPROFILE%\AppData\Local\ms-playwright`) +2. 复制到 `%APPDATA%\erpauto\ms-playwright\` +3. 确保目录结构正确: + ``` + ms-playwright/ + ├── chromium-1208/ + │ ├── chrome-win64/ + │ │ └── chrome.exe + │ └── INSTALLATION_COMPLETE + └── chromium_headless_shell-1208/ + └── ... + ``` + +### 方法 3:使用 PLAYWRIGHT_BROWSERS_PATH 环境变量 + +将浏览器文件放在共享位置,然后设置环境变量: + +```bash +# 系统环境变量 +setx PLAYWRIGHT_BROWSERS_PATH "D:\shared\playwright-browsers" +``` + +或在应用启动脚本中设置。 + +## 验证安装 + +运行应用后,检查是否还有错误提示。如果没有浏览器错误,说明安装成功。 + +你也可以在应用日志中查找: + +- `Found Chromium revision: chromium-1208` - 表示成功找到浏览器 +- `Playwright browser not found` - 表示未找到,会显示可用目录列表 + +## 常见问题 + +### Q: 显示"浏览器文件未找到"但文件确实在那里 + +检查目录结构是否正确: + +```powershell +# 查看当前目录内容 +Get-ChildItem $env:APPDATA\erpauto\ms-playwright + +# 检查 chrome.exe 是否存在 +Test-Path "$env:APPDATA\erpauto\ms-playwright\chromium-*/chrome-win64/chrome.exe" +``` + +### Q: 不同用户使用同一个浏览器文件 + +使用环境变量 `PLAYWRIGHT_BROWSERS_PATH` 指向共享目录。 + +### Q: 离线部署 + +1. 在有网络的机器上运行 `npx playwright install chromium` +2. 复制整个 `ms-playwright` 目录 +3. 在目标机器上设置 `PLAYWRIGHT_BROWSERS_PATH` 指向该目录 + +## 下次构建 + +只需运行: + +```bash +npm run build:win +``` + +所有配置已保存,Playwright 模块和浏览器路径检查会自动处理。 diff --git a/electron-builder.yml b/electron-builder.yml index 441eff3..98bc229 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -10,11 +10,17 @@ files: - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}' - '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}' - 'package.json' + # Include build output + - 'out/**/*' # Include config.template.yaml in the build for reference - 'config.template.yaml' + # Exclude Playwright browser downloads (manual install for company environment) + - '!**/node_modules/playwright-core/.local-browsers/**' asarUnpack: - resources/** + # Unpack playwright for native modules - '**/node_modules/playwright/**' + - '**/node_modules/playwright-core/**' win: executableName: erpauto target: diff --git a/electron.vite.config.ts b/electron.vite.config.ts index 37e2622..80304cd 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -4,13 +4,7 @@ import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ - main: { - build: { - rollupOptions: { - external: ['playwright', 'playwright-core'] - } - } - }, + main: {}, preload: {}, renderer: { resolve: { diff --git a/package-lock.json b/package-lock.json index d897061..eb2df3f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,8 @@ "lucide-react": "^0.575.0", "mssql": "^12.2.0", "mysql2": "^3.18.2", + "playwright": "^1.58.2", + "playwright-core": "^1.58.2", "reflect-metadata": "^0.2.2", "typeorm": "^0.3.28", "uuid": "^13.0.0", @@ -48,7 +50,6 @@ "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", - "playwright": "^1.58.2", "postcss": "^8.5.6", "prettier": "^3.7.4", "react": "^19.2.1", @@ -10116,7 +10117,6 @@ "version": "1.58.2", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", - "dev": true, "license": "Apache-2.0", "dependencies": { "playwright-core": "1.58.2" @@ -10135,7 +10135,6 @@ "version": "1.58.2", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", - "dev": true, "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" diff --git a/package.json b/package.json index 04d57ff..ddff448 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "dev": "chcp 65001 && electron-vite dev", "build": "chcp 65001 && npm run typecheck && electron-vite build", "postinstall": "electron-builder install-app-deps", - "build:unpack": "chcp 65001 && npm run build && electron-builder --dir", - "build:win": "chcp 65001 && npm run build && electron-builder --win", + "build:unpack": "chcp 65001 && set PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 && npm run build && electron-builder --dir", + "build:win": "chcp 65001 && set PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 && npm run build && electron-builder --win", "build:mac": "chcp 65001 && electron-vite build && electron-builder --mac", "build:linux": "chcp 65001 && electron-vite build && electron-builder --linux", "test": "vitest", @@ -40,6 +40,8 @@ "lucide-react": "^0.575.0", "mssql": "^12.2.0", "mysql2": "^3.18.2", + "playwright": "^1.58.2", + "playwright-core": "^1.58.2", "reflect-metadata": "^0.2.2", "typeorm": "^0.3.28", "uuid": "^13.0.0", @@ -68,7 +70,6 @@ "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", - "playwright": "^1.58.2", "postcss": "^8.5.6", "prettier": "^3.7.4", "react": "^19.2.1", diff --git a/src/main/index.ts b/src/main/index.ts index ceddea8..24374aa 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,4 +1,4 @@ -import { app, shell, BrowserWindow, ipcMain } from 'electron' +import { app, shell, BrowserWindow, ipcMain, dialog } from 'electron' import { join } from 'path' import { electronApp, optimizer, is } from '@electron-toolkit/utils' import icon from '../../resources/icon.png?asset' @@ -6,6 +6,10 @@ import { registerIpcHandlers } from './ipc' import { ConfigManager } from './services/config/config-manager' import { fileURLToPath } from 'url' import { dirname } from 'path' +import fs from 'fs' + +// Set Playwright browsers path BEFORE any playwright import +process.env.PLAYWRIGHT_BROWSERS_PATH = join(app.getPath('userData'), 'ms-playwright') const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) @@ -46,6 +50,59 @@ function createWindow(): void { // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.whenReady().then(async () => { + // Validate Playwright browser path + const browsersPath = process.env.PLAYWRIGHT_BROWSERS_PATH! + + // Create directory if it doesn't exist + try { + fs.mkdirSync(browsersPath, { recursive: true }) + } catch (error) { + console.error('Failed to create browsers directory:', error) + } + + // Check if chromium browser exists (supports both old and new Playwright directory structures) + // New format (v1.48+): chromium-1208/chrome-win64/chrome.exe + // Old format: chromium-win32/chrome.exe + const newChromiumPath = join(browsersPath, 'chromium-1208', 'chrome-win64', 'chrome.exe') + const oldChromiumPath = join(browsersPath, 'chromium-win32', 'chrome.exe') + const chromiumPath = fs.existsSync(newChromiumPath) ? newChromiumPath : oldChromiumPath + + if (!fs.existsSync(chromiumPath)) { + // Try to find any chromium revision + let foundRevision = false + try { + const entries = fs.readdirSync(browsersPath) + for (const entry of entries) { + if (entry.startsWith('chromium-') && !entry.includes('headless')) { + const revisionPath = join(browsersPath, entry, 'chrome-win64', 'chrome.exe') + if (fs.existsSync(revisionPath)) { + console.log('Found Chromium revision:', entry) + foundRevision = true + break + } + } + } + } catch (e) { + // Ignore + } + + if (!foundRevision) { + dialog.showErrorBox( + '浏览器文件未找到', + `Playwright 浏览器文件不存在。\n\n` + + `期望路径:${newChromiumPath}\n` + + `或:${oldChromiumPath}\n\n` + + `当前目录内容:${fs.existsSync(browsersPath) ? fs.readdirSync(browsersPath).join(', ') : '目录不存在'}\n\n` + + `请运行以下命令安装浏览器:\n` + + `npx playwright install chromium` + ) + console.warn( + 'Playwright browser not found. Available:', + fs.existsSync(browsersPath) ? fs.readdirSync(browsersPath) : 'none' + ) + } + } + // Initialize ConfigManager BEFORE registering IPC handlers // This ensures config is loaded before any service tries to use it try {