mirror of
https://github.com/geekgeekrun/geekgeekrun.git
synced 2026-05-12 02:19:55 +08:00
Merge branch 'perf/ui-blocked' into feature/ui
This commit is contained in:
@@ -1,103 +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 { is } from '@electron-toolkit/utils'
|
||||
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 (is.dev) {
|
||||
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
|
||||
@@ -1,66 +0,0 @@
|
||||
import { is } from '@electron-toolkit/utils'
|
||||
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 (is.dev) {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,53 +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 findAndLocateExistedChromiumExecutable from './check-and-locate-existed-chromium-executable'
|
||||
import {
|
||||
sleep
|
||||
} from '@geekgeekrun/utils/sleep.mjs'
|
||||
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
|
||||
|
||||
export enum DOWNLOAD_ERROR_EXIT_CODE {
|
||||
NO_ERROR = 0,
|
||||
DOWNLOAD_ERROR = 1
|
||||
}
|
||||
|
||||
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 {
|
||||
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()
|
||||
|
||||
@@ -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')
|
||||
@@ -0,0 +1,213 @@
|
||||
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 {
|
||||
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 () => {
|
||||
const electron = await import('electron')
|
||||
|
||||
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 findAndLocateUserInstalledChromiumExecutable()
|
||||
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 findAndLocateUserInstalledChromiumExecutableSync(): Promise<BrowserInfo> {
|
||||
const electron = await import('electron')
|
||||
|
||||
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 findAndLocateUserInstalledChromiumExecutable(): 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)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { parentPort } from 'node:worker_threads'
|
||||
import { findAndLocateUserInstalledChromiumExecutableSync } from '../index'
|
||||
;(async () => {
|
||||
try {
|
||||
const result = await findAndLocateUserInstalledChromiumExecutableSync()
|
||||
parentPort?.postMessage({
|
||||
type: 'RESULT',
|
||||
data: result
|
||||
})
|
||||
} catch (error) {
|
||||
parentPort?.postMessage({
|
||||
type: 'ERROR',
|
||||
error
|
||||
})
|
||||
}
|
||||
|
||||
process.exit(0)
|
||||
})()
|
||||
@@ -3,8 +3,8 @@ import { app } from 'electron'
|
||||
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 { pipeWriteRegardlessError } from '../utils/pipe'
|
||||
import { getAnyAvailablePuppeteerExecutable } from '../CHECK_AND_DOWNLOAD_DEPENDENCIES/utils/puppeteer-executable'
|
||||
|
||||
const { groupRobotAccessToken: dingTalkAccessToken } = readConfigFile('dingtalk.json')
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { runAutoChat } from './flow/GEEK_AUTO_START_CHAT_WITH_BOSS'
|
||||
import { runAutoChat } from './flow/GEEK_AUTO_START_CHAT_WITH_BOSS/index'
|
||||
import { openSettingWindow } from './flow/OPEN_SETTING_WINDOW'
|
||||
import { checkAndDownloadDependenciesForInit } from './flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/index'
|
||||
import { launchBossZhipinLoginPageWithPreloadExtension } from './flow/LAUNCH_BOSS_ZHIPIN_LOGIN_PAGE_WITH_PRELOAD_EXTENSION'
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { BrowserWindow, ipcMain, shell } from 'electron'
|
||||
import path from 'path'
|
||||
import * as childProcess from 'node:child_process'
|
||||
import { is } from '@electron-toolkit/utils'
|
||||
import {
|
||||
ensureConfigFileExist,
|
||||
ensureStorageFileExist,
|
||||
|
||||
configFileNameList,
|
||||
readConfigFile,
|
||||
writeConfigFile,
|
||||
@@ -15,10 +13,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
|
||||
|
||||
@@ -31,8 +27,8 @@ export function createMainWindow(): void {
|
||||
autoHideMenuBar: true,
|
||||
...(process.platform === 'linux'
|
||||
? {
|
||||
/* icon */
|
||||
}
|
||||
/* icon */
|
||||
}
|
||||
: {}),
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, '../preload/index.js'),
|
||||
@@ -40,7 +36,7 @@ export function createMainWindow(): void {
|
||||
}
|
||||
})
|
||||
|
||||
is.dev && mainWindow.webContents.openDevTools()
|
||||
process.env.NODE_ENV === 'development' && mainWindow.webContents.openDevTools()
|
||||
|
||||
mainWindow.on('ready-to-show', () => {
|
||||
mainWindow.show()
|
||||
@@ -53,7 +49,7 @@ export function createMainWindow(): void {
|
||||
|
||||
// HMR for renderer base on electron-vite cli.
|
||||
// Load the remote URL for development or the local html file for production.
|
||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||
if (process.env.NODE_ENV === 'development' && process.env['ELECTRON_RENDERER_URL']) {
|
||||
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
|
||||
} else {
|
||||
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'))
|
||||
@@ -70,7 +66,7 @@ export function createMainWindow(): void {
|
||||
return readConfigFile(fileName)
|
||||
})
|
||||
const result = {
|
||||
config: {},
|
||||
config: {}
|
||||
}
|
||||
|
||||
configFileNameList.forEach((fileName, index) => {
|
||||
@@ -106,17 +102,28 @@ export function createMainWindow(): void {
|
||||
|
||||
// const currentExecutablePath = app.getPath('exe')
|
||||
// console.log(currentExecutablePath)
|
||||
ipcMain.handle('prepare-run-geek-auto-start-chat-with-boss', async () => {
|
||||
mainWindow?.webContents.send('locating-puppeteer-executable')
|
||||
const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable()
|
||||
if (!puppeteerExecutable) {
|
||||
return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
|
||||
}
|
||||
mainWindow?.webContents.send('puppeteer-executable-is-located')
|
||||
})
|
||||
|
||||
let subProcessOfPuppeteer: ChildProcess | null = null
|
||||
ipcMain.handle('run-geek-auto-start-chat-with-boss', async () => {
|
||||
if (subProcessOfPuppeteer) {
|
||||
return
|
||||
}
|
||||
|
||||
const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable()
|
||||
if (!puppeteerExecutable) {
|
||||
return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
|
||||
}
|
||||
const subProcessEnv = {
|
||||
...process.env,
|
||||
MAIN_BOSSGEEKGO_UI_RUN_MODE: 'geekAutoStartWithBoss',
|
||||
PUPPETEER_EXECUTABLE_PATH: (await getAnyAvailablePuppeteerExecutable())!.executablePath
|
||||
PUPPETEER_EXECUTABLE_PATH: puppeteerExecutable.executablePath
|
||||
}
|
||||
subProcessOfPuppeteer = childProcess.spawn(process.argv[0], process.argv.slice(1), {
|
||||
env: subProcessEnv,
|
||||
@@ -171,7 +178,7 @@ export function createMainWindow(): void {
|
||||
}
|
||||
const subProcessEnv = {
|
||||
...process.env,
|
||||
MAIN_BOSSGEEKGO_UI_RUN_MODE: 'checkAndDownloadDependenciesForInit',
|
||||
MAIN_BOSSGEEKGO_UI_RUN_MODE: 'checkAndDownloadDependenciesForInit'
|
||||
}
|
||||
subProcessOfCheckAndDownloadDependencies = childProcess.spawn(
|
||||
process.argv[0],
|
||||
@@ -194,7 +201,7 @@ export function createMainWindow(): void {
|
||||
}
|
||||
case 'PUPPETEER_DOWNLOAD_ENCOUNTER_ERROR': {
|
||||
console.error(data)
|
||||
break;
|
||||
break
|
||||
}
|
||||
default: {
|
||||
return
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<template>
|
||||
<el-config-provider :locale="zhCn">
|
||||
<RouterView />
|
||||
<Suspense>
|
||||
<RouterView />
|
||||
</Suspense>
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { createApp } from 'vue'
|
||||
import ElementPlus from 'element-plus'
|
||||
import DependenciesSetupProgressIndicatorDialog from './index.vue'
|
||||
|
||||
export const mountGlobalDialog = (o: { dependenciesStatus: Record<string, boolean>, processWaitee? }) => {
|
||||
const containerElId = `elForDependenciesSetupProgressIndicatorDialog`
|
||||
|
||||
if (document.getElementById(containerElId)) {
|
||||
return
|
||||
}
|
||||
let containerEl: null | HTMLElement = (() => {
|
||||
const el = document.createElement('div')
|
||||
el.id = containerElId
|
||||
return el
|
||||
})()
|
||||
document.body.append(containerEl)
|
||||
|
||||
const dispose = () => {
|
||||
app?.unmount()
|
||||
containerEl?.remove()
|
||||
|
||||
app = null
|
||||
containerEl = null
|
||||
}
|
||||
let app: null | ReturnType<typeof createApp> = createApp(DependenciesSetupProgressIndicatorDialog, {
|
||||
modelValue: true,
|
||||
onClosed() {
|
||||
dispose()
|
||||
},
|
||||
dispose,
|
||||
dependenciesStatus: o?.dependenciesStatus,
|
||||
processWaitee: o?.processWaitee
|
||||
}).use(ElementPlus)
|
||||
app.mount(containerEl)
|
||||
|
||||
return {
|
||||
dispose
|
||||
}
|
||||
}
|
||||
@@ -1,106 +1,116 @@
|
||||
<template>
|
||||
<div
|
||||
class="container"
|
||||
class="flying-company-logo-list-container"
|
||||
:style="{
|
||||
gridTemplateColumns: `repeat(${colCount}, 1fr)`,
|
||||
gridTemplateRows: `repeat(${rowCount}, 1fr)`
|
||||
}"
|
||||
>
|
||||
<img
|
||||
v-for="n in rowCount * colCount"
|
||||
:key="n"
|
||||
class="dot"
|
||||
:src="currentIndexToSrcMap[n - 1]"
|
||||
@animationiteration="
|
||||
(ev) => {
|
||||
handleAnimationiteration(ev, n - 1)
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
ref="imageElContainer"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { logoQueueInjectKey } from './types'
|
||||
|
||||
const logoQueue = ref<string[]>([])
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
const rowCount = 4
|
||||
const colCount = 6
|
||||
const currentIndexToSrcMap: string[] = ref([])
|
||||
const logoQueue: HTMLImageElement[] = []
|
||||
const imageElContainer = ref<HTMLElement>()
|
||||
onMounted(async () => {
|
||||
const res = (
|
||||
await Promise.all(
|
||||
[...Object.values(import.meta.glob('./resources/*.png', { as: 'url' }))].map((it) => it())
|
||||
)
|
||||
).map((url) => {
|
||||
const img = new Image()
|
||||
img.src = url
|
||||
img.onanimationiteration = () => {
|
||||
const newImg = logoQueue.shift()!
|
||||
newImg.classList.add('dot')
|
||||
Object.assign(newImg.style, {
|
||||
position: 'relative',
|
||||
left: (Math.random() > 0.5 ? -1 : 1) * (100 * Math.random()) + 'px',
|
||||
bottom: (Math.random() > 0.5 ? -1 : 1) * (100 * Math.random()) + 'px',
|
||||
transform: `translateZ(${-5000}px)`
|
||||
})
|
||||
img.replaceWith(newImg)
|
||||
img.classList.remove('dot')
|
||||
Object.assign(img.style, {
|
||||
position: '',
|
||||
left: '',
|
||||
bottom: '',
|
||||
transform: ``
|
||||
})
|
||||
logoQueue.push(img as HTMLImageElement)
|
||||
}
|
||||
|
||||
Promise.all(
|
||||
[...Object.values(import.meta.glob('./resources/*.png', { as: 'url' }))].map((it) => it())
|
||||
).then((res) => {
|
||||
logoQueue.value = logoQueue.value.concat(res)
|
||||
currentIndexToSrcMap.value = logoQueue.value.splice(0, rowCount * colCount)
|
||||
return img
|
||||
})
|
||||
logoQueue.push(...res)
|
||||
|
||||
for (let i = 0; i < rowCount * colCount; i++) {
|
||||
const img = logoQueue.shift()!
|
||||
|
||||
Object.assign(img.style, {
|
||||
position: 'relative',
|
||||
left: -40 * Math.random() + 'px',
|
||||
bottom: -40 * Math.random() + 'px',
|
||||
transform: `translateZ(${-5000}px)`
|
||||
})
|
||||
|
||||
img.classList.add('dot')
|
||||
imageElContainer.value?.append(img)
|
||||
}
|
||||
})
|
||||
|
||||
const handleAnimationiteration = (ev, indexInSrcMap) => {
|
||||
logoQueue.value.push(currentIndexToSrcMap.value[indexInSrcMap])
|
||||
currentIndexToSrcMap.value[indexInSrcMap] = logoQueue.value.shift()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@keyframes fly-in {
|
||||
0% {
|
||||
transform: translateZ(-2500px);
|
||||
opacity: 0;
|
||||
}
|
||||
25% {
|
||||
opacity: 1;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
transform: translateZ(0);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
height: 600px;
|
||||
perspective: 1200px;
|
||||
.flying-company-logo-list-container {
|
||||
perspective: 600px;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
justify-content: center;
|
||||
gap: 30px;
|
||||
}
|
||||
</style>
|
||||
|
||||
.dot {
|
||||
display: block;
|
||||
--dot-run-duration: 1s;
|
||||
animation: fly-in var(--dot-run-duration) linear infinite;
|
||||
transform-origin: center;
|
||||
mix-blend-mode: darken;
|
||||
width: 200px;
|
||||
<style lang="scss">
|
||||
@keyframes fly-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
70% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
transform: translateZ(0);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.dot:nth-child(2n) {
|
||||
animation-delay: calc(-1 * var(--dot-run-duration));
|
||||
}
|
||||
.dot:nth-child(2n + 1) {
|
||||
animation-delay: calc(-0.1 * var(--dot-run-duration));
|
||||
}
|
||||
.dot:nth-child(3n + 1) {
|
||||
animation-delay: calc(-0.2 * var(--dot-run-duration));
|
||||
}
|
||||
.dot:nth-child(5n + 1) {
|
||||
animation-delay: calc(-0.1 * var(--dot-run-duration));
|
||||
}
|
||||
.dot:nth-child(7n + 1) {
|
||||
animation-delay: calc(-0.4 * var(--dot-run-duration));
|
||||
}
|
||||
.dot:nth-child(11n + 1) {
|
||||
animation-delay: calc(-0.5 * var(--dot-run-duration));
|
||||
}
|
||||
.dot:nth-child(13n + 1) {
|
||||
animation-delay: calc(0 * var(--dot-run-duration));
|
||||
}
|
||||
.dot:nth-child(17n + 1) {
|
||||
animation-delay: calc(-0.1 * var(--dot-run-duration));
|
||||
.flying-company-logo-list-container {
|
||||
.dot {
|
||||
display: block;
|
||||
--dot-run-duration: 2.5s;
|
||||
animation: fly-in var(--dot-run-duration) ease-in-out infinite;
|
||||
transform-origin: center;
|
||||
mix-blend-mode: darken;
|
||||
width: 200px;
|
||||
}
|
||||
.dot:nth-child(2n) {
|
||||
animation-delay: calc(-0.35 * var(--dot-run-duration));
|
||||
}
|
||||
.dot:nth-child(2n + 1) {
|
||||
animation-delay: calc(-0.15 * var(--dot-run-duration));
|
||||
}
|
||||
.dot:nth-child(3n + 1) {
|
||||
animation-delay: calc(-0.22 * var(--dot-run-duration));
|
||||
}
|
||||
.dot:nth-child(5n + 1) {
|
||||
animation-delay: calc(-0.05 * var(--dot-run-duration));
|
||||
}
|
||||
.dot:nth-child(7n + 1) {
|
||||
animation-delay: calc(-0.1 * var(--dot-run-duration));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { createApp } from 'vue'
|
||||
import ElementPlus from 'element-plus'
|
||||
import WaitForLogin from './index.vue'
|
||||
|
||||
export const mountGlobalDialog = (o: { processWaitee? }) => {
|
||||
const containerElId = `elForWaitForLogin`
|
||||
|
||||
if (document.getElementById(containerElId)) {
|
||||
return
|
||||
}
|
||||
let containerEl: null | HTMLElement = (() => {
|
||||
const el = document.createElement('div')
|
||||
el.id = containerElId
|
||||
return el
|
||||
})()
|
||||
document.body.append(containerEl)
|
||||
|
||||
const dispose = () => {
|
||||
app?.unmount()
|
||||
containerEl?.remove()
|
||||
|
||||
app = null
|
||||
containerEl = null
|
||||
}
|
||||
let app: null | ReturnType<typeof createApp> = createApp(WaitForLogin, {
|
||||
modelValue: true,
|
||||
onClosed() {
|
||||
dispose()
|
||||
},
|
||||
dispose,
|
||||
processWaitee: o?.processWaitee
|
||||
}).use(ElementPlus)
|
||||
app.mount(containerEl)
|
||||
|
||||
return {
|
||||
dispose
|
||||
}
|
||||
}
|
||||
41
packages/ui/src/renderer/src/page/BootstrapSplash/index.vue
Normal file
41
packages/ui/src/renderer/src/page/BootstrapSplash/index.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>愿你心想事成</div>
|
||||
<RouterView
|
||||
:dependencies-status="checkDependenciesResult"
|
||||
:process-waitee="downloadProcessWaitee"
|
||||
></RouterView>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const checkDependenciesResult = ref({})
|
||||
const downloadProcessWaitee = ref(null)
|
||||
|
||||
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.value!.resolve()
|
||||
}
|
||||
|
||||
downloadProcessWaitee.value!.promise.then(async () => {
|
||||
const isCookieFileValid = await electron.ipcRenderer.invoke('check-boss-zhipin-cookie-file')
|
||||
if (!isCookieFileValid) {
|
||||
router.replace('/cookieAssistant')
|
||||
} else {
|
||||
await sleep(1000)
|
||||
router.replace('/configuration')
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
@@ -1,20 +1,12 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:show-close="false"
|
||||
@open="handleDialogOpen"
|
||||
>
|
||||
<template v-if="!copiedDependenciesStatus.puppeteerExecutableAvailable">
|
||||
<div mb14px>正在下载核心组件</div>
|
||||
<el-progress
|
||||
:percentage="browserDownloadPercentage"
|
||||
:format="(n) => `${n.toFixed(1)}%`"
|
||||
:stroke-width="10"
|
||||
/>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<div v-if="!dependenciesStatus.puppeteerExecutableAvailable">
|
||||
<div mb14px>正在下载核心组件</div>
|
||||
<el-progress
|
||||
:percentage="browserDownloadPercentage"
|
||||
:format="(n) => `${n.toFixed(1)}%`"
|
||||
:stroke-width="10"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -22,7 +14,6 @@ import { ref, onUnmounted, PropType } from 'vue'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
|
||||
const props = defineProps({
|
||||
dispose: Function,
|
||||
dependenciesStatus: {
|
||||
type: Object as PropType<Record<string, boolean>>,
|
||||
default: () => ({})
|
||||
@@ -30,15 +21,6 @@ const props = defineProps({
|
||||
processWaitee: Object
|
||||
})
|
||||
|
||||
// shallow copy
|
||||
const copiedDependenciesStatus = {
|
||||
...props.dependenciesStatus
|
||||
}
|
||||
|
||||
const handleDialogOpen = () => {
|
||||
browserDownloadPercentage.value = 0
|
||||
}
|
||||
|
||||
const browserDownloadPercentage = ref(0)
|
||||
const handleBrowserDownloadProgress = (ev, { downloadedBytes, totalBytes }) => {
|
||||
browserDownloadPercentage.value = (downloadedBytes / totalBytes) * 100
|
||||
@@ -63,11 +45,11 @@ const processDownloadBrowser = async () => {
|
||||
|
||||
const promiseList: Array<Promise<void>> = []
|
||||
const processTasks = async () => {
|
||||
if (!copiedDependenciesStatus.puppeteerExecutableAvailable) {
|
||||
if (!props.dependenciesStatus.puppeteerExecutableAvailable) {
|
||||
const p = processDownloadBrowser()
|
||||
promiseList.push(p)
|
||||
p.then(() => {
|
||||
copiedDependenciesStatus.puppeteerExecutableAvailable = true
|
||||
props.dependenciesStatus.puppeteerExecutableAvailable = true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -77,7 +59,6 @@ const processTasks = async () => {
|
||||
p.then(() => {
|
||||
if (!promiseList.length) {
|
||||
props.processWaitee?.resolve?.()
|
||||
props.dispose?.()
|
||||
}
|
||||
})
|
||||
await p
|
||||
@@ -95,7 +76,6 @@ const processTasks = async () => {
|
||||
.catch(() => {
|
||||
// FIXME: should exit app here
|
||||
promiseList.length = 0
|
||||
props.dispose?.()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ElForm, ElMessage } from 'element-plus'
|
||||
import router from '../../router/index'
|
||||
import { mountGlobalDialog as mountDependenciesSetupProgressIndicatorDialog } from '@renderer/features/DependenciesSetupProgressIndicatorDialog/operations'
|
||||
import { mountGlobalDialog as mountWaitForLoginDialog } from '@renderer/features/WaitForLoginDialog/operations'
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
|
||||
const formContent = ref({
|
||||
dingtalkRobotAccessToken: '',
|
||||
@@ -51,28 +50,7 @@ const handleSubmit = async () => {
|
||||
await formRef.value!.validate()
|
||||
await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value))
|
||||
|
||||
try {
|
||||
const res = await electron.ipcRenderer.invoke(
|
||||
'run-geek-auto-start-chat-with-boss',
|
||||
JSON.stringify(formContent.value)
|
||||
)
|
||||
|
||||
if (res.type === 'GEEK_AUTO_START_CHAT_WITH_BOSS_STARTED') {
|
||||
router.replace('/geekAutoStartChatWithBoss/runningStatus')
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof Error && err.message.includes('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')) {
|
||||
ElMessage.error({
|
||||
message: `核心组件损坏,正在尝试修复`
|
||||
})
|
||||
const checkDependenciesResult = await electron.ipcRenderer.invoke('check-dependencies')
|
||||
if (Object.values(checkDependenciesResult).includes(false)) {
|
||||
mountDependenciesSetupProgressIndicatorDialog(checkDependenciesResult)
|
||||
// TODO: should continue interrupted task
|
||||
}
|
||||
}
|
||||
console.error(err)
|
||||
}
|
||||
router.replace('/geekAutoStartChatWithBoss/prepareRun')
|
||||
}
|
||||
const handleSave = async () => {
|
||||
await formRef.value!.validate()
|
||||
@@ -89,7 +67,7 @@ const handleExpectCompaniesInputBlur = (event) => {
|
||||
}
|
||||
|
||||
const handleClickLaunchLogin = () => {
|
||||
mountWaitForLoginDialog()
|
||||
router.replace('/cookieAssistant')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import { mountGlobalDialog as mountDependenciesSetupProgressIndicatorDialog } from '@renderer/features/DependenciesSetupProgressIndicatorDialog/operations'
|
||||
import { mountGlobalDialog as mountWaitForLoginDialog } from '@renderer/features/WaitForLoginDialog/operations'
|
||||
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
const unmountedCbs: Array<InstanceType<typeof Function>> = []
|
||||
onUnmounted(() => {
|
||||
while (unmountedCbs.length) {
|
||||
@@ -14,31 +13,27 @@ onUnmounted(() => {
|
||||
} catch {}
|
||||
}
|
||||
})
|
||||
const goToCheckBossZhipinCookieFile = () => router.replace('/cookieAssistant')
|
||||
onMounted(() => {
|
||||
electron.ipcRenderer.on('check-boss-zhipin-cookie-file', mountWaitForLoginDialog)
|
||||
electron.ipcRenderer.on('check-boss-zhipin-cookie-file', goToCheckBossZhipinCookieFile)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
electron.ipcRenderer.removeListener('check-boss-zhipin-cookie-file', mountWaitForLoginDialog)
|
||||
electron.ipcRenderer.removeListener(
|
||||
'check-boss-zhipin-cookie-file',
|
||||
goToCheckBossZhipinCookieFile
|
||||
)
|
||||
})
|
||||
;(async () => {
|
||||
const checkDependenciesResult = await electron.ipcRenderer.invoke('check-dependencies')
|
||||
if (Object.values(checkDependenciesResult).includes(false)) {
|
||||
const processWaitee = Promise.withResolvers()
|
||||
mountDependenciesSetupProgressIndicatorDialog({
|
||||
checkDependenciesResult, processWaitee
|
||||
})
|
||||
|
||||
await processWaitee.promise
|
||||
router.replace('/')
|
||||
return
|
||||
}
|
||||
|
||||
const isCookieFileValid = await electron.ipcRenderer.invoke('check-boss-zhipin-cookie-file')
|
||||
if (!isCookieFileValid) {
|
||||
const processWaitee = Promise.withResolvers()
|
||||
mountWaitForLoginDialog({
|
||||
processWaitee
|
||||
})
|
||||
|
||||
await processWaitee.promise
|
||||
router.replace('/cookieAssistant')
|
||||
return
|
||||
}
|
||||
})()
|
||||
</script>
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="!cookieInvalid"
|
||||
title="Boss直聘 Cookie助手"
|
||||
:width="720"
|
||||
top="20px"
|
||||
lock-scroll
|
||||
:show-close="!cookieInvalid"
|
||||
>
|
||||
<div class="cookie-assistant-page">
|
||||
<div ml1em mt1em mb1em >Cookie 助手</div>
|
||||
<el-alert
|
||||
v-if="cookieInvalid"
|
||||
type="warning"
|
||||
@@ -18,21 +10,17 @@
|
||||
由于您是首次使用本程序,或者您之前使用的Boss直聘账号登录状态失效,因此您需要重新获取登录凭证。
|
||||
</el-alert>
|
||||
<div ml1em mt1em line-height-normal>
|
||||
如果您了解如何获取Cookie、了解有效的Cookie格式,可以直接在下方输入框中进行编辑。<br />
|
||||
手动编辑较为麻烦,建议您打开已登录过Boss直聘的浏览器,使用
|
||||
<a
|
||||
如果您了解如何获取Cookie、了解有效的Cookie格式,可以直接在下方输入框中进行编辑。由于手动编辑较为麻烦,建议您打开已登录过Boss直聘的浏览器,使用<a
|
||||
color-blue
|
||||
decoration-none
|
||||
href="javascript:void(0)"
|
||||
@click.prevent="handleEditThisCookieExtensionStoreLinkClick"
|
||||
>EditThisCookie 扩展程序</a
|
||||
>
|
||||
复制Cookie,然后粘贴在下方输入框中。<br />
|
||||
格式为被序列化为JSON的数组,不含两侧引号。
|
||||
>复制Cookie,然后粘贴在下方输入框中。文本格式为被序列化为JSON的数组,不含两侧引号。
|
||||
</div>
|
||||
<br />
|
||||
<div ml1em line-height-normal>
|
||||
如果您不了解Cookie相关概念,或者不能访问Chrome扩展程序商店下载EditThisCookie来获取Cookie,请按照以下步骤进行操作:
|
||||
如果您不了解Cookie相关概念,或者期望操作简单一些,请按照以下步骤进行操作:
|
||||
</div>
|
||||
<ol lh-2em mt-0>
|
||||
<li>
|
||||
@@ -104,23 +92,20 @@
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button v-if="!cookieInvalid" @click="dispose">关闭</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">保存Cookie</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<footer flex mt20px pb20px flex-justify-end>
|
||||
<el-button v-if="!cookieInvalid" @click="handleCancel">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ElForm, ElMessage } from 'element-plus'
|
||||
import { ref, onUnmounted, onMounted } from 'vue'
|
||||
import { checkCookieListFormat } from '../../../../common/utils/cookie'
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const props = defineProps({
|
||||
dispose: Function,
|
||||
processWaitee: Object
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
const cookieInvalid = ref(false)
|
||||
|
||||
enum LOGIN_COOKIE_WAITING_STATUS {
|
||||
@@ -195,6 +180,9 @@ const handleEditThisCookieExtensionStoreLinkClick = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
router.replace('/configuration')
|
||||
}
|
||||
const handleSubmit = async () => {
|
||||
await formRef.value!.validate()
|
||||
await electron.ipcRenderer.invoke('write-storage-file', {
|
||||
@@ -202,8 +190,7 @@ const handleSubmit = async () => {
|
||||
data: formContent.value.collectedCookies
|
||||
})
|
||||
ElMessage.success('Boss直聘 Cookie 保存成功')
|
||||
props.processWaitee?.resolve?.()
|
||||
props.dispose()
|
||||
router.replace('/configuration')
|
||||
}
|
||||
|
||||
const handleBossZhipinLoginPageClosed = () => {
|
||||
@@ -235,6 +222,13 @@ onUnmounted(() => {
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cookie-assistant-page {
|
||||
max-width: 640px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.cookie-form.el-form {
|
||||
.el-form-item__error--inline {
|
||||
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="geek-auto-start-chat-with-boss__prepare-run">
|
||||
<div>{{ statusText }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { hasOwn } from '@vue/shared'
|
||||
import { PropType, computed } from 'vue'
|
||||
|
||||
const statusTextMap = {
|
||||
_: '请稍后,正在进行一些处理',
|
||||
'locating-puppeteer-executable': '正在寻找可用的浏览器'
|
||||
} as const
|
||||
|
||||
const props = defineProps({
|
||||
status: {
|
||||
type: String as PropType<keyof typeof statusText>,
|
||||
default: '_'
|
||||
}
|
||||
})
|
||||
const statusText = computed(() => {
|
||||
if (hasOwn(statusTextMap, props.status)) {
|
||||
return statusTextMap[props.status]
|
||||
} else {
|
||||
return statusTextMap._
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="geek-auto-start-chat-with-boss__running-status">
|
||||
<FlyingCompanyLogoList class="flying-company-logo-list" />
|
||||
<div class="tip">
|
||||
<article>
|
||||
<h1>👋 BOSS炸弹正在运行</h1>
|
||||
@@ -9,14 +10,14 @@
|
||||
</article>
|
||||
<el-button :disabled="isStopping" @click="handleStopButtonClick">停止开聊</el-button>
|
||||
</div>
|
||||
<FlyingCompanyLogoList class="flying-company-logo-list" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onUnmounted } from 'vue';
|
||||
import { ref, onUnmounted, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import FlyingCompanyLogoList from '../../features/FlyingCompanyLogoList/index.vue'
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const { ipcRenderer } = electron
|
||||
const router = useRouter()
|
||||
@@ -40,16 +41,46 @@ onUnmounted(() => {
|
||||
ipcRenderer.removeListener('geek-auto-start-chat-with-boss-stopped', handleStopped)
|
||||
ipcRenderer.removeListener('geek-auto-start-chat-with-boss-stopping', handleStopping)
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await electron.ipcRenderer.invoke('run-geek-auto-start-chat-with-boss')
|
||||
} catch (err) {
|
||||
if (err instanceof Error && err.message.includes('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')) {
|
||||
ElMessage.error({
|
||||
message: `核心组件损坏,正在尝试修复`
|
||||
})
|
||||
const checkDependenciesResult = await electron.ipcRenderer.invoke('check-dependencies')
|
||||
if (Object.values(checkDependenciesResult).includes(false)) {
|
||||
router.replace('/')
|
||||
// TODO: should continue interrupted task
|
||||
}
|
||||
}
|
||||
console.error(err)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.geek-auto-start-chat-with-boss__running-status {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.tip {
|
||||
margin: 0 auto;
|
||||
margin-top: -15vh;
|
||||
max-width: 640px;
|
||||
}
|
||||
.flying-company-logo-list {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
opacity: 0.25;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1 +1,37 @@
|
||||
<template><RouterView /></template>
|
||||
<template><RouterView :status="currentStatus" /></template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
|
||||
const currentStatus = ref('')
|
||||
onMounted(() => {
|
||||
const promise = electron.ipcRenderer.invoke('prepare-run-geek-auto-start-chat-with-boss')
|
||||
const handleLocatingPuppeteerExecutable = () => {
|
||||
currentStatus.value = 'locating-puppeteer-executable'
|
||||
}
|
||||
electron.ipcRenderer.once('locating-puppeteer-executable', handleLocatingPuppeteerExecutable)
|
||||
onUnmounted(() => {
|
||||
electron.ipcRenderer.removeListener(
|
||||
'locating-puppeteer-executable',
|
||||
handleLocatingPuppeteerExecutable
|
||||
)
|
||||
})
|
||||
|
||||
promise
|
||||
.then(() => {
|
||||
router.replace('/geekAutoStartChatWithBoss/runningStatus')
|
||||
})
|
||||
.catch(async (err) => {
|
||||
if (err instanceof Error && err.message.includes('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')) {
|
||||
ElMessage.error({
|
||||
message: `核心组件损坏,正在尝试修复`
|
||||
})
|
||||
router.replace('/')
|
||||
}
|
||||
console.error(err)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
|
||||
import BootstrapSplash from '@renderer/page/BootstrapSplash/index.vue'
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/',
|
||||
component: defineComponent({ setup: () => h('div') }),
|
||||
redirect: '/configuration',
|
||||
children: []
|
||||
path: '/cookieAssistant',
|
||||
component: () => import('@renderer/page/CookieAssistant/index.vue'),
|
||||
meta: {
|
||||
title: 'Cookie 助手'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/configuration',
|
||||
@@ -26,6 +27,13 @@ const routes: Array<RouteRecordRaw> = [
|
||||
path: '/geekAutoStartChatWithBoss',
|
||||
component: () => import('@renderer/page/GeekAutoStartChatWithBoss/index.vue'),
|
||||
children: [
|
||||
{
|
||||
path: 'prepareRun',
|
||||
component: () => import('@renderer/page/GeekAutoStartChatWithBoss/PrepareRun.vue'),
|
||||
meta: {
|
||||
title: 'BOSS炸弹 正在预热'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'runningStatus',
|
||||
component: () => import('@renderer/page/GeekAutoStartChatWithBoss/RunningStatus.vue'),
|
||||
@@ -34,7 +42,23 @@ const routes: Array<RouteRecordRaw> = [
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: BootstrapSplash,
|
||||
meta: {
|
||||
title: '薪想事成'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/downloadingDependencies',
|
||||
component: () => import('@renderer/page/BootstrapSplash/page/DownloadingDependencies.vue'),
|
||||
meta: {
|
||||
title: '正在下载浏览器'
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
|
||||
Reference in New Issue
Block a user