main.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. const { app, BrowserWindow } = require('electron')
  2. const path = require('path')
  3. const http = require('http')
  4. const os = require('os')
  5. const fs = require('fs')
  6. const config = require('../configs/config.js')
  7. const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged
  8. // 修复缓存权限问题:设置用户数据目录到有权限的位置
  9. // 必须在 app.whenReady() 之前调用
  10. if (process.platform === 'win32') {
  11. try {
  12. // 设置缓存目录到用户临时目录,避免权限问题
  13. const userDataPath = path.join(os.tmpdir(), 'electron-react-vite-app')
  14. // 确保目录存在
  15. if (!fs.existsSync(userDataPath)) {
  16. fs.mkdirSync(userDataPath, { recursive: true })
  17. }
  18. // 创建缓存子目录
  19. const cacheDir = path.join(userDataPath, 'cache')
  20. const gpuCacheDir = path.join(userDataPath, 'gpu-cache')
  21. if (!fs.existsSync(cacheDir)) {
  22. fs.mkdirSync(cacheDir, { recursive: true })
  23. }
  24. if (!fs.existsSync(gpuCacheDir)) {
  25. fs.mkdirSync(gpuCacheDir, { recursive: true })
  26. }
  27. // 设置用户数据路径(必须在 app.whenReady() 之前)
  28. app.setPath('userData', userDataPath)
  29. // 设置缓存目录到有权限的位置
  30. app.commandLine.appendSwitch('disk-cache-dir', cacheDir)
  31. app.commandLine.appendSwitch('gpu-disk-cache-dir', gpuCacheDir)
  32. console.log(`[OK] Cache directories set to: ${userDataPath}`)
  33. } catch (error) {
  34. console.warn('[WARN] Failed to set cache directories:', error.message)
  35. // 如果设置失败,尝试禁用 GPU 缓存作为备选方案
  36. app.commandLine.appendSwitch('disable-gpu-sandbox')
  37. }
  38. }
  39. /**
  40. * 检测 Vite 开发服务器实际使用的端口
  41. * 如果配置的端口被占用,Vite 会自动尝试下一个端口
  42. * 需要确认是 Vite 服务器,而不仅仅是端口响应
  43. */
  44. async function findVitePort(startPort, maxAttempts = 10) {
  45. const viteHost = config.vite?.host || 'localhost'
  46. for (let offset = 0; offset < maxAttempts; offset++) {
  47. const port = startPort + offset
  48. const isViteServer = await new Promise((resolve) => {
  49. const req = http.get(`http://${viteHost}:${port}`, (res) => {
  50. // 检查状态码
  51. if (res.statusCode !== 200) {
  52. resolve(false)
  53. return
  54. }
  55. // 读取响应数据确认是否是 Vite 服务器
  56. let data = ''
  57. let resolved = false
  58. res.on('data', (chunk) => {
  59. if (resolved) return
  60. data += chunk.toString()
  61. // 如果响应包含 Vite 特征,立即确认
  62. if (data.length > 100 && (data.includes('/vite') || data.includes('Vite') || data.includes('vite/client'))) {
  63. resolved = true
  64. resolve(true)
  65. }
  66. })
  67. res.on('end', () => {
  68. if (resolved) return
  69. // 检查响应内容是否包含 Vite 特征
  70. if (data.includes('/vite') || data.includes('Vite') || data.includes('vite/client')) {
  71. resolve(true)
  72. } else {
  73. resolve(false)
  74. }
  75. })
  76. })
  77. req.on('error', () => {
  78. resolve(false)
  79. })
  80. req.setTimeout(2000, () => {
  81. req.destroy()
  82. resolve(false)
  83. })
  84. })
  85. if (isViteServer) {
  86. return port
  87. }
  88. }
  89. // 如果找不到,返回配置的端口
  90. return startPort
  91. }
  92. async function createWindow() {
  93. const mainWindow = new BrowserWindow({
  94. width: config.window.width,
  95. height: config.window.height,
  96. autoHideMenuBar: config.window.autoHideMenuBar, // 从配置文件读取
  97. webPreferences: {
  98. nodeIntegration: false,
  99. contextIsolation: true
  100. }
  101. })
  102. if (isDev) {
  103. // 从配置文件读取 Vite 开发服务器端口
  104. const configPort = config.vite?.port || 5173
  105. // 检测实际使用的端口(如果配置端口被占用,Vite 会自动尝试下一个)
  106. const vitePort = await findVitePort(configPort)
  107. const viteHost = config.vite?.host || 'localhost'
  108. console.log(`Loading Vite dev server at http://${viteHost}:${vitePort}`)
  109. mainWindow.loadURL(`http://${viteHost}:${vitePort}`)
  110. // 根据配置文件决定是否打开调试侧边栏
  111. if (config.devTools.enabled) {
  112. mainWindow.webContents.openDevTools()
  113. }
  114. } else {
  115. mainWindow.loadFile(path.join(__dirname, '../dist/index.html'))
  116. }
  117. }
  118. app.whenReady().then(() => {
  119. createWindow()
  120. app.on('activate', () => {
  121. if (BrowserWindow.getAllWindows().length === 0) {
  122. createWindow()
  123. }
  124. })
  125. })
  126. app.on('window-all-closed', () => {
  127. if (process.platform !== 'darwin') {
  128. app.quit()
  129. }
  130. })