迁移: - 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)
252 lines
5.9 KiB
TypeScript
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()
|
|
})
|