feat: implement secure IPC-based file export for Electron
Replace sandboxed Capacitor filesystem with native IPC for reliable file exports: - Add IPC handler in main process for direct Downloads folder access - Expose secure electronAPI via contextBridge in preload script - Update ElectronPlatformService to use native IPC with web fallback - Add TypeScript definitions for electron APIs - Fix file export issues where files were trapped in virtual filesystem - Enable proper date-stamped backup filenames in Downloads folder - Follow Electron security best practices with process isolation Files now export directly to ~/Downloads with exact path feedback.
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import type { CapacitorElectronConfig } from '@capacitor-community/electron';
|
||||
import { getCapacitorElectronConfig, setupElectronDeepLinking } from '@capacitor-community/electron';
|
||||
import type { MenuItemConstructorOptions } from 'electron';
|
||||
import { app, MenuItem } from 'electron';
|
||||
import { app, MenuItem, ipcMain } from 'electron';
|
||||
import electronIsDev from 'electron-is-dev';
|
||||
import unhandled from 'electron-unhandled';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
import { promises as fs } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { ElectronCapacitorApp, setupContentSecurityPolicy, setupReloadWatcher } from './setup';
|
||||
|
||||
@@ -106,3 +108,38 @@ app.on('activate', async function () {
|
||||
});
|
||||
|
||||
// Place all ipc or other electron api calls and custom functionality under this line
|
||||
|
||||
/**
|
||||
* IPC Handler for exporting data to the user's Downloads folder.
|
||||
*
|
||||
* This provides a secure, native way to save files directly to the Downloads
|
||||
* directory using the main process's file system access.
|
||||
*
|
||||
* @param fileName - The name of the file to save (including extension)
|
||||
* @param data - The data to write to the file (string or buffer)
|
||||
* @returns Promise<{success: boolean, path?: string, error?: string}>
|
||||
*/
|
||||
ipcMain.handle('export-data-to-downloads', async (_event, fileName: string, data: string) => {
|
||||
try {
|
||||
// Get the user's Downloads directory path
|
||||
const downloadsDir = app.getPath('downloads');
|
||||
const filePath = join(downloadsDir, fileName);
|
||||
|
||||
// Write the file to the Downloads directory
|
||||
await fs.writeFile(filePath, data, 'utf-8');
|
||||
|
||||
console.log(`[Electron Main] File exported successfully: ${filePath}`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
path: filePath
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`[Electron Main] File export failed:`, error);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,34 @@
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
|
||||
require('./rt/electron-rt');
|
||||
//////////////////////////////
|
||||
// User Defined Preload scripts below
|
||||
console.log('User Preload!');
|
||||
|
||||
/**
|
||||
* Expose secure IPC APIs to the renderer process.
|
||||
*
|
||||
* This creates a bridge between the sandboxed renderer and the main process,
|
||||
* allowing secure file operations while maintaining Electron's security model.
|
||||
*/
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
/**
|
||||
* Export data to the user's Downloads folder.
|
||||
*
|
||||
* @param fileName - The name of the file to save (e.g., 'backup-2025-07-06.json')
|
||||
* @param data - The content to write to the file (string)
|
||||
* @returns Promise<{success: boolean, path?: string, error?: string}>
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const result = await window.electronAPI.exportData('my-backup.json', JSON.stringify(data));
|
||||
* if (result.success) {
|
||||
* console.log('File saved to:', result.path);
|
||||
* } else {
|
||||
* console.error('Export failed:', result.error);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
exportData: (fileName: string, data: string) =>
|
||||
ipcRenderer.invoke('export-data-to-downloads', fileName, data)
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user