refactor the folder structure in ui main process CHECK_AND_DOWNLOAD_DEPENDENCIES flow; fix the cannot read property on vm issue in BootstrapSplash in renderer process

This commit is contained in:
geekgeekrun
2024-03-09 09:15:55 +08:00
parent 4b0a1aa777
commit e69851c008
10 changed files with 232 additions and 253 deletions

View File

@@ -1,102 +0,0 @@
import * as path from 'node:path'
import * as os from 'node:os'
import * as fs from 'node:fs'
import type { InstalledBrowser } from '@puppeteer/browsers'
import electron from 'electron'
import { saveLastUsedAndAvailableBrowserInfo, BrowserInfo } from './history-utils'
export const EXPECT_CHROMIUM_BUILD_ID = '113.0.5672.63'
const cacheDir = path.join(
os.homedir(),
'.geekgeekrun',
'cache'
)
const getPuppeteerManagerModule = async () => {
let puppeteerManager
if (process.env.NODE_ENV === 'development') {
puppeteerManager = await import('@puppeteer/browsers')
} else {
puppeteerManager = (
await import(
'file://' +
path.resolve(
electron.app.getAppPath(),
'..',
'external-node-runtime-dependencies/index.mjs'
)
)
).puppeteerManager
}
return puppeteerManager
}
export const getExpectCachedPuppeteerExecutable = async (): Promise<BrowserInfo> => {
const puppeteerManager = await getPuppeteerManagerModule()
const executablePath = puppeteerManager.computeExecutablePath({
browser: puppeteerManager.Browser.CHROME,
cacheDir,
buildId: EXPECT_CHROMIUM_BUILD_ID
})
const browser = puppeteerManager.Browser.CHROME[0].toUpperCase() + puppeteerManager.Browser.CHROME.slice(1) + ' ' + EXPECT_CHROMIUM_BUILD_ID
return {
executablePath,
browser
}
}
export const checkCachedPuppeteerExecutable = async () => {
try {
const executablePath = (await getExpectCachedPuppeteerExecutable()).executablePath
return fs.existsSync(executablePath)
} catch {
// should limit [ERR_MODULE_NOT_FOUND]
return false
}
}
const checkAndDownloadPuppeteerExecutable = async (
options: {
downloadProgressCallback?: (downloadedBytes: number, totalBytes: number) => void
confirmContinuePromise?: Promise<void>
} = {}
) => {
const puppeteerManager = await getPuppeteerManagerModule()
let installedBrowser: InstalledBrowser
if (!(await checkCachedPuppeteerExecutable())) {
try {
await options.confirmContinuePromise
} catch {
throw new Error('USER_CANCEL_DOWNLOAD_PUPPETEER')
}
// maybe the exist installation is broken.
await puppeteerManager.uninstall({
cacheDir,
buildId: EXPECT_CHROMIUM_BUILD_ID,
browser: puppeteerManager.Browser.CHROME
})
installedBrowser = await puppeteerManager.install({
browser: puppeteerManager.Browser.CHROME,
cacheDir,
buildId: EXPECT_CHROMIUM_BUILD_ID,
downloadProgressCallback: options.downloadProgressCallback
})
} else {
installedBrowser = (
await puppeteerManager.getInstalledBrowsers({
cacheDir
})
).find((it) => it.buildId === EXPECT_CHROMIUM_BUILD_ID)!
}
await saveLastUsedAndAvailableBrowserInfo({
executablePath: installedBrowser.executablePath,
browser: installedBrowser.browser[0].toUpperCase() + installedBrowser.browser.slice(1) + ' ' + EXPECT_CHROMIUM_BUILD_ID
})
return installedBrowser
}
export default checkAndDownloadPuppeteerExecutable

View File

