/** * File IPC Handlers * 处理本地文件操作和远程文件读写 */ import { ipcMain, IpcMainInvokeEvent, app } from 'electron' import fs from 'fs' import path from 'path' // IPC channel names export const FILE_CHANNELS = { // Remote file operations (for zmodem) READ: 'file:read', WRITE: 'file:write', // Local file system operations LOCAL_GET_HOME: 'local:getHome', LOCAL_READ_DIR: 'local:readDir', LOCAL_GET_STATS: 'local:getStats', LOCAL_DELETE: 'local:delete', LOCAL_RENAME: 'local:rename', LOCAL_MKDIR: 'local:mkdir', } as const export function registerFileIPCHandlers(): void { // Read file (for rz) ipcMain.handle(FILE_CHANNELS.READ, async (_event: IpcMainInvokeEvent, filePath: string) => { try { const data = fs.readFileSync(filePath) return { success: true, data: data.toString('base64') } } catch (error: any) { return { success: false, error: error.message } } }) // Write file (for sz) ipcMain.handle(FILE_CHANNELS.WRITE, async (_event: IpcMainInvokeEvent, filePath: string, data: string) => { try { const buffer = Buffer.from(data, 'base64') fs.writeFileSync(filePath, buffer) return { success: true } } catch (error: any) { return { success: false, error: error.message } } }) // Get user home directory ipcMain.handle(FILE_CHANNELS.LOCAL_GET_HOME, async () => { try { const homePath = app.getPath('home') return { success: true, path: homePath } } catch (error: any) { return { success: false, error: error.message } } }) // Read directory contents ipcMain.handle(FILE_CHANNELS.LOCAL_READ_DIR, async (_event: IpcMainInvokeEvent, dirPath: string) => { try { const entries = fs.readdirSync(dirPath, { withFileTypes: true }) const files = entries.map((entry) => { const fullPath = path.join(dirPath, entry.name) let stats: fs.Stats | null = null try { stats = fs.lstatSync(fullPath) } catch { // Skip files we can't stat } return { name: entry.name, path: fullPath, type: entry.isDirectory() ? 'directory' : entry.isSymbolicLink() ? 'symlink' : 'file', size: stats?.size || 0, modified: stats?.mtime ? Math.floor(stats.mtime.getTime() / 1000) : 0, permissions: formatPermissions(stats?.mode || 0), } }) // Sort: directories first, then by name files.sort((a, b) => { if (a.type !== 'file' && b.type === 'file') return -1 if (a.type === 'file' && b.type !== 'file') return 1 return a.name.localeCompare(b.name) }) return { success: true, files } } catch (error: any) { return { success: false, error: error.message } } }) // Get file stats ipcMain.handle(FILE_CHANNELS.LOCAL_GET_STATS, async (_event: IpcMainInvokeEvent, filePath: string) => { try { const stats = fs.lstatSync(filePath) return { success: true, stats: { name: path.basename(filePath), path: filePath, type: stats.isDirectory() ? 'directory' : stats.isSymbolicLink() ? 'symlink' : 'file', size: stats.size, modified: Math.floor(stats.mtime.getTime() / 1000), permissions: formatPermissions(stats.mode), }, } } catch (error: any) { return { success: false, error: error.message } } }) // Delete file or directory ipcMain.handle(FILE_CHANNELS.LOCAL_DELETE, async (_event: IpcMainInvokeEvent, filePath: string) => { try { const stats = fs.lstatSync(filePath) if (stats.isDirectory()) { fs.rmSync(filePath, { recursive: true, force: true }) } else { fs.unlinkSync(filePath) } return { success: true } } catch (error: any) { return { success: false, error: error.message } } }) // Rename file or directory ipcMain.handle(FILE_CHANNELS.LOCAL_RENAME, async (_event: IpcMainInvokeEvent, oldPath: string, newPath: string) => { try { fs.renameSync(oldPath, newPath) return { success: true } } catch (error: any) { return { success: false, error: error.message } } }) // Create directory ipcMain.handle(FILE_CHANNELS.LOCAL_MKDIR, async (_event: IpcMainInvokeEvent, dirPath: string) => { try { fs.mkdirSync(dirPath, { recursive: true }) return { success: true } } catch (error: any) { return { success: false, error: error.message } } }) } // Helper function to format Unix permissions function formatPermissions(mode: number): string { const perms: string[] = [] const types = ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx'] // Owner perms.push(types[(mode >> 6) & 7]) // Group perms.push(types[(mode >> 3) & 7]) // Other perms.push(types[mode & 7]) return perms.join('') }