feat: try no gui mode
parent
3ae6282870
commit
164532f3b7
|
@ -42,14 +42,17 @@ function newServerStart ({ mitmproxyPath }) {
|
|||
}
|
||||
server.start = newServerStart
|
||||
async function startup ({ mitmproxyPath }) {
|
||||
let server
|
||||
const conf = config.get()
|
||||
|
||||
if (conf.server.enabled) {
|
||||
try {
|
||||
await server.start({ mitmproxyPath })
|
||||
server = await server.start({ mitmproxyPath })
|
||||
} catch (err) {
|
||||
log.error('代理服务启动失败:', err)
|
||||
}
|
||||
}
|
||||
|
||||
if (conf.proxy.enabled) {
|
||||
try {
|
||||
await proxy.start()
|
||||
|
@ -57,8 +60,10 @@ async function startup ({ mitmproxyPath }) {
|
|||
log.error('开启系统代理失败:', err)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const plugins = []
|
||||
|
||||
for (const key in plugin) {
|
||||
if (conf.plugin[key].enabled) {
|
||||
const start = async () => {
|
||||
|
@ -72,12 +77,15 @@ async function startup ({ mitmproxyPath }) {
|
|||
plugins.push(start())
|
||||
}
|
||||
}
|
||||
|
||||
if (plugins && plugins.length > 0) {
|
||||
await Promise.all(plugins)
|
||||
}
|
||||
} catch (err) {
|
||||
log.error('开启插件失败:', err)
|
||||
}
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
async function shutdown () {
|
||||
|
|
|
@ -30,7 +30,7 @@ const serverApi = {
|
|||
return this.close()
|
||||
}
|
||||
},
|
||||
async start ({ mitmproxyPath, plugins }) {
|
||||
async start ({ mitmproxyPath, plugins, options }) {
|
||||
const allConfig = config.get()
|
||||
const serverConfig = lodash.cloneDeep(allConfig.server)
|
||||
|
||||
|
@ -78,7 +78,7 @@ const serverApi = {
|
|||
const runningConfigPath = path.join(basePath, '/running.json')
|
||||
fs.writeFileSync(runningConfigPath, jsonApi.stringify(serverConfig))
|
||||
log.info('保存 running.json 运行时配置文件成功:', runningConfigPath)
|
||||
const serverProcess = fork(mitmproxyPath, [runningConfigPath])
|
||||
const serverProcess = fork(mitmproxyPath, [runningConfigPath], options)
|
||||
server = {
|
||||
id: serverProcess.pid,
|
||||
process: serverProcess,
|
||||
|
@ -86,6 +86,7 @@ const serverApi = {
|
|||
serverProcess.send({ type: 'action', event: { key: 'close' } })
|
||||
},
|
||||
}
|
||||
|
||||
serverProcess.on('beforeExit', (code) => {
|
||||
log.warn('server process beforeExit, code:', code)
|
||||
})
|
||||
|
@ -113,7 +114,8 @@ const serverApi = {
|
|||
event.fire('speed', msg.event)
|
||||
}
|
||||
})
|
||||
return { port: serverConfig.port }
|
||||
|
||||
return { port: serverConfig.port, server }
|
||||
},
|
||||
async kill () {
|
||||
if (server) {
|
||||
|
|
|
@ -45,7 +45,7 @@ function log4jsConfigure (categories) {
|
|||
|
||||
for (const category of categories) {
|
||||
config.appenders[category] = { ...appenderConfig, filename: path.join(basePath, `/${category}.log`) }
|
||||
config.categories[category] = { appenders: [category, 'std'], level }
|
||||
config.categories[category] = { appenders: [category, ...process.env.NO_CONSOLE_LOG ? [] : ['std']], level }
|
||||
}
|
||||
|
||||
log4js.configure(config)
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
____ _____ _ __
|
||||
/ __ \___ _ __ / ___/(_)___/ /__ _________ ______
|
||||
/ / / / _ \ | / /_____\__ \/ / __ / _ \/ ___/ __ `/ ___/
|
||||
/ /_/ / __/ |/ /_____/__/ / / /_/ / __/ /__/ /_/ / /
|
||||
/_____/\___/|___/ /____/_/\__,_/\___/\___/\__,_/_/
|
||||
|
||||
|
||||
==================== 开发者边车 ====================
|
|
@ -13,6 +13,7 @@
|
|||
"lint": "vue-cli-service lint",
|
||||
"electron:build": "vue-cli-service electron:build",
|
||||
"electron": "vue-cli-service electron:serve",
|
||||
"electron:headless": "cross-env ELECTRON_RUN_AS_NODE=1 vue-cli-service electron:serve",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"postuninstall": "electron-builder install-app-deps",
|
||||
"electron:icons": "electron-icon-builder --input=./public/logo/win.png --output=build --flatten",
|
||||
|
@ -27,6 +28,7 @@
|
|||
"@vscode/sudo-prompt": "^9.3.1",
|
||||
"adm-zip": "^0.5.16",
|
||||
"ant-design-vue": "^1.7.8",
|
||||
"cac": "^6.7.14",
|
||||
"electron-baidu-tongji": "^1.0.5",
|
||||
"electron-updater": "^6.3.9",
|
||||
"json5": "^2.2.3",
|
||||
|
@ -44,6 +46,7 @@
|
|||
"@vue/babel-preset-jsx": "^1.4.0",
|
||||
"@vue/cli-plugin-babel": "^5.0.8",
|
||||
"@vue/cli-service": "^5.0.8",
|
||||
"cross-env": "^7.0.3",
|
||||
"electron": "^19.1.9",
|
||||
"electron-builder": "^25.1.8",
|
||||
"electron-icon-builder": "^2.0.1",
|
||||
|
|
|
@ -1,490 +1,38 @@
|
|||
'use strict'
|
||||
/* global __static */
|
||||
import path from 'node:path'
|
||||
import DevSidecar from '@docmirror/dev-sidecar'
|
||||
import { app, BrowserWindow, dialog, globalShortcut, ipcMain, Menu, nativeImage, nativeTheme, powerMonitor, protocol, Tray } from 'electron'
|
||||
import minimist from 'minimist'
|
||||
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
|
||||
import backend from './bridge/backend'
|
||||
import jsonApi from '@docmirror/mitmproxy/src/json'
|
||||
import log from './utils/util.log'
|
||||
import { fork } from 'node:child_process'
|
||||
|
||||
const isWindows = process.platform === 'win32'
|
||||
const isMac = process.platform === 'darwin'
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
const RUN_AS_NODE = !!process.env.ELECTRON_RUN_AS_NODE
|
||||
|
||||
// 避免其他系统出现异常,只有 Windows 使用 './background/powerMonitor'
|
||||
let _powerMonitor = powerMonitor
|
||||
if (isWindows) {
|
||||
try {
|
||||
_powerMonitor = require('./background/powerMonitor').powerMonitor
|
||||
} catch (e) {
|
||||
log.error(`加载 './background/powerMonitor' 失败,现捕获异常并使用默认的 powerMonitor。\r\n目前,启动着DS重启电脑时,将无法正常关闭系统代理,届时请自行关闭系统代理!\r\n捕获的异常信息:`, e)
|
||||
}
|
||||
}
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let win
|
||||
let winIsHidden = false
|
||||
|
||||
let tray // 防止被内存清理
|
||||
let forceClose = false
|
||||
DevSidecar.api.config.reload()
|
||||
let hideDockWhenWinClose = DevSidecar.api.config.get().app.dock.hideWhenWinClose || false
|
||||
// Scheme must be registered before the app is ready
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{ scheme: 'app', privileges: { secure: true, standard: true } },
|
||||
])
|
||||
|
||||
function openDevTools () {
|
||||
try {
|
||||
log.debug('尝试打开 `开发者工具`')
|
||||
win.webContents.openDevTools()
|
||||
log.debug('打开 `开发者工具` 成功')
|
||||
} catch (e) {
|
||||
log.error('打开 `开发者工具` 失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
function closeDevTools () {
|
||||
try {
|
||||
log.debug('尝试关闭 `开发者工具`')
|
||||
win.webContents.closeDevTools()
|
||||
log.debug('关闭 `开发者工具` 成功')
|
||||
} catch (e) {
|
||||
log.error('关闭 `开发者工具` 失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
function switchDevTools () {
|
||||
if (!win || !win.webContents) {
|
||||
return
|
||||
}
|
||||
if (win.webContents.isDevToolsOpened()) {
|
||||
closeDevTools()
|
||||
;(async () => {
|
||||
if (RUN_AS_NODE) {
|
||||
await startHeadless()
|
||||
} else {
|
||||
openDevTools()
|
||||
await startGUI()
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
||||
// 隐藏主窗口,并创建托盘,绑定关闭事件
|
||||
function setTray () {
|
||||
// const topMenu = Menu.buildFromTemplate({})
|
||||
// Menu.setApplicationMenu(topMenu)
|
||||
// 用一个 Tray 来表示一个图标,这个图标处于正在运行的系统的通知区
|
||||
// 通常被添加到一个 context menu 上.
|
||||
// 系统托盘右键菜单
|
||||
const trayMenuTemplate = [
|
||||
{
|
||||
// 系统托盘图标目录
|
||||
label: 'DevTools (F12)',
|
||||
click: switchDevTools,
|
||||
},
|
||||
{
|
||||
// 系统托盘图标目录
|
||||
label: '退出',
|
||||
click: () => {
|
||||
log.info('force quit')
|
||||
forceClose = true
|
||||
quit()
|
||||
},
|
||||
},
|
||||
]
|
||||
// 设置系统托盘图标
|
||||
const iconRootPath = path.join(__dirname, '../extra/icons/tray')
|
||||
let iconPath = path.join(iconRootPath, 'icon.png')
|
||||
const iconWhitePath = path.join(iconRootPath, 'icon-white.png')
|
||||
const iconBlackPath = path.join(iconRootPath, 'icon-black.png')
|
||||
if (isMac) {
|
||||
iconPath = nativeTheme.shouldUseDarkColors ? iconWhitePath : iconBlackPath
|
||||
}
|
||||
|
||||
const trayIcon = nativeImage.createFromPath(iconPath)
|
||||
const appTray = new Tray(trayIcon)
|
||||
|
||||
// 当桌面主题更新时
|
||||
if (isMac) {
|
||||
nativeTheme.on('updated', () => {
|
||||
console.log('i am changed')
|
||||
if (nativeTheme.shouldUseDarkColors) {
|
||||
console.log('i am dark.')
|
||||
tray.setImage(iconWhitePath)
|
||||
} else {
|
||||
console.log('i am light.')
|
||||
tray.setImage(iconBlackPath)
|
||||
// tray.setPressedImage(iconWhitePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 图标的上下文菜单
|
||||
const contextMenu = Menu.buildFromTemplate(trayMenuTemplate)
|
||||
|
||||
// 设置托盘悬浮提示
|
||||
appTray.setToolTip('DevSidecar-开发者边车辅助工具')
|
||||
// 单击托盘小图标显示应用
|
||||
appTray.on('click', () => {
|
||||
// 显示主程序
|
||||
showWin()
|
||||
})
|
||||
|
||||
appTray.on('right-click', () => {
|
||||
setTimeout(() => {
|
||||
appTray.popUpContextMenu(contextMenu)
|
||||
}, 200)
|
||||
})
|
||||
|
||||
return appTray
|
||||
}
|
||||
|
||||
function isLinux () {
|
||||
const platform = DevSidecar.api.shell.getSystemPlatform()
|
||||
return platform === 'linux'
|
||||
}
|
||||
|
||||
function hideWin () {
|
||||
if (win) {
|
||||
if (isLinux()) {
|
||||
quit()
|
||||
return
|
||||
}
|
||||
win.hide()
|
||||
if (isMac && hideDockWhenWinClose) {
|
||||
app.dock.hide()
|
||||
}
|
||||
winIsHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
function showWin () {
|
||||
if (win) {
|
||||
win.show()
|
||||
}
|
||||
if (app.dock) {
|
||||
app.dock.show()
|
||||
}
|
||||
winIsHidden = false
|
||||
}
|
||||
|
||||
function changeAppConfig (config) {
|
||||
if (config.hideDockWhenWinClose != null) {
|
||||
hideDockWhenWinClose = config.hideDockWhenWinClose
|
||||
}
|
||||
}
|
||||
|
||||
function createWindow (startHideWindow) {
|
||||
// Create the browser window.
|
||||
const windowSize = DevSidecar.api.config.get().app.windowSize || {}
|
||||
win = new BrowserWindow({
|
||||
width: windowSize.width || 900,
|
||||
height: windowSize.height || 750,
|
||||
title: 'DevSidecar',
|
||||
webPreferences: {
|
||||
enableRemoteModule: true,
|
||||
contextIsolation: false,
|
||||
nativeWindowOpen: true, // ADD THIS
|
||||
// preload: path.join(__dirname, 'preload.js'),
|
||||
// Use pluginOptions.nodeIntegration, leave this alone
|
||||
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
|
||||
nodeIntegration: true, // process.env.ELECTRON_NODE_INTEGRATION
|
||||
},
|
||||
show: !startHideWindow,
|
||||
icon: path.join(__static, 'icon.png'),
|
||||
})
|
||||
winIsHidden = !!startHideWindow
|
||||
|
||||
Menu.setApplicationMenu(null)
|
||||
win.setMenu(null)
|
||||
|
||||
// !!IMPORTANT
|
||||
if (isWindows && typeof _powerMonitor.setupMainWindow === 'function') {
|
||||
_powerMonitor.setupMainWindow(win)
|
||||
}
|
||||
|
||||
if (process.env.WEBPACK_DEV_SERVER_URL) {
|
||||
// Load the url of the dev server if in development mode
|
||||
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
|
||||
if (!process.env.IS_TEST) {
|
||||
setTimeout(openDevTools, 2000)
|
||||
}
|
||||
async function startHeadless () {
|
||||
const cli = path.join(__dirname, 'dev-sidecar-cli.js')
|
||||
let argv
|
||||
if (isDevelopment) {
|
||||
argv = process.argv.splice(3)
|
||||
} else {
|
||||
createProtocol('app')
|
||||
// Load the index.html when not in development
|
||||
win.loadURL('app://./index.html')
|
||||
argv = process.argv
|
||||
}
|
||||
|
||||
if (startHideWindow) {
|
||||
hideWin()
|
||||
}
|
||||
|
||||
win.on('closed', async (...args) => {
|
||||
log.info('win closed:', ...args)
|
||||
win = null
|
||||
tray = null
|
||||
const cliProcess = fork(cli, argv, {
|
||||
env: {
|
||||
...process.env,
|
||||
NO_CONSOLE_LOG: true,
|
||||
},
|
||||
detached: true,
|
||||
stdio: 'inherit',
|
||||
})
|
||||
|
||||
ipcMain.on('close', async (event, message) => {
|
||||
if (message.value === 1) {
|
||||
quit()
|
||||
} else {
|
||||
hideWin()
|
||||
}
|
||||
})
|
||||
|
||||
win.on('close', (e, ...args) => {
|
||||
log.info('win close:', e, ...args)
|
||||
if (forceClose) {
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
if (isLinux()) {
|
||||
quit()
|
||||
return
|
||||
}
|
||||
const config = DevSidecar.api.config.get()
|
||||
const closeStrategy = config.app.closeStrategy
|
||||
if (closeStrategy === 0) {
|
||||
// 弹窗提示,选择关闭策略
|
||||
win.webContents.send('close.showTip', closeStrategy)
|
||||
} else if (closeStrategy === 1) {
|
||||
// 直接退出
|
||||
quit()
|
||||
} else if (closeStrategy === 2) {
|
||||
// 隐藏窗口
|
||||
hideWin()
|
||||
}
|
||||
})
|
||||
|
||||
win.on('session-end', async (e, ...args) => {
|
||||
log.info('win session-end:', e, ...args)
|
||||
await quit()
|
||||
})
|
||||
|
||||
const shortcut = (event, input) => {
|
||||
// 按 F12,打开/关闭 开发者工具
|
||||
if (input.key === 'F12') {
|
||||
// 阻止默认的按键事件行为
|
||||
event.preventDefault()
|
||||
// 切换开发者工具显示状态
|
||||
switchDevTools()
|
||||
// eslint-disable-next-line style/brace-style
|
||||
}
|
||||
// 按 F5,刷新页面
|
||||
else if (input.key === 'F5') {
|
||||
// 阻止默认的按键事件行为
|
||||
event.preventDefault()
|
||||
// 刷新页面
|
||||
win.webContents.reload()
|
||||
}
|
||||
}
|
||||
|
||||
// 监听键盘事件
|
||||
win.webContents.on('before-input-event', (event, input) => {
|
||||
if (input.type !== 'keyUp' || input.control || input.alt || input.shift || input.meta) {
|
||||
return
|
||||
}
|
||||
win.webContents.executeJavaScript('config')
|
||||
.then((value) => {
|
||||
console.info('window.config:', value, ', key:', input.key)
|
||||
if (!value || (value.disableBeforeInputEvent !== true && value.disableBeforeInputEvent !== 'true')) {
|
||||
shortcut(event, input)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
shortcut(event, input)
|
||||
})
|
||||
})
|
||||
|
||||
// 监听渲染进程发送过来的消息
|
||||
win.webContents.on('ipc-message', (event, channel, message, ...args) => {
|
||||
console.info('win ipc-message:', event, channel, message, ...args)
|
||||
if (channel === 'change-showHideShortcut') {
|
||||
registerShowHideShortcut(message)
|
||||
}
|
||||
})
|
||||
cliProcess.unref()
|
||||
}
|
||||
|
||||
async function beforeQuit () {
|
||||
return DevSidecar.api.shutdown()
|
||||
async function startGUI () {
|
||||
|
||||
}
|
||||
async function quit () {
|
||||
if (tray) {
|
||||
tray.displayBalloon({ title: '正在关闭', content: '关闭中,请稍候。。。' })
|
||||
}
|
||||
await beforeQuit()
|
||||
forceClose = true
|
||||
app.quit()
|
||||
}
|
||||
|
||||
function registerShowHideShortcut (showHideShortcut) {
|
||||
globalShortcut.unregisterAll()
|
||||
if (showHideShortcut && showHideShortcut !== '无' && showHideShortcut.length > 1) {
|
||||
try {
|
||||
const registerSuccess = globalShortcut.register(DevSidecar.api.config.get().app.showHideShortcut, () => {
|
||||
if (winIsHidden || !win.isFocused()) {
|
||||
if (!win.isFocused()) {
|
||||
win.focus()
|
||||
}
|
||||
if (winIsHidden) {
|
||||
showWin()
|
||||
}
|
||||
} else {
|
||||
// linux,快捷键不关闭窗口
|
||||
if (!isLinux()) {
|
||||
hideWin()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (registerSuccess) {
|
||||
log.info('注册快捷键成功:', DevSidecar.api.config.get().app.showHideShortcut)
|
||||
} else {
|
||||
log.error('注册快捷键失败:', DevSidecar.api.config.get().app.showHideShortcut)
|
||||
}
|
||||
} catch (e) {
|
||||
log.error('注册快捷键异常:', DevSidecar.api.config.get().app.showHideShortcut, ', error:', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initApp () {
|
||||
if (isMac) {
|
||||
app.whenReady().then(() => {
|
||||
app.dock.setIcon(path.join(__dirname, '../build/mac/512x512.png'))
|
||||
})
|
||||
}
|
||||
|
||||
// 全局监听快捷键,用于 显示/隐藏 窗口
|
||||
app.whenReady().then(async () => {
|
||||
registerShowHideShortcut(DevSidecar.api.config.get().app.showHideShortcut)
|
||||
})
|
||||
}
|
||||
|
||||
// -------------执行开始---------------
|
||||
app.disableHardwareAcceleration() // 禁用gpu
|
||||
|
||||
// 开启后是否默认隐藏window
|
||||
let startHideWindow = !DevSidecar.api.config.get().app.startShowWindow
|
||||
if (app.getLoginItemSettings().wasOpenedAsHidden) {
|
||||
startHideWindow = true
|
||||
} else if (process.argv) {
|
||||
const args = minimist(process.argv)
|
||||
log.info('start args:', args)
|
||||
|
||||
// 通过启动参数,判断是否隐藏窗口
|
||||
const hideWindowArg = `${args.hideWindow}`
|
||||
if (hideWindowArg === 'true' || hideWindowArg === '1') {
|
||||
startHideWindow = true
|
||||
} else if (hideWindowArg === 'false' || hideWindowArg === '0') {
|
||||
startHideWindow = false
|
||||
}
|
||||
}
|
||||
log.info('startHideWindow = ', startHideWindow, ', app.getLoginItemSettings() = ', jsonApi.stringify2(app.getLoginItemSettings()))
|
||||
|
||||
// 禁止双开
|
||||
const isFirstInstance = app.requestSingleInstanceLock()
|
||||
if (!isFirstInstance) {
|
||||
log.info('is second instance')
|
||||
setTimeout(() => {
|
||||
app.quit()
|
||||
}, 1000)
|
||||
} else {
|
||||
app.on('before-quit', async () => {
|
||||
log.info('before-quit')
|
||||
if (process.platform === 'darwin') {
|
||||
quit()
|
||||
}
|
||||
})
|
||||
app.on('will-quit', () => {
|
||||
log.info('应用关闭,注销所有快捷键')
|
||||
globalShortcut.unregisterAll()
|
||||
})
|
||||
app.on('second-instance', (event, commandLine) => {
|
||||
log.info('new app started, command:', commandLine)
|
||||
if (win) {
|
||||
showWin()
|
||||
win.focus()
|
||||
}
|
||||
})
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
log.info('window-all-closed')
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (win == null) {
|
||||
createWindow(false)
|
||||
} else {
|
||||
showWin()
|
||||
}
|
||||
})
|
||||
|
||||
// initApp()
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', async () => {
|
||||
if (isDevelopment && !process.env.IS_TEST) {
|
||||
// Install Vue Devtools
|
||||
// try {
|
||||
// await installExtension(VUEJS_DEVTOOLS)
|
||||
// } catch (e) {
|
||||
// log.error('Vue Devtools failed to install:', e.toString())
|
||||
// }
|
||||
}
|
||||
try {
|
||||
createWindow(startHideWindow)
|
||||
const context = { win, app, beforeQuit, quit, ipcMain, dialog, log, api: DevSidecar.api, changeAppConfig }
|
||||
backend.install(context) // 模块安装
|
||||
} catch (err) {
|
||||
log.info('error:', err)
|
||||
}
|
||||
|
||||
try {
|
||||
// 最小化到托盘
|
||||
tray = setTray()
|
||||
} catch (err) {
|
||||
log.info('error:', err)
|
||||
}
|
||||
|
||||
_powerMonitor.on('shutdown', async (e) => {
|
||||
if (e) {
|
||||
e.preventDefault()
|
||||
}
|
||||
log.info('系统关机,恢复代理设置')
|
||||
await quit()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
initApp()
|
||||
|
||||
// Exit cleanly on request from parent process in development mode.
|
||||
if (isDevelopment) {
|
||||
if (process.platform === 'win32') {
|
||||
process.on('message', (data) => {
|
||||
if (data === 'graceful-exit') {
|
||||
quit()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
process.on('SIGINT', () => {
|
||||
quit()
|
||||
})
|
||||
}
|
||||
}
|
||||
// 系统关机和重启时的操作
|
||||
process.on('exit', () => {
|
||||
log.info('进程结束,退出app')
|
||||
quit()
|
||||
})
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import DevSidecar from '@docmirror/dev-sidecar'
|
||||
import { getExtraPath, mitmproxyPath } from './config'
|
||||
import path from 'node:path'
|
||||
import fs from 'node:fs'
|
||||
import os from 'node:os'
|
||||
import cac from 'cac'
|
||||
|
||||
const cli = cac('dev-sidecar')
|
||||
const pk = require('../../package.json')
|
||||
|
||||
async function startup () {
|
||||
console.log('启动 DevSidecar 服务')
|
||||
const config = DevSidecar.api.config.get()
|
||||
|
||||
if (config.server.pid) {
|
||||
process.kill(config.server.pid, os.constants.signals.SIGINT)
|
||||
config.server.pid = null
|
||||
DevSidecar.api.config.save(config)
|
||||
}
|
||||
|
||||
// 开启自动下载远程配置
|
||||
await DevSidecar.api.config.startAutoDownloadRemoteConfig()
|
||||
|
||||
const { server } = await DevSidecar.api.server.start({
|
||||
mitmproxyPath,
|
||||
options: {
|
||||
stdio: 'ignore',
|
||||
},
|
||||
})
|
||||
|
||||
// 写入server进程pid
|
||||
config.server.pid = server.id
|
||||
DevSidecar.api.config.save(config)
|
||||
}
|
||||
|
||||
async function stop () {
|
||||
console.log('关闭 DevSidecar 服务')
|
||||
const config = DevSidecar.api.config.get()
|
||||
|
||||
if (config.server.pid) {
|
||||
process.kill(config.server.pid, os.constants.signals.SIGINT)
|
||||
config.server.pid = null
|
||||
DevSidecar.api.config.save(config)
|
||||
}
|
||||
}
|
||||
|
||||
async function restart () {
|
||||
console.log('重启 DevSidecar 服务')
|
||||
const config = DevSidecar.api.config.get()
|
||||
|
||||
if (config.server.pid) {
|
||||
process.kill(config.server.pid, 'SIGINT')
|
||||
}
|
||||
|
||||
await startup()
|
||||
}
|
||||
|
||||
const banner = fs.readFileSync(path.join(getExtraPath(), 'banner.txt'))
|
||||
console.log(banner.toString())
|
||||
|
||||
cli
|
||||
.help()
|
||||
.usage('start')
|
||||
.version(pk.version)
|
||||
|
||||
cli
|
||||
.command('start', '启动 DevSidecar 服务')
|
||||
.action(async () => {
|
||||
await startup()
|
||||
process.exit(0)
|
||||
})
|
||||
|
||||
cli
|
||||
.command('stop', '关闭 DevSidecar 服务')
|
||||
.action(stop)
|
||||
|
||||
cli
|
||||
.command('restart', '重启 DevSidecar 服务')
|
||||
.action(restart)
|
||||
|
||||
cli.parse()
|
|
@ -0,0 +1,93 @@
|
|||
import path from 'node:path'
|
||||
import DevSidecar from '@docmirror/dev-sidecar'
|
||||
import fs from 'node:fs'
|
||||
|
||||
const jsonApi = require('@docmirror/mitmproxy/src/json')
|
||||
const log = require('../utils/util.log')
|
||||
const configFromFiles = require('@docmirror/dev-sidecar/src/config/index.js').configFromFiles
|
||||
|
||||
export const mitmproxyPath = path.join(__dirname, 'mitmproxy.js')
|
||||
process.env.DS_EXTRA_PATH = path.join(__dirname, '../extra/')
|
||||
|
||||
function getDefaultConfigBasePath () {
|
||||
return DevSidecar.api.config.get().server.setting.userBasePath
|
||||
}
|
||||
|
||||
export function getSettingsPath () {
|
||||
const dir = getDefaultConfigBasePath()
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir)
|
||||
} else {
|
||||
// 兼容1.7.3及以下版本的配置文件处理逻辑
|
||||
const newFilePath = path.join(dir, '/setting.json')
|
||||
const oldFilePath = path.join(dir, '/setting.json5')
|
||||
if (!fs.existsSync(newFilePath) && fs.existsSync(oldFilePath)) {
|
||||
return oldFilePath // 如果新文件不存在,且旧文件存在,则返回旧文件路径
|
||||
}
|
||||
return newFilePath
|
||||
}
|
||||
return path.join(dir, '/setting.json')
|
||||
}
|
||||
|
||||
export function getLogDir () {
|
||||
return configFromFiles.app.logFileSavePath || path.join(getDefaultConfigBasePath(), '/logs/')
|
||||
}
|
||||
|
||||
export function loadConfig () {
|
||||
const settingPath = getSettingsPath()
|
||||
let setting = {}
|
||||
if (fs.existsSync(settingPath)) {
|
||||
const file = fs.readFileSync(settingPath)
|
||||
try {
|
||||
setting = jsonApi.parse(file.toString())
|
||||
log.info('读取 setting.json 成功:', settingPath)
|
||||
} catch (e) {
|
||||
log.error('读取 setting.json 失败:', settingPath, ', error:', e)
|
||||
}
|
||||
if (setting == null) {
|
||||
setting = {}
|
||||
}
|
||||
}
|
||||
if (setting.overwall == null) {
|
||||
setting.overwall = false
|
||||
}
|
||||
|
||||
if (setting.installTime == null) {
|
||||
// 设置安装时间
|
||||
setting.installTime = getDateTimeStr()
|
||||
|
||||
// 初始化 rootCa.setuped
|
||||
if (setting.rootCa == null) {
|
||||
setting.rootCa = {
|
||||
setuped: false,
|
||||
desc: '根证书未安装',
|
||||
}
|
||||
}
|
||||
|
||||
// 保存 setting.json
|
||||
saveConfig(setting)
|
||||
}
|
||||
return setting
|
||||
}
|
||||
|
||||
export function saveConfig (setting) {
|
||||
const settingPath = getSettingsPath()
|
||||
fs.writeFileSync(settingPath, jsonApi.stringify(setting))
|
||||
log.info('保存 setting.json 配置文件成功:', settingPath)
|
||||
}
|
||||
|
||||
export function getDateTimeStr () {
|
||||
const date = new Date() // 创建一个表示当前日期和时间的 Date 对象
|
||||
const year = date.getFullYear() // 获取年份
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0') // 获取月份(注意月份从 0 开始计数)
|
||||
const day = String(date.getDate()).padStart(2, '0') // 获取天数
|
||||
const hours = String(date.getHours()).padStart(2, '0') // 获取小时
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0') // 获取分钟
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0') // 获取秒数
|
||||
const milliseconds = String(date.getMilliseconds()).padStart(3, '0') // 获取毫秒
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`
|
||||
}
|
||||
|
||||
export function getExtraPath () {
|
||||
return path.join(__dirname, '../extra/')
|
||||
}
|
|
@ -0,0 +1,493 @@
|
|||
/* global __static */
|
||||
|
||||
import path from 'node:path'
|
||||
import DevSidecar from '@docmirror/dev-sidecar'
|
||||
import { app, BrowserWindow, dialog, globalShortcut, ipcMain, Menu, nativeImage, nativeTheme, powerMonitor, protocol, Tray } from 'electron'
|
||||
import minimist from 'minimist'
|
||||
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
|
||||
import backend from './bridge/backend'
|
||||
import jsonApi from '@docmirror/mitmproxy/src/json'
|
||||
import log from './utils/util.log'
|
||||
|
||||
DevSidecar.api.config.reload()
|
||||
|
||||
// Scheme must be registered before the app is ready
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{ scheme: 'app', privileges: { secure: true, standard: true } },
|
||||
])
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let win
|
||||
let winIsHidden = false
|
||||
|
||||
let tray // 防止被内存清理
|
||||
let forceClose = false
|
||||
|
||||
let hideDockWhenWinClose = DevSidecar.api.config.get().app.dock.hideWhenWinClose || false
|
||||
|
||||
const isWindows = process.platform === 'win32'
|
||||
const isMac = process.platform === 'darwin'
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
// 避免其他系统出现异常,只有 Windows 使用 './background/powerMonitor'
|
||||
let _powerMonitor = powerMonitor
|
||||
if (isWindows) {
|
||||
try {
|
||||
_powerMonitor = require('./background/powerMonitor').powerMonitor
|
||||
} catch (e) {
|
||||
log.error(`加载 './background/powerMonitor' 失败,现捕获异常并使用默认的 powerMonitor。\r\n目前,启动着DS重启电脑时,将无法正常关闭系统代理,届时请自行关闭系统代理!\r\n捕获的异常信息:`, e)
|
||||
}
|
||||
}
|
||||
|
||||
function openDevTools () {
|
||||
try {
|
||||
log.debug('尝试打开 `开发者工具`')
|
||||
win.webContents.openDevTools()
|
||||
log.debug('打开 `开发者工具` 成功')
|
||||
} catch (e) {
|
||||
log.error('打开 `开发者工具` 失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
function closeDevTools () {
|
||||
try {
|
||||
log.debug('尝试关闭 `开发者工具`')
|
||||
win.webContents.closeDevTools()
|
||||
log.debug('关闭 `开发者工具` 成功')
|
||||
} catch (e) {
|
||||
log.error('关闭 `开发者工具` 失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
function switchDevTools () {
|
||||
if (!win || !win.webContents) {
|
||||
return
|
||||
}
|
||||
if (win.webContents.isDevToolsOpened()) {
|
||||
closeDevTools()
|
||||
} else {
|
||||
openDevTools()
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏主窗口,并创建托盘,绑定关闭事件
|
||||
function setTray () {
|
||||
// const topMenu = Menu.buildFromTemplate({})
|
||||
// Menu.setApplicationMenu(topMenu)
|
||||
// 用一个 Tray 来表示一个图标,这个图标处于正在运行的系统的通知区
|
||||
// 通常被添加到一个 context menu 上.
|
||||
// 系统托盘右键菜单
|
||||
const trayMenuTemplate = [
|
||||
{
|
||||
// 系统托盘图标目录
|
||||
label: 'DevTools (F12)',
|
||||
click: switchDevTools,
|
||||
},
|
||||
{
|
||||
// 系统托盘图标目录
|
||||
label: '退出',
|
||||
click: () => {
|
||||
log.info('force quit')
|
||||
forceClose = true
|
||||
quit()
|
||||
},
|
||||
},
|
||||
]
|
||||
// 设置系统托盘图标
|
||||
const iconRootPath = path.join(__dirname, '../extra/icons/tray')
|
||||
let iconPath = path.join(iconRootPath, 'icon.png')
|
||||
const iconWhitePath = path.join(iconRootPath, 'icon-white.png')
|
||||
const iconBlackPath = path.join(iconRootPath, 'icon-black.png')
|
||||
if (isMac) {
|
||||
iconPath = nativeTheme.shouldUseDarkColors ? iconWhitePath : iconBlackPath
|
||||
}
|
||||
|
||||
const trayIcon = nativeImage.createFromPath(iconPath)
|
||||
const appTray = new Tray(trayIcon)
|
||||
|
||||
// 当桌面主题更新时
|
||||
if (isMac) {
|
||||
nativeTheme.on('updated', () => {
|
||||
console.log('i am changed')
|
||||
if (nativeTheme.shouldUseDarkColors) {
|
||||
console.log('i am dark.')
|
||||
tray.setImage(iconWhitePath)
|
||||
} else {
|
||||
console.log('i am light.')
|
||||
tray.setImage(iconBlackPath)
|
||||
// tray.setPressedImage(iconWhitePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 图标的上下文菜单
|
||||
const contextMenu = Menu.buildFromTemplate(trayMenuTemplate)
|
||||
|
||||
// 设置托盘悬浮提示
|
||||
appTray.setToolTip('DevSidecar-开发者边车辅助工具')
|
||||
// 单击托盘小图标显示应用
|
||||
appTray.on('click', () => {
|
||||
// 显示主程序
|
||||
showWin()
|
||||
})
|
||||
|
||||
appTray.on('right-click', () => {
|
||||
setTimeout(() => {
|
||||
appTray.popUpContextMenu(contextMenu)
|
||||
}, 200)
|
||||
})
|
||||
|
||||
return appTray
|
||||
}
|
||||
|
||||
function isLinux () {
|
||||
const platform = DevSidecar.api.shell.getSystemPlatform()
|
||||
return platform === 'linux'
|
||||
}
|
||||
|
||||
function hideWin () {
|
||||
if (win) {
|
||||
if (isLinux()) {
|
||||
quit()
|
||||
return
|
||||
}
|
||||
win.hide()
|
||||
if (isMac && hideDockWhenWinClose) {
|
||||
app.dock.hide()
|
||||
}
|
||||
winIsHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
function showWin () {
|
||||
if (win) {
|
||||
win.show()
|
||||
}
|
||||
if (app.dock) {
|
||||
app.dock.show()
|
||||
}
|
||||
winIsHidden = false
|
||||
}
|
||||
|
||||
function changeAppConfig (config) {
|
||||
if (config.hideDockWhenWinClose != null) {
|
||||
hideDockWhenWinClose = config.hideDockWhenWinClose
|
||||
}
|
||||
}
|
||||
|
||||
function createWindow (startHideWindow) {
|
||||
// Create the browser window.
|
||||
const windowSize = DevSidecar.api.config.get().app.windowSize || {}
|
||||
win = new BrowserWindow({
|
||||
width: windowSize.width || 900,
|
||||
height: windowSize.height || 750,
|
||||
title: 'DevSidecar',
|
||||
webPreferences: {
|
||||
enableRemoteModule: true,
|
||||
contextIsolation: false,
|
||||
nativeWindowOpen: true, // ADD THIS
|
||||
// preload: path.join(__dirname, 'preload.js'),
|
||||
// Use pluginOptions.nodeIntegration, leave this alone
|
||||
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
|
||||
nodeIntegration: true, // process.env.ELECTRON_NODE_INTEGRATION
|
||||
},
|
||||
show: !startHideWindow,
|
||||
icon: path.join(__static, 'icon.png'),
|
||||
})
|
||||
winIsHidden = !!startHideWindow
|
||||
|
||||
Menu.setApplicationMenu(null)
|
||||
win.setMenu(null)
|
||||
|
||||
// !!IMPORTANT
|
||||
if (isWindows && typeof _powerMonitor.setupMainWindow === 'function') {
|
||||
_powerMonitor.setupMainWindow(win)
|
||||
}
|
||||
|
||||
if (process.env.WEBPACK_DEV_SERVER_URL) {
|
||||
// Load the url of the dev server if in development mode
|
||||
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
|
||||
if (!process.env.IS_TEST) {
|
||||
setTimeout(openDevTools, 2000)
|
||||
}
|
||||
} else {
|
||||
createProtocol('app')
|
||||
// Load the index.html when not in development
|
||||
win.loadURL('app://./index.html')
|
||||
}
|
||||
|
||||
if (startHideWindow) {
|
||||
hideWin()
|
||||
}
|
||||
|
||||
win.on('closed', async (...args) => {
|
||||
log.info('win closed:', ...args)
|
||||
win = null
|
||||
tray = null
|
||||
})
|
||||
|
||||
ipcMain.on('close', async (event, message) => {
|
||||
if (message.value === 1) {
|
||||
quit()
|
||||
} else {
|
||||
hideWin()
|
||||
}
|
||||
})
|
||||
|
||||
win.on('close', (e, ...args) => {
|
||||
log.info('win close:', e, ...args)
|
||||
if (forceClose) {
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
if (isLinux()) {
|
||||
quit()
|
||||
return
|
||||
}
|
||||
const config = DevSidecar.api.config.get()
|
||||
const closeStrategy = config.app.closeStrategy
|
||||
if (closeStrategy === 0) {
|
||||
// 弹窗提示,选择关闭策略
|
||||
win.webContents.send('close.showTip', closeStrategy)
|
||||
} else if (closeStrategy === 1) {
|
||||
// 直接退出
|
||||
quit()
|
||||
} else if (closeStrategy === 2) {
|
||||
// 隐藏窗口
|
||||
hideWin()
|
||||
}
|
||||
})
|
||||
|
||||
win.on('session-end', async (e, ...args) => {
|
||||
log.info('win session-end:', e, ...args)
|
||||
await quit()
|
||||
})
|
||||
|
||||
const shortcut = (event, input) => {
|
||||
// 按 F12,打开/关闭 开发者工具
|
||||
if (input.key === 'F12') {
|
||||
// 阻止默认的按键事件行为
|
||||
event.preventDefault()
|
||||
// 切换开发者工具显示状态
|
||||
switchDevTools()
|
||||
// eslint-disable-next-line style/brace-style
|
||||
}
|
||||
// 按 F5,刷新页面
|
||||
else if (input.key === 'F5') {
|
||||
// 阻止默认的按键事件行为
|
||||
event.preventDefault()
|
||||
// 刷新页面
|
||||
win.webContents.reload()
|
||||
}
|
||||
}
|
||||
|
||||
// 监听键盘事件
|
||||
win.webContents.on('before-input-event', (event, input) => {
|
||||
if (input.type !== 'keyUp' || input.control || input.alt || input.shift || input.meta) {
|
||||
return
|
||||
}
|
||||
win.webContents.executeJavaScript('config')
|
||||
.then((value) => {
|
||||
console.info('window.config:', value, ', key:', input.key)
|
||||
if (!value || (value.disableBeforeInputEvent !== true && value.disableBeforeInputEvent !== 'true')) {
|
||||
shortcut(event, input)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
shortcut(event, input)
|
||||
})
|
||||
})
|
||||
|
||||
// 监听渲染进程发送过来的消息
|
||||
win.webContents.on('ipc-message', (event, channel, message, ...args) => {
|
||||
console.info('win ipc-message:', event, channel, message, ...args)
|
||||
if (channel === 'change-showHideShortcut') {
|
||||
registerShowHideShortcut(message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function beforeQuit () {
|
||||
return DevSidecar.api.shutdown()
|
||||
}
|
||||
async function quit () {
|
||||
if (tray) {
|
||||
tray.displayBalloon({ title: '正在关闭', content: '关闭中,请稍候。。。' })
|
||||
}
|
||||
await beforeQuit()
|
||||
forceClose = true
|
||||
app.quit()
|
||||
}
|
||||
|
||||
function registerShowHideShortcut (showHideShortcut) {
|
||||
globalShortcut.unregisterAll()
|
||||
if (showHideShortcut && showHideShortcut !== '无' && showHideShortcut.length > 1) {
|
||||
try {
|
||||
const registerSuccess = globalShortcut.register(DevSidecar.api.config.get().app.showHideShortcut, () => {
|
||||
if (winIsHidden || !win.isFocused()) {
|
||||
if (!win.isFocused()) {
|
||||
win.focus()
|
||||
}
|
||||
if (winIsHidden) {
|
||||
showWin()
|
||||
}
|
||||
} else {
|
||||
// linux,快捷键不关闭窗口
|
||||
if (!isLinux()) {
|
||||
hideWin()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (registerSuccess) {
|
||||
log.info('注册快捷键成功:', DevSidecar.api.config.get().app.showHideShortcut)
|
||||
} else {
|
||||
log.error('注册快捷键失败:', DevSidecar.api.config.get().app.showHideShortcut)
|
||||
}
|
||||
} catch (e) {
|
||||
log.error('注册快捷键异常:', DevSidecar.api.config.get().app.showHideShortcut, ', error:', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initApp () {
|
||||
if (isMac) {
|
||||
app.whenReady().then(() => {
|
||||
app.dock.setIcon(path.join(__dirname, '../build/mac/512x512.png'))
|
||||
})
|
||||
}
|
||||
|
||||
// 全局监听快捷键,用于 显示/隐藏 窗口
|
||||
app.whenReady().then(async () => {
|
||||
registerShowHideShortcut(DevSidecar.api.config.get().app.showHideShortcut)
|
||||
})
|
||||
}
|
||||
|
||||
// -------------执行开始---------------
|
||||
app.disableHardwareAcceleration() // 禁用gpu
|
||||
|
||||
// 开启后是否默认隐藏window
|
||||
let startHideWindow = !DevSidecar.api.config.get().app.startShowWindow
|
||||
if (app.getLoginItemSettings().wasOpenedAsHidden) {
|
||||
startHideWindow = true
|
||||
} else if (process.argv) {
|
||||
const args = minimist(process.argv)
|
||||
log.info('start args:', args)
|
||||
|
||||
// 通过启动参数,判断是否隐藏窗口
|
||||
const hideWindowArg = `${args.hideWindow}`
|
||||
if (hideWindowArg === 'true' || hideWindowArg === '1') {
|
||||
startHideWindow = true
|
||||
} else if (hideWindowArg === 'false' || hideWindowArg === '0') {
|
||||
startHideWindow = false
|
||||
}
|
||||
}
|
||||
log.info('startHideWindow = ', startHideWindow, ', app.getLoginItemSettings() = ', jsonApi.stringify2(app.getLoginItemSettings()))
|
||||
|
||||
// 禁止双开
|
||||
const isFirstInstance = app.requestSingleInstanceLock()
|
||||
if (!isFirstInstance) {
|
||||
log.info('is second instance')
|
||||
setTimeout(() => {
|
||||
app.quit()
|
||||
}, 1000)
|
||||
} else {
|
||||
app.on('before-quit', async () => {
|
||||
log.info('before-quit')
|
||||
if (process.platform === 'darwin') {
|
||||
quit()
|
||||
}
|
||||
})
|
||||
app.on('will-quit', () => {
|
||||
log.info('应用关闭,注销所有快捷键')
|
||||
globalShortcut.unregisterAll()
|
||||
})
|
||||
app.on('second-instance', (event, commandLine) => {
|
||||
log.info('new app started, command:', commandLine)
|
||||
if (win) {
|
||||
showWin()
|
||||
win.focus()
|
||||
}
|
||||
})
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
log.info('window-all-closed')
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (win == null) {
|
||||
createWindow(false)
|
||||
} else {
|
||||
showWin()
|
||||
}
|
||||
})
|
||||
|
||||
// initApp()
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', async () => {
|
||||
if (isDevelopment && !process.env.IS_TEST) {
|
||||
// Install Vue Devtools
|
||||
// try {
|
||||
// await installExtension(VUEJS_DEVTOOLS)
|
||||
// } catch (e) {
|
||||
// log.error('Vue Devtools failed to install:', e.toString())
|
||||
// }
|
||||
}
|
||||
try {
|
||||
createWindow(startHideWindow)
|
||||
const context = { win, app, beforeQuit, quit, ipcMain, dialog, log, api: DevSidecar.api, changeAppConfig }
|
||||
backend.install(context) // 模块安装
|
||||
} catch (err) {
|
||||
log.info('error:', err)
|
||||
}
|
||||
|
||||
try {
|
||||
// 最小化到托盘
|
||||
tray = setTray()
|
||||
} catch (err) {
|
||||
log.info('error:', err)
|
||||
}
|
||||
|
||||
_powerMonitor.on('shutdown', async (e) => {
|
||||
if (e) {
|
||||
e.preventDefault()
|
||||
}
|
||||
log.info('系统关机,恢复代理设置')
|
||||
await quit()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
initApp()
|
||||
|
||||
// Exit cleanly on request from parent process in development mode.
|
||||
if (isDevelopment) {
|
||||
if (process.platform === 'win32') {
|
||||
process.on('message', (data) => {
|
||||
if (data === 'graceful-exit') {
|
||||
quit()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
process.on('SIGINT', () => {
|
||||
quit()
|
||||
})
|
||||
}
|
||||
}
|
||||
// 系统关机和重启时的操作
|
||||
process.on('exit', () => {
|
||||
log.info('进程结束,退出app')
|
||||
quit()
|
||||
})
|
|
@ -1,33 +1,11 @@
|
|||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import DevSidecar from '@docmirror/dev-sidecar'
|
||||
import { ipcMain } from 'electron'
|
||||
import lodash from 'lodash'
|
||||
import { getDateTimeStr, getDefaultConfigBasePath, getLogDir, loadConfig, mitmproxyPath, saveConfig } from '../../background/config'
|
||||
|
||||
const jsonApi = require('@docmirror/mitmproxy/src/json')
|
||||
const pk = require('../../../package.json')
|
||||
const configFromFiles = require('@docmirror/dev-sidecar/src/config/index.js').configFromFiles
|
||||
const log = require('../../utils/util.log')
|
||||
|
||||
const mitmproxyPath = path.join(__dirname, 'mitmproxy.js')
|
||||
process.env.DS_EXTRA_PATH = path.join(__dirname, '../extra/')
|
||||
|
||||
const getDefaultConfigBasePath = function () {
|
||||
return DevSidecar.api.config.get().server.setting.userBasePath
|
||||
}
|
||||
|
||||
const getDateTimeStr = function () {
|
||||
const date = new Date() // 创建一个表示当前日期和时间的 Date 对象
|
||||
const year = date.getFullYear() // 获取年份
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0') // 获取月份(注意月份从 0 开始计数)
|
||||
const day = String(date.getDate()).padStart(2, '0') // 获取天数
|
||||
const hours = String(date.getHours()).padStart(2, '0') // 获取小时
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0') // 获取分钟
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0') // 获取秒数
|
||||
const milliseconds = String(date.getMilliseconds()).padStart(3, '0') // 获取毫秒
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`
|
||||
}
|
||||
|
||||
const localApi = {
|
||||
/**
|
||||
* 返回所有api列表,供vue来ipc调用
|
||||
|
@ -52,7 +30,7 @@ const localApi = {
|
|||
return getDefaultConfigBasePath()
|
||||
},
|
||||
getLogDir () {
|
||||
return configFromFiles.app.logFileSavePath || path.join(getDefaultConfigBasePath(), '/logs/')
|
||||
return getLogDir()
|
||||
},
|
||||
getSystemPlatform (throwIfUnknown = false) {
|
||||
return DevSidecar.api.shell.getSystemPlatform(throwIfUnknown)
|
||||
|
@ -63,45 +41,10 @@ const localApi = {
|
|||
*/
|
||||
setting: {
|
||||
load () {
|
||||
const settingPath = _getSettingsPath()
|
||||
let setting = {}
|
||||
if (fs.existsSync(settingPath)) {
|
||||
const file = fs.readFileSync(settingPath)
|
||||
try {
|
||||
setting = jsonApi.parse(file.toString())
|
||||
log.info('读取 setting.json 成功:', settingPath)
|
||||
} catch (e) {
|
||||
log.error('读取 setting.json 失败:', settingPath, ', error:', e)
|
||||
}
|
||||
if (setting == null) {
|
||||
setting = {}
|
||||
}
|
||||
}
|
||||
if (setting.overwall == null) {
|
||||
setting.overwall = false
|
||||
}
|
||||
|
||||
if (setting.installTime == null) {
|
||||
// 设置安装时间
|
||||
setting.installTime = getDateTimeStr()
|
||||
|
||||
// 初始化 rootCa.setuped
|
||||
if (setting.rootCa == null) {
|
||||
setting.rootCa = {
|
||||
setuped: false,
|
||||
desc: '根证书未安装',
|
||||
}
|
||||
}
|
||||
|
||||
// 保存 setting.json
|
||||
localApi.setting.save(setting)
|
||||
}
|
||||
return setting
|
||||
return loadConfig()
|
||||
},
|
||||
save (setting = {}) {
|
||||
const settingPath = _getSettingsPath()
|
||||
fs.writeFileSync(settingPath, jsonApi.stringify(setting))
|
||||
log.info('保存 setting.json 配置文件成功:', settingPath)
|
||||
saveConfig(setting)
|
||||
},
|
||||
},
|
||||
/**
|
||||
|
@ -140,22 +83,6 @@ function _deepFindFunction (list, parent, parentKey) {
|
|||
}
|
||||
}
|
||||
|
||||
function _getSettingsPath () {
|
||||
const dir = getDefaultConfigBasePath()
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir)
|
||||
} else {
|
||||
// 兼容1.7.3及以下版本的配置文件处理逻辑
|
||||
const newFilePath = path.join(dir, '/setting.json')
|
||||
const oldFilePath = path.join(dir, '/setting.json5')
|
||||
if (!fs.existsSync(newFilePath) && fs.existsSync(oldFilePath)) {
|
||||
return oldFilePath // 如果新文件不存在,且旧文件存在,则返回旧文件路径
|
||||
}
|
||||
return newFilePath
|
||||
}
|
||||
return path.join(dir, '/setting.json')
|
||||
}
|
||||
|
||||
function invoke (api, param) {
|
||||
let target = lodash.get(localApi, api)
|
||||
if (target == null) {
|
||||
|
|
|
@ -122,6 +122,7 @@ module.exports = defineConfig({
|
|||
},
|
||||
chainWebpackMainProcess (config) {
|
||||
config.entry('mitmproxy').add(path.join(__dirname, 'src/bridge/mitmproxy.js'))
|
||||
config.entry('dev-sidecar-cli').add(path.join(__dirname, 'src/background/cli.js'))
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -87,6 +87,9 @@ importers:
|
|||
ant-design-vue:
|
||||
specifier: ^1.7.8
|
||||
version: 1.7.8(vue-template-compiler@2.7.16)(vue@2.7.16)
|
||||
cac:
|
||||
specifier: ^6.7.14
|
||||
version: 6.7.14
|
||||
electron-baidu-tongji:
|
||||
specifier: ^1.0.5
|
||||
version: 1.0.5
|
||||
|
@ -133,6 +136,9 @@ importers:
|
|||
'@vue/cli-service':
|
||||
specifier: ^5.0.8
|
||||
version: 5.0.8(@vue/compiler-sfc@3.5.12)(ejs@3.1.10)(encoding@0.1.13)(handlebars@4.7.8)(lodash@4.17.21)(sass-loader@16.0.3(sass@1.81.0)(webpack@5.96.1))(underscore@1.13.7)(vue-template-compiler@2.7.16)(vue@2.7.16)(webpack-sources@3.2.3)
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
electron:
|
||||
specifier: ^19.1.9
|
||||
version: 19.1.9
|
||||
|
@ -2239,6 +2245,10 @@ packages:
|
|||
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
cac@6.7.14:
|
||||
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
cacache@16.1.3:
|
||||
resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==}
|
||||
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
|
||||
|
@ -2771,6 +2781,11 @@ packages:
|
|||
crc@3.8.0:
|
||||
resolution: {integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==}
|
||||
|
||||
cross-env@7.0.3:
|
||||
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
|
||||
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
|
||||
hasBin: true
|
||||
|
||||
cross-spawn@4.0.2:
|
||||
resolution: {integrity: sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==}
|
||||
|
||||
|
@ -9841,6 +9856,8 @@ snapshots:
|
|||
|
||||
bytes@3.1.2: {}
|
||||
|
||||
cac@6.7.14: {}
|
||||
|
||||
cacache@16.1.3:
|
||||
dependencies:
|
||||
'@npmcli/fs': 2.1.2
|
||||
|
@ -10262,6 +10279,10 @@ snapshots:
|
|||
buffer: 5.7.1
|
||||
optional: true
|
||||
|
||||
cross-env@7.0.3:
|
||||
dependencies:
|
||||
cross-spawn: 7.0.5
|
||||
|
||||
cross-spawn@4.0.2:
|
||||
dependencies:
|
||||
lru-cache: 4.1.5
|
||||
|
|
Loading…
Reference in New Issue