347 lines
14 KiB
TypeScript
347 lines
14 KiB
TypeScript
import { app, BrowserWindow, ipcMain, shell, Tray, Menu, nativeImage } from 'electron'
|
|
import { join } from 'path'
|
|
import { PhpManager } from './services/PhpManager'
|
|
import { MysqlManager } from './services/MysqlManager'
|
|
import { NginxManager } from './services/NginxManager'
|
|
import { RedisManager } from './services/RedisManager'
|
|
import { NodeManager } from './services/NodeManager'
|
|
import { ServiceManager } from './services/ServiceManager'
|
|
import { HostsManager } from './services/HostsManager'
|
|
import { ConfigStore } from './services/ConfigStore'
|
|
|
|
let mainWindow: BrowserWindow | null = null
|
|
let tray: Tray | null = null
|
|
let isQuitting = false
|
|
|
|
// 发送下载进度到渲染进程
|
|
export function sendDownloadProgress(type: string, progress: number, downloaded: number, total: number) {
|
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
mainWindow.webContents.send('download-progress', { type, progress, downloaded, total })
|
|
}
|
|
}
|
|
|
|
// 初始化各服务管理器
|
|
const configStore = new ConfigStore()
|
|
const phpManager = new PhpManager(configStore)
|
|
const mysqlManager = new MysqlManager(configStore)
|
|
const nginxManager = new NginxManager(configStore)
|
|
const redisManager = new RedisManager(configStore)
|
|
const nodeManager = new NodeManager(configStore)
|
|
const serviceManager = new ServiceManager(configStore)
|
|
const hostsManager = new HostsManager()
|
|
|
|
function createWindow() {
|
|
mainWindow = new BrowserWindow({
|
|
width: 1400,
|
|
height: 900,
|
|
minWidth: 1200,
|
|
minHeight: 700,
|
|
webPreferences: {
|
|
preload: join(__dirname, 'preload.js'),
|
|
contextIsolation: true,
|
|
nodeIntegration: false
|
|
},
|
|
titleBarStyle: 'hidden',
|
|
titleBarOverlay: {
|
|
color: '#1a1a2e',
|
|
symbolColor: '#ffffff',
|
|
height: 40
|
|
},
|
|
frame: false,
|
|
icon: join(__dirname, '../public/icon.ico'),
|
|
show: false // 先不显示,等 ready-to-show
|
|
})
|
|
|
|
// 开发环境加载 Vite 开发服务器
|
|
const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL
|
|
if (VITE_DEV_SERVER_URL) {
|
|
mainWindow.loadURL(VITE_DEV_SERVER_URL)
|
|
mainWindow.webContents.openDevTools()
|
|
} else {
|
|
mainWindow.loadFile(join(__dirname, '../dist/index.html'))
|
|
}
|
|
|
|
// 窗口准备好后显示
|
|
mainWindow.once('ready-to-show', () => {
|
|
// 检查是否开机自启且静默启动
|
|
const startMinimized = configStore.get('startMinimized')
|
|
if (!startMinimized) {
|
|
mainWindow?.show()
|
|
}
|
|
})
|
|
|
|
// 关闭按钮改为最小化到托盘
|
|
mainWindow.on('close', (event) => {
|
|
if (!isQuitting) {
|
|
event.preventDefault()
|
|
mainWindow?.hide()
|
|
}
|
|
})
|
|
|
|
mainWindow.on('closed', () => {
|
|
mainWindow = null
|
|
})
|
|
}
|
|
|
|
// 创建系统托盘
|
|
function createTray() {
|
|
// 创建托盘图标
|
|
const iconPath = join(__dirname, '../public/favicon.svg')
|
|
let trayIcon
|
|
|
|
try {
|
|
trayIcon = nativeImage.createFromPath(iconPath)
|
|
if (trayIcon.isEmpty()) {
|
|
// 如果 SVG 无法加载,创建一个简单的图标
|
|
trayIcon = nativeImage.createEmpty()
|
|
}
|
|
} catch (e) {
|
|
trayIcon = nativeImage.createEmpty()
|
|
}
|
|
|
|
tray = new Tray(trayIcon)
|
|
tray.setToolTip('PHPer 开发环境管理器')
|
|
|
|
// 创建托盘菜单
|
|
const contextMenu = Menu.buildFromTemplate([
|
|
{
|
|
label: '显示主窗口',
|
|
click: () => {
|
|
if (mainWindow) {
|
|
mainWindow.show()
|
|
mainWindow.focus()
|
|
} else {
|
|
createWindow()
|
|
}
|
|
}
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
label: '启动全部服务',
|
|
click: async () => {
|
|
const result = await serviceManager.startAll()
|
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
mainWindow.webContents.send('service-status-changed')
|
|
}
|
|
}
|
|
},
|
|
{
|
|
label: '停止全部服务',
|
|
click: async () => {
|
|
const result = await serviceManager.stopAll()
|
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
mainWindow.webContents.send('service-status-changed')
|
|
}
|
|
}
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
label: '退出',
|
|
click: () => {
|
|
isQuitting = true
|
|
app.quit()
|
|
}
|
|
}
|
|
])
|
|
|
|
tray.setContextMenu(contextMenu)
|
|
|
|
// 双击托盘图标显示窗口
|
|
tray.on('double-click', () => {
|
|
if (mainWindow) {
|
|
mainWindow.show()
|
|
mainWindow.focus()
|
|
} else {
|
|
createWindow()
|
|
}
|
|
})
|
|
}
|
|
|
|
app.whenReady().then(async () => {
|
|
createTray()
|
|
createWindow()
|
|
|
|
// 检查是否启用开机自启且自动启动服务
|
|
const autoStartServices = configStore.get('autoStartServicesOnBoot')
|
|
if (autoStartServices) {
|
|
await serviceManager.startAll()
|
|
}
|
|
|
|
app.on('activate', () => {
|
|
if (BrowserWindow.getAllWindows().length === 0) {
|
|
createWindow()
|
|
}
|
|
})
|
|
})
|
|
|
|
// 不要在所有窗口关闭时退出,保持托盘运行
|
|
app.on('window-all-closed', () => {
|
|
// 什么都不做,保持后台运行
|
|
})
|
|
|
|
// 真正退出前清理
|
|
app.on('before-quit', () => {
|
|
isQuitting = true
|
|
})
|
|
|
|
// ==================== IPC 处理程序 ====================
|
|
|
|
// 窗口控制
|
|
ipcMain.handle('window:minimize', () => mainWindow?.minimize())
|
|
ipcMain.handle('window:maximize', () => {
|
|
if (mainWindow?.isMaximized()) {
|
|
mainWindow.unmaximize()
|
|
} else {
|
|
mainWindow?.maximize()
|
|
}
|
|
})
|
|
ipcMain.handle('window:close', () => mainWindow?.close())
|
|
|
|
// 打开外部链接
|
|
ipcMain.handle('shell:openExternal', (_, url: string) => shell.openExternal(url))
|
|
ipcMain.handle('shell:openPath', (_, path: string) => shell.openPath(path))
|
|
|
|
// 选择文件夹对话框
|
|
ipcMain.handle('dialog:selectDirectory', async () => {
|
|
const { dialog } = await import('electron')
|
|
const result = await dialog.showOpenDialog(mainWindow!, {
|
|
properties: ['openDirectory'],
|
|
title: '选择目录'
|
|
})
|
|
return result.canceled ? null : result.filePaths[0]
|
|
})
|
|
|
|
// ==================== PHP 管理 ====================
|
|
ipcMain.handle('php:getVersions', () => phpManager.getInstalledVersions())
|
|
ipcMain.handle('php:getAvailableVersions', () => phpManager.getAvailableVersions())
|
|
ipcMain.handle('php:install', (_, version: string) => phpManager.install(version))
|
|
ipcMain.handle('php:uninstall', (_, version: string) => phpManager.uninstall(version))
|
|
ipcMain.handle('php:setActive', (_, version: string) => phpManager.setActive(version))
|
|
ipcMain.handle('php:getExtensions', (_, version: string) => phpManager.getExtensions(version))
|
|
ipcMain.handle('php:openExtensionDir', (_, version: string) => phpManager.openExtensionDir(version))
|
|
ipcMain.handle('php:getAvailableExtensions', (_, version: string, searchKeyword?: string) => phpManager.getAvailableExtensions(version, searchKeyword))
|
|
ipcMain.handle('php:enableExtension', (_, version: string, ext: string) => phpManager.enableExtension(version, ext))
|
|
ipcMain.handle('php:disableExtension', (_, version: string, ext: string) => phpManager.disableExtension(version, ext))
|
|
ipcMain.handle('php:installExtension', (_, version: string, ext: string, downloadUrl?: string, packageName?: string) => phpManager.installExtension(version, ext, downloadUrl, packageName))
|
|
ipcMain.handle('php:getConfig', (_, version: string) => phpManager.getConfig(version))
|
|
ipcMain.handle('php:saveConfig', (_, version: string, config: string) => phpManager.saveConfig(version, config))
|
|
|
|
// ==================== MySQL 管理 ====================
|
|
ipcMain.handle('mysql:getVersions', () => mysqlManager.getInstalledVersions())
|
|
ipcMain.handle('mysql:getAvailableVersions', () => mysqlManager.getAvailableVersions())
|
|
ipcMain.handle('mysql:install', (_, version: string) => mysqlManager.install(version))
|
|
ipcMain.handle('mysql:uninstall', (_, version: string) => mysqlManager.uninstall(version))
|
|
ipcMain.handle('mysql:start', (_, version: string) => mysqlManager.start(version))
|
|
ipcMain.handle('mysql:stop', (_, version: string) => mysqlManager.stop(version))
|
|
ipcMain.handle('mysql:restart', (_, version: string) => mysqlManager.restart(version))
|
|
ipcMain.handle('mysql:getStatus', (_, version: string) => mysqlManager.getStatus(version))
|
|
ipcMain.handle('mysql:changePassword', (_, version: string, newPassword: string, currentPassword?: string) => mysqlManager.changeRootPassword(version, newPassword, currentPassword))
|
|
ipcMain.handle('mysql:getConfig', (_, version: string) => mysqlManager.getConfig(version))
|
|
ipcMain.handle('mysql:saveConfig', (_, version: string, config: string) => mysqlManager.saveConfig(version, config))
|
|
ipcMain.handle('mysql:reinitialize', (_, version: string) => mysqlManager.reinitialize(version))
|
|
|
|
// ==================== Nginx 管理 ====================
|
|
ipcMain.handle('nginx:getVersions', () => nginxManager.getInstalledVersions())
|
|
ipcMain.handle('nginx:getAvailableVersions', () => nginxManager.getAvailableVersions())
|
|
ipcMain.handle('nginx:install', (_, version: string) => nginxManager.install(version))
|
|
ipcMain.handle('nginx:uninstall', (_, version: string) => nginxManager.uninstall(version))
|
|
ipcMain.handle('nginx:start', () => nginxManager.start())
|
|
ipcMain.handle('nginx:stop', () => nginxManager.stop())
|
|
ipcMain.handle('nginx:restart', () => nginxManager.restart())
|
|
ipcMain.handle('nginx:reload', () => nginxManager.reload())
|
|
ipcMain.handle('nginx:getStatus', () => nginxManager.getStatus())
|
|
ipcMain.handle('nginx:getConfig', () => nginxManager.getConfig())
|
|
ipcMain.handle('nginx:saveConfig', (_, config: string) => nginxManager.saveConfig(config))
|
|
ipcMain.handle('nginx:getSites', () => nginxManager.getSites())
|
|
ipcMain.handle('nginx:addSite', (_, site: any) => nginxManager.addSite(site))
|
|
ipcMain.handle('nginx:removeSite', (_, name: string) => nginxManager.removeSite(name))
|
|
ipcMain.handle('nginx:updateSite', (_, originalName: string, site: any) => nginxManager.updateSite(originalName, site))
|
|
ipcMain.handle('nginx:enableSite', (_, name: string) => nginxManager.enableSite(name))
|
|
ipcMain.handle('nginx:disableSite', (_, name: string) => nginxManager.disableSite(name))
|
|
ipcMain.handle('nginx:generateLaravelConfig', (_, site: any) => nginxManager.generateLaravelConfig(site))
|
|
ipcMain.handle('nginx:requestSSL', (_, domain: string, email: string) => nginxManager.requestSSLCertificate(domain, email))
|
|
|
|
// ==================== Redis 管理 ====================
|
|
ipcMain.handle('redis:getVersions', () => redisManager.getInstalledVersions())
|
|
ipcMain.handle('redis:getAvailableVersions', () => redisManager.getAvailableVersions())
|
|
ipcMain.handle('redis:install', (_, version: string) => redisManager.install(version))
|
|
ipcMain.handle('redis:uninstall', (_, version: string) => redisManager.uninstall(version))
|
|
ipcMain.handle('redis:start', () => redisManager.start())
|
|
ipcMain.handle('redis:stop', () => redisManager.stop())
|
|
ipcMain.handle('redis:restart', () => redisManager.restart())
|
|
ipcMain.handle('redis:getStatus', () => redisManager.getStatus())
|
|
ipcMain.handle('redis:getConfig', () => redisManager.getConfig())
|
|
ipcMain.handle('redis:saveConfig', (_, config: string) => redisManager.saveConfig(config))
|
|
|
|
// ==================== Node.js 管理 ====================
|
|
ipcMain.handle('node:getVersions', () => nodeManager.getInstalledVersions())
|
|
ipcMain.handle('node:getAvailableVersions', () => nodeManager.getAvailableVersions())
|
|
ipcMain.handle('node:install', (_, version: string, downloadUrl: string) => nodeManager.install(version, downloadUrl))
|
|
ipcMain.handle('node:uninstall', (_, version: string) => nodeManager.uninstall(version))
|
|
ipcMain.handle('node:setActive', (_, version: string) => nodeManager.setActive(version))
|
|
ipcMain.handle('node:getInfo', (_, version: string) => nodeManager.getNodeInfo(version))
|
|
|
|
// ==================== 服务管理 ====================
|
|
ipcMain.handle('service:getAll', () => serviceManager.getAllServices())
|
|
ipcMain.handle('service:setAutoStart', (_, service: string, enabled: boolean) => serviceManager.setAutoStart(service, enabled))
|
|
ipcMain.handle('service:getAutoStart', (_, service: string) => serviceManager.getAutoStart(service))
|
|
ipcMain.handle('service:startAll', () => serviceManager.startAll())
|
|
ipcMain.handle('service:stopAll', () => serviceManager.stopAll())
|
|
|
|
// ==================== Hosts 管理 ====================
|
|
ipcMain.handle('hosts:get', () => hostsManager.getHosts())
|
|
ipcMain.handle('hosts:add', (_, domain: string, ip: string) => hostsManager.addHost(domain, ip))
|
|
ipcMain.handle('hosts:remove', (_, domain: string) => hostsManager.removeHost(domain))
|
|
|
|
// ==================== 配置管理 ====================
|
|
ipcMain.handle('config:get', (_, key: string) => configStore.get(key))
|
|
ipcMain.handle('config:set', (_, key: string, value: any) => configStore.set(key, value))
|
|
ipcMain.handle('config:getBasePath', () => configStore.getBasePath())
|
|
ipcMain.handle('config:setBasePath', (_, path: string) => configStore.setBasePath(path))
|
|
|
|
// ==================== 应用设置 ====================
|
|
// 设置开机自启
|
|
ipcMain.handle('app:setAutoLaunch', async (_, enabled: boolean) => {
|
|
app.setLoginItemSettings({
|
|
openAtLogin: enabled,
|
|
openAsHidden: true, // 静默启动
|
|
args: ['--hidden']
|
|
})
|
|
configStore.set('autoLaunch', enabled)
|
|
return { success: true, message: enabled ? '已启用开机自启' : '已禁用开机自启' }
|
|
})
|
|
|
|
// 获取开机自启状态
|
|
ipcMain.handle('app:getAutoLaunch', () => {
|
|
return app.getLoginItemSettings().openAtLogin
|
|
})
|
|
|
|
// 设置启动时最小化到托盘
|
|
ipcMain.handle('app:setStartMinimized', (_, enabled: boolean) => {
|
|
configStore.set('startMinimized', enabled)
|
|
return { success: true }
|
|
})
|
|
|
|
// 获取启动时最小化状态
|
|
ipcMain.handle('app:getStartMinimized', () => {
|
|
return configStore.get('startMinimized') || false
|
|
})
|
|
|
|
// 设置开机自启时自动启动服务
|
|
ipcMain.handle('app:setAutoStartServices', (_, enabled: boolean) => {
|
|
configStore.set('autoStartServicesOnBoot', enabled)
|
|
return { success: true }
|
|
})
|
|
|
|
// 获取自动启动服务状态
|
|
ipcMain.handle('app:getAutoStartServices', () => {
|
|
return configStore.get('autoStartServicesOnBoot') || false
|
|
})
|
|
|
|
// 真正退出应用
|
|
ipcMain.handle('app:quit', () => {
|
|
isQuitting = true
|
|
app.quit()
|
|
})
|
|
|