@@ -1,65 +0,0 @@
import electron from 'electron'
import * as os from 'node:os'
import * as fs from 'node:fs'
import path from 'node:path'
import { EXPECT_CHROMIUM_BUILD_ID } from './check-and-download-puppeteer-executable'
import {
getExecutableFileVersion
} from '@geekgeekrun/utils/windows-only/file.mjs'
import { BrowserInfo } from './history-utils'
export default async function findAndLocateExistedChromiumExecutable(): Promise<BrowserInfo> {
const exceptChromiumMainVersion = Number(EXPECT_CHROMIUM_BUILD_ID.split('.')[0])
// For windows, try to find Edge(chromium)
if (os.platform() === 'win32') {
// TODO: handle windows
const edgeExecutableLocation = path.join(
process.env['ProgramFiles(x86)']!,
'Microsoft/Edge/Application',
'msedge.exe'
)
if (
fs.existsSync(edgeExecutableLocation)
) {
try {
const version = await getExecutableFileVersion(edgeExecutableLocation)
const mainVersion = Number(version.split('.')[0])
if ( mainVersion >= exceptChromiumMainVersion) {
return {
executablePath: edgeExecutableLocation,
browser: `Edge ${version}`
}
}
} catch(err) {
console.log(err)
}
}
}
// For other, use findChrome
let findChrome: typeof import('find-chrome-bin').findChrome
if (process.env.NODE_ENV === 'development') {
findChrome = (await import('find-chrome-bin')).findChrome
} else {
findChrome = (
await import(
'file://' +
path.resolve(
electron.app.getAppPath(),
'..',
'external-node-runtime-dependencies/index.mjs'
)
)
).findChromeBin.findChrome
}
const targetBrowser = await findChrome({
min: exceptChromiumMainVersion
})
if (!targetBrowser?.executablePath) {
throw new Error('NO_EXPECT_CHROMIUM_FOUND')
}
return {
executablePath: targetBrowser.executablePath,
browser: targetBrowser.browser
}
}

View File

