phper/electron/main.ts

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()
})