194 lines
No EOL
5.8 KiB
JavaScript
194 lines
No EOL
5.8 KiB
JavaScript
/**
|
|
* Extension Manager Module
|
|
*
|
|
* Handles Chrome extension management for Sneedium browser
|
|
* - Loading extensions from disk
|
|
* - Installing extensions from CRX files
|
|
* - Removing extensions
|
|
* - Creating extensions management UI
|
|
*/
|
|
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
|
|
const { installCrxExtension } = require('./crx-extractor');
|
|
|
|
// Define extension directory path
|
|
const homePath = app.getPath('home');
|
|
const extensionsDir = path.join(homePath, 'SneedExtensions');
|
|
|
|
// Ensure extensions directory exists
|
|
if (!fs.existsSync(extensionsDir)) {
|
|
fs.mkdirSync(extensionsDir, { recursive: true });
|
|
}
|
|
|
|
/**
|
|
* Initialize extension manager and set up IPC handlers
|
|
* @returns {Object} Extension manager functions
|
|
*/
|
|
function initExtensionManager() {
|
|
// Set up IPC handlers for extension management
|
|
setupIpcHandlers();
|
|
|
|
return {
|
|
getExtensionsDir: () => extensionsDir,
|
|
createExtensionsWindow,
|
|
loadExtensions
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Set up IPC handlers for extension management
|
|
*/
|
|
function setupIpcHandlers() {
|
|
// Get list of installed extensions
|
|
ipcMain.handle('get-extensions-list', getExtensionsList);
|
|
|
|
// Install extension from CRX file
|
|
ipcMain.handle('install-extension', handleInstallExtension);
|
|
|
|
// Remove installed extension
|
|
ipcMain.handle('remove-extension', handleRemoveExtension);
|
|
|
|
// Open extension management window
|
|
ipcMain.on('open-extensions-page', () => createExtensionsWindow());
|
|
}
|
|
|
|
/**
|
|
* Get list of installed extensions with their metadata
|
|
* @returns {Array} List of extension objects with metadata
|
|
*/
|
|
async function getExtensionsList() {
|
|
try {
|
|
const extensionFolders = fs.readdirSync(extensionsDir, { withFileTypes: true })
|
|
.filter(dirent => dirent.isDirectory())
|
|
.map(dirent => {
|
|
const extPath = path.join(extensionsDir, dirent.name);
|
|
let manifest = {};
|
|
try {
|
|
const manifestPath = path.join(extPath, 'manifest.json');
|
|
if (fs.existsSync(manifestPath)) {
|
|
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
}
|
|
} catch (e) {
|
|
console.error(`Error reading manifest for extension ${dirent.name}:`, e);
|
|
}
|
|
return {
|
|
id: dirent.name,
|
|
name: manifest.name || dirent.name,
|
|
version: manifest.version || 'unknown',
|
|
description: manifest.description || '',
|
|
path: extPath
|
|
};
|
|
});
|
|
return extensionFolders;
|
|
} catch (error) {
|
|
console.error('Error getting extensions list:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle extension installation request
|
|
* @param {Object} event - IPC event
|
|
* @param {string} crxPath - Path to CRX file or null to prompt for file
|
|
* @returns {Object} Result of installation
|
|
*/
|
|
async function handleInstallExtension(event, crxPath) {
|
|
try {
|
|
// If no path provided, open dialog to select CRX file
|
|
if (!crxPath) {
|
|
const result = await dialog.showOpenDialog({
|
|
properties: ['openFile'],
|
|
filters: [{ name: 'Chrome Extension', extensions: ['crx'] }]
|
|
});
|
|
|
|
if (result.canceled || result.filePaths.length === 0) {
|
|
return { success: false, message: 'No file selected' };
|
|
}
|
|
|
|
crxPath = result.filePaths[0];
|
|
}
|
|
|
|
// Use the CRX extractor to install the extension
|
|
const installResult = await installCrxExtension(crxPath, extensionsDir);
|
|
|
|
return installResult;
|
|
} catch (error) {
|
|
console.error('Error installing extension:', error);
|
|
return { success: false, message: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle extension removal request
|
|
* @param {Object} event - IPC event
|
|
* @param {string} extensionId - ID of extension to remove
|
|
* @returns {Object} Result of removal
|
|
*/
|
|
async function handleRemoveExtension(event, extensionId) {
|
|
try {
|
|
const extensionPath = path.join(extensionsDir, extensionId);
|
|
if (fs.existsSync(extensionPath)) {
|
|
fs.rmSync(extensionPath, { recursive: true, force: true });
|
|
return { success: true };
|
|
} else {
|
|
return { success: false, message: 'Extension not found' };
|
|
}
|
|
} catch (error) {
|
|
console.error('Error removing extension:', error);
|
|
return { success: false, message: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create extension management window
|
|
*/
|
|
function createExtensionsWindow() {
|
|
const extensionsWindow = new BrowserWindow({
|
|
width: 800,
|
|
height: 600,
|
|
title: 'Extension Manager',
|
|
webPreferences: {
|
|
preload: path.join(__dirname, 'extensions-preload.js'),
|
|
nodeIntegration: false,
|
|
contextIsolation: true
|
|
}
|
|
});
|
|
|
|
extensionsWindow.loadFile('extensions.html');
|
|
extensionsWindow.setMenuBarVisibility(false);
|
|
}
|
|
|
|
/**
|
|
* Load all extensions from the extensions directory
|
|
* @param {Object} extensionsInstance - ElectronChromeExtensions instance
|
|
*/
|
|
function loadExtensions(extensionsInstance) {
|
|
if (!extensionsInstance) return;
|
|
|
|
try {
|
|
// Get list of extension directories
|
|
const extensionFolders = fs.readdirSync(extensionsDir, { withFileTypes: true })
|
|
.filter(dirent => dirent.isDirectory())
|
|
.map(dirent => path.join(extensionsDir, dirent.name));
|
|
|
|
// Load each extension
|
|
for (const extensionPath of extensionFolders) {
|
|
try {
|
|
if (fs.existsSync(path.join(extensionPath, 'manifest.json'))) {
|
|
const loadResult = extensionsInstance.loadExtension(extensionPath);
|
|
console.log(`Loaded extension from ${extensionPath}`, loadResult);
|
|
} else {
|
|
console.warn(`Missing manifest.json in extension directory: ${extensionPath}`);
|
|
}
|
|
} catch (extError) {
|
|
console.error(`Failed to load extension at ${extensionPath}:`, extError);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading extensions:', error);
|
|
}
|
|
}
|
|
|
|
module.exports = initExtensionManager;
|