@@ -1,72 +1,14 @@
import { app } from 'electron'
import checkAndDownloadPuppeteerExecutable, {
checkCachedPuppeteerExecutable,
getExpectCachedPuppeteerExecutable
} from './check-and-download-puppeteer-executable'
import { checkAndDownloadPuppeteerExecutable } from './utils/puppeteer-executable/index'
import * as fs from 'fs'
import { pipeWriteRegardlessError } from '../utils/pipe'
import {
removeLastUsedAndAvailableBrowserPath,
getLastUsedAndAvailableBrowser,
saveLastUsedAndAvailableBrowserInfo,
BrowserInfo
} from './history-utils'
import { type Worker } from 'worker_threads'
import {
sleep
} from '@geekgeekrun/utils/sleep.mjs'
import CheckAndLocateExistedChromiumExecutableWorker from '../../worker/check-and-locate-existed-chromium-executable?nodeWorker&url'
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
export enum DOWNLOAD_ERROR_EXIT_CODE {
NO_ERROR = 0,
DOWNLOAD_ERROR = 1
}
async function findAndLocateExistedChromiumExecutable(): Promise<BrowserInfo | null> {
return new Promise((resolve, reject) => {
const worker: Worker = new CheckAndLocateExistedChromiumExecutableWorker()
worker.once('message', (data) => {
if (data.type === 'RESULT') {
resolve(data.data)
}
})
worker.once('message', (data) => {
if (data.type === 'ERROR') {
reject(data.error)
}
})
})
};
export const getAnyAvailablePuppeteerExecutable = async (): Promise<BrowserInfo | null> => {
const lastUsedOne = await getLastUsedAndAvailableBrowser()
if (lastUsedOne) {
return lastUsedOne
}
// find existed browser - the one maybe actively installed by user or ship with os like Edge on windows
try {
const existedOne = (await findAndLocateExistedChromiumExecutable())
await saveLastUsedAndAvailableBrowserInfo(existedOne)
// save its path
return existedOne
} catch (err) {
console.error(err)
console.log('no existed browser path found')
}
// find existed browser - the fallback one
if (await checkCachedPuppeteerExecutable()) {
const cachedOne = await getExpectCachedPuppeteerExecutable()
await saveLastUsedAndAvailableBrowserInfo(cachedOne)
return cachedOne
}
// if no one available, then return null and remove last used browser
await removeLastUsedAndAvailableBrowserPath()
return null
}
export const checkAndDownloadDependenciesForInit = async () => {
process.on('disconnect', () => app.exit())
app.dock?.hide()

View File

@@ -4,8 +4,8 @@ import * as fs from 'fs'
import * as fsPromise from 'fs/promises'
export interface BrowserInfo {
browser: string;
executablePath: string;
browser: string
executablePath: string
}
const runtimeFolderPath = path.join(os.homedir(), '.geekgeekrun')
@@ -27,7 +27,7 @@ export const getLastUsedAndAvailableBrowser = async (): Promise<BrowserInfo | nu
}
try {
const fileContent = (await fsPromise.readFile(lastUsedBrowserRecordFilePath)).toString()
const [path, browser] = fileContent.split('\n').map(it => it.trim())
const [path, browser] = fileContent.split('\n').map((it) => it.trim())
if (!path || !fs.existsSync(path)) {
await removeLastUsedAndAvailableBrowserPath()
return null
@@ -48,10 +48,8 @@ export const saveLastUsedAndAvailableBrowserInfo = async (browserInfo: BrowserIn
await fsPromise.mkdir(runtimeFolderPath)
}
await fsPromise.writeFile(
lastUsedBrowserRecordFilePath, [
browserInfo.executablePath,
browserInfo.browser
].join('\n')
lastUsedBrowserRecordFilePath,
[browserInfo.executablePath, browserInfo.browser].join('\n')
)
} catch {
console.warn('lastUsedBrowserRecordFile write error')

View File

@@ -0,0 +1,210 @@
import * as path from 'node:path'
import * as os from 'node:os'
import * as fs from 'node:fs'
import type { InstalledBrowser } from '@puppeteer/browsers'
import electron from 'electron'
import {
saveLastUsedAndAvailableBrowserInfo,
BrowserInfo,
getLastUsedAndAvailableBrowser,
removeLastUsedAndAvailableBrowserPath
} from '../browser-history'
import { getExecutableFileVersion } from '@geekgeekrun/utils/windows-only/file.mjs'
import CheckAndLocateExistedChromiumExecutableWorker from './worker/find-and-locate-existed-chromium-executable?nodeWorker&url'
import { type Worker } from 'worker_threads'
const getPuppeteerManagerModule = async () => {
let puppeteerManager
if (process.env.NODE_ENV === 'development') {
puppeteerManager = await import('@puppeteer/browsers')
} else {
puppeteerManager = (
await import(
'file://' +
path.resolve(
electron.app.getAppPath(),
'..',
'external-node-runtime-dependencies/index.mjs'
)
)
).puppeteerManager
}
return puppeteerManager
}
const EXPECT_CHROMIUM_BUILD_ID = '113.0.5672.63'
const cacheDir = path.join(os.homedir(), '.geekgeekrun', 'cache')
const getExpectCachedPuppeteerExecutable = async (): Promise<BrowserInfo> => {
const puppeteerManager = await getPuppeteerManagerModule()
const executablePath = puppeteerManager.computeExecutablePath({
browser: puppeteerManager.Browser.CHROME,
cacheDir,
buildId: EXPECT_CHROMIUM_BUILD_ID
})
const browser =
puppeteerManager.Browser.CHROME[0].toUpperCase() +
puppeteerManager.Browser.CHROME.slice(1) +
' ' +
EXPECT_CHROMIUM_BUILD_ID
return {
executablePath,
browser
}
}
const checkCachedPuppeteerExecutable = async () => {
try {
const executablePath = (await getExpectCachedPuppeteerExecutable()).executablePath
return fs.existsSync(executablePath)
} catch {
// should limit [ERR_MODULE_NOT_FOUND]
return false
}
}
export const checkAndDownloadPuppeteerExecutable = async (
options: {
downloadProgressCallback?: (downloadedBytes: number, totalBytes: number) => void
confirmContinuePromise?: Promise<void>
} = {}
) => {
const puppeteerManager = await getPuppeteerManagerModule()
let installedBrowser: InstalledBrowser
if (!(await checkCachedPuppeteerExecutable())) {
try {
await options.confirmContinuePromise
} catch {
throw new Error('USER_CANCEL_DOWNLOAD_PUPPETEER')
}
// maybe the exist installation is broken.
await puppeteerManager.uninstall({
cacheDir,
buildId: EXPECT_CHROMIUM_BUILD_ID,
browser: puppeteerManager.Browser.CHROME
})
installedBrowser = await puppeteerManager.install({
browser: puppeteerManager.Browser.CHROME,
cacheDir,
buildId: EXPECT_CHROMIUM_BUILD_ID,
downloadProgressCallback: options.downloadProgressCallback
})
} else {
installedBrowser = (
await puppeteerManager.getInstalledBrowsers({
cacheDir
})
).find((it) => it.buildId === EXPECT_CHROMIUM_BUILD_ID)!
}
await saveLastUsedAndAvailableBrowserInfo({
executablePath: installedBrowser.executablePath,
browser:
installedBrowser.browser[0].toUpperCase() +
installedBrowser.browser.slice(1) +
' ' +
EXPECT_CHROMIUM_BUILD_ID
})
return installedBrowser
}
export const getAnyAvailablePuppeteerExecutable = async (): Promise<BrowserInfo | null> => {
const lastUsedOne = await getLastUsedAndAvailableBrowser()
if (lastUsedOne) {
return lastUsedOne
}
// find existed browser - the one maybe actively installed by user or ship with os like Edge on windows
try {
const existedOne = await findAndLocateExistedChromiumExecutable()
await saveLastUsedAndAvailableBrowserInfo(existedOne)
// save its path
return existedOne
} catch (err) {
console.error(err)
console.log('no existed browser path found')
}
// find existed browser - the fallback one
if (await checkCachedPuppeteerExecutable()) {
const cachedOne = await getExpectCachedPuppeteerExecutable()
await saveLastUsedAndAvailableBrowserInfo(cachedOne)
return cachedOne
}
// if no one available, then return null and remove last used browser
await removeLastUsedAndAvailableBrowserPath()
return null
}
export async function findAndLocateExistedChromiumExecutableSync(): Promise<BrowserInfo> {
const exceptChromiumMainVersion = Number(EXPECT_CHROMIUM_BUILD_ID.split('.')[0])
// For windows, try to find Edge(chromium)
if (os.platform() === 'win32') {
// TODO: handle windows
const edgeExecutableLocation = path.join(
process.env['ProgramFiles(x86)']!,
'Microsoft/Edge/Application',
'msedge.exe'
)
if (fs.existsSync(edgeExecutableLocation)) {
try {
const version = await getExecutableFileVersion(edgeExecutableLocation)
const mainVersion = Number(version.split('.')[0])
if (mainVersion >= exceptChromiumMainVersion) {
return {
executablePath: edgeExecutableLocation,
browser: `Edge ${version}`
}
}
} catch (err) {
console.log(err)
}
}
}
// For other, use findChrome
let findChrome: typeof import('find-chrome-bin').findChrome
if (process.env.NODE_ENV === 'development') {
findChrome = (await import('find-chrome-bin')).findChrome
} else {
findChrome = (
await import(
'file://' +
path.resolve(
electron.app.getAppPath(),
'..',
'external-node-runtime-dependencies/index.mjs'
)
)
).findChromeBin.findChrome
}
const targetBrowser = await findChrome({
min: exceptChromiumMainVersion
})
if (!targetBrowser?.executablePath) {
throw new Error('NO_EXPECT_CHROMIUM_FOUND')
}
return {
executablePath: targetBrowser.executablePath,
browser: targetBrowser.browser
}
}
export async function findAndLocateExistedChromiumExecutable(): Promise<BrowserInfo> {
return new Promise((resolve, reject) => {
const worker: Worker = new CheckAndLocateExistedChromiumExecutableWorker()
worker.once('message', (data) => {
if (data.type === 'RESULT') {
resolve(data.data)
}
})
worker.once('message', (data) => {
if (data.type === 'ERROR') {
reject(data.error)
}
})
})
}

View File

@@ -1,8 +1,8 @@
import { parentPort } from 'node:worker_threads'
import findAndLocateExistedChromiumExecutable from '../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/check-and-locate-existed-chromium-executable'
import { findAndLocateExistedChromiumExecutableSync } from '../index'
;(async () => {
try {
const result = await findAndLocateExistedChromiumExecutable()
const result = await findAndLocateExistedChromiumExecutableSync()
parentPort?.postMessage({
type: 'RESULT',
data: result

View File

@@ -4,7 +4,7 @@ import { SyncHook, AsyncSeriesHook } from 'tapable'
import { readConfigFile } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
import * as fs from 'fs'
import { pipeWriteRegardlessError } from './utils/pipe'
import { getAnyAvailablePuppeteerExecutable } from './CHECK_AND_DOWNLOAD_DEPENDENCIES'
import { getAnyAvailablePuppeteerExecutable } from './CHECK_AND_DOWNLOAD_DEPENDENCIES/utils/puppeteer-executable'
const { groupRobotAccessToken: dingTalkAccessToken } = readConfigFile('dingtalk.json')

View File

@@ -3,11 +3,6 @@ import { main, loginEventBus } from '@geekgeekrun/launch-bosszhipin-login-page-w
import { pipeWriteRegardlessError } from './utils/pipe'
import fs from "node:fs";
export enum DOWNLOAD_ERROR_EXIT_CODE {
NO_ERROR = 0,
DOWNLOAD_ERROR = 1
}
export const launchBossZhipinLoginPageWithPreloadExtension = async () => {
process.on('disconnect', () => app.exit())
app.dock?.hide()

View File

@@ -14,10 +14,8 @@ import {
import { ChildProcess } from 'child_process'
import * as JSONStream from 'JSONStream'
import { checkCookieListFormat } from '../../common/utils/cookie'
import {
DOWNLOAD_ERROR_EXIT_CODE,
getAnyAvailablePuppeteerExecutable
} from '../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES'
import { getAnyAvailablePuppeteerExecutable } from '../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/utils/puppeteer-executable/index'
import { DOWNLOAD_ERROR_EXIT_CODE } from '../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/index'
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
let mainWindow: BrowserWindow | null = null

View File

@@ -10,22 +10,25 @@
<script lang="ts" setup>
import { useRouter } from 'vue-router'
import { onMounted } from 'vue'
import { onMounted, ref } from 'vue'
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
const router = useRouter()
onMounted(async () => {
const checkDependenciesResult = await electron.ipcRenderer.invoke('check-dependencies')
const downloadProcessWaitee = Promise.withResolvers()
const checkDependenciesResult = ref({})
const downloadProcessWaitee = ref(null)
if (Object.values(checkDependenciesResult).includes(false)) {
onMounted(async () => {
checkDependenciesResult.value = await electron.ipcRenderer.invoke('check-dependencies')
downloadProcessWaitee.value = Promise.withResolvers()
if (Object.values(checkDependenciesResult.value).includes(false)) {
router.replace('/downloadingDependencies')
} else {
downloadProcessWaitee.resolve()
downloadProcessWaitee.value!.resolve()
}
downloadProcessWaitee.promise.then(async () => {
downloadProcessWaitee.value!.promise.then(async () => {
const isCookieFileValid = await electron.ipcRenderer.invoke('check-boss-zhipin-cookie-file')
if (!isCookieFileValid) {
router.replace('/cookieAssistant')