Files
opsxshell/electron/main.ts
T
OpsXShell Dev 53274e2291 v1.1.0: better-sqlite3→sql.js迁移 + 6项UI/代码修复
迁移:
- better-sqlite3(原生C++) → sql.js(WASM纯JS), 兼容Node 24
- database-service.ts: PreparedStatement包装器保持API兼容
- main.ts: initialize()改为async/await
- 移除@electron/rebuild和@types/better-sqlite3依赖
- 新增.npmrc阿里云镜像加速

UI/代码修复(任务1-6):
1. TerminalView: 清理111行调试日志(645→534行)
2. 设计系统: cyan/slate→accent-blue/dark-*颜色统一(FileRow/TransferItem/Breadcrumb/ContextMenu)
3. StatusBar: 重写接入实时连接状态,三区布局
4. SessionDialog: 私钥浏览按钮添加onClick处理
5. WelcomeView: 真实session数据替换硬编码假数据
6. SFTPView: emoji→Lucide图标(FolderOpen/Upload/Download/Trash2)

构建验证: tsc零错误 + vite build成功(Node 24.14.0)
2026-04-18 15:40:19 +08:00

252 lines
5.9 KiB
TypeScript

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