/** * OpsXShell - Main Process Entry * 跨平台 SSH/SFTP 终端工具 · 简洁高效 · 安全可靠 */ import { app, BrowserWindow, Menu, shell, dialog } from 'electron' import path from 'path' import { createLogger } from './utils/logger' const logger = createLogger('Main') // Services import { SSHService } from './services/ssh-service' import { DatabaseService } from './services/database-service' import { keyService } from './services/key-service' import { ZmodemService } from './services/zmodem-service' // IPC Handlers import { registerAllIPCHandlers } from './ipc' // ============== Global State ============== let mainWindow: BrowserWindow | null = null // Services (initialized after app ready) let sshService: SSHService let zmodemService: ZmodemService let dbService: DatabaseService | null = null // ============== Window Management ============== function createWindow(): void { mainWindow = new BrowserWindow({ width: 1400, height: 900, minWidth: 1000, minHeight: 600, titleBarStyle: 'hiddenInset', webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false, sandbox: false, }, show: false, backgroundColor: '#0f172a', }) // Load the app if (process.env.VITE_DEV_SERVER_URL) { mainWindow.loadURL(process.env.VITE_DEV_SERVER_URL) // DevTools 通过快捷键 Cmd+Option+I 打开,不自动调用 } else { mainWindow.loadFile(path.join(__dirname, '../dist/index.html')) } // Show window when ready mainWindow.once('ready-to-show', () => { mainWindow?.show() }) // Handle window closed mainWindow.on('closed', () => { mainWindow = null }) // Open external links in browser mainWindow.webContents.setWindowOpenHandler(({ url }) => { shell.openExternal(url) return { action: 'deny' } }) // Set up application menu createApplicationMenu() } // ============== Application Menu ============== function createApplicationMenu(): void { const template: Electron.MenuItemConstructorOptions[] = [ { label: 'OpsXShell', submenu: [ { label: '关于 OpsXShell', click: () => { dialog.showMessageBox(mainWindow!, { type: 'info', title: '关于 OpsXShell', message: 'OpsXShell v1.0.0', detail: '跨平台 SSH/SFTP 终端工具 · 简洁高效 · 安全可靠\n\nMIT License', }) }, }, { type: 'separator' }, { label: '退出', accelerator: process.platform === 'darwin' ? 'Cmd+Q' : 'Ctrl+Q', click: () => { app.quit() }, }, ], }, { label: '会话', submenu: [ { label: '新建会话', accelerator: 'CmdOrCtrl+N', click: () => { mainWindow?.webContents.send('menu-new-session') }, }, { label: '快速连接', accelerator: 'CmdOrCtrl+K', click: () => { mainWindow?.webContents.send('menu-quick-connect') }, }, { type: 'separator' }, { label: '导入会话', click: () => { mainWindow?.webContents.send('menu-import-sessions') }, }, { label: '导出会话', click: () => { mainWindow?.webContents.send('menu-export-sessions') }, }, ], }, { label: '编辑', submenu: [ { role: 'undo' }, { role: 'redo' }, { type: 'separator' }, { role: 'cut' }, { role: 'copy' }, { role: 'paste' }, { role: 'selectAll' }, ], }, { label: '视图', submenu: [ { label: '重新加载', accelerator: 'CmdOrCtrl+R', click: () => { mainWindow?.webContents.reload() }, }, { label: '切换开发者工具', accelerator: process.platform === 'darwin' ? 'Alt+Cmd+I' : 'F12', click: () => { mainWindow?.webContents.toggleDevTools() }, }, { type: 'separator' }, { role: 'resetZoom' }, { role: 'zoomIn' }, { role: 'zoomOut' }, { type: 'separator' }, { role: 'togglefullscreen' }, ], }, { label: '窗口', submenu: [ { role: 'minimize' }, { role: 'close' }, ], }, { label: '帮助', submenu: [ { label: '文档', click: () => { shell.openExternal('https://github.com/opsxshell/docs') }, }, { label: '报告问题', click: () => { shell.openExternal('https://github.com/opsxshell/issues') }, }, ], }, ] const menu = Menu.buildFromTemplate(template) Menu.setApplicationMenu(menu) } // ============== App Lifecycle ============== app.whenReady().then(async () => { // Initialize encryption key first keyService.getMasterKey() logger.info('[App] Encryption key initialized') // Initialize services sshService = new SSHService() zmodemService = new ZmodemService() dbService = new DatabaseService() await dbService.initialize() // Register IPC handlers registerAllIPCHandlers({ sshService, databaseService: dbService, zmodemService, mainWindowGetter: () => mainWindow, }) logger.info('[App] IPC handlers registered') // Create main window createWindow() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow() } }) }) app.on('window-all-closed', () => { // Close all SSH connections sshService?.disconnectAll() // Close database dbService?.close() if (process.platform !== 'darwin') { app.quit() } }) app.on('before-quit', () => { sshService?.disconnectAll() dbService?.close() })