Skip to content

Commit

Permalink
edge case covering
Browse files Browse the repository at this point in the history
  • Loading branch information
seriouscatsoserious committed Feb 1, 2025
1 parent 7c005ff commit 8f776e4
Show file tree
Hide file tree
Showing 9 changed files with 343 additions and 82 deletions.
66 changes: 63 additions & 3 deletions public/electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const ApiManager = require("./modules/apiManager");
const DirectoryManager = require("./modules/directoryManager");
const UpdateManager = require("./modules/updateManager");


const configPath = path.join(__dirname, "chain_config.json");
let config;
let mainWindow = null;
Expand Down Expand Up @@ -78,6 +77,20 @@ function createWindow() {
mainWindow.on('close', (event) => {
if (!isShuttingDown) {
event.preventDefault();
// Check for active downloads before initiating shutdown
const activeDownloads = downloadManager?.getDownloads() || [];
if (activeDownloads.length > 0) {
// Serialize download data to ensure it can be sent through IPC
const serializedDownloads = activeDownloads.map(download => ({
chainId: download.chainId,
progress: download.progress,
status: download.status,
downloadedLength: download.downloadedLength,
totalLength: download.totalLength
}));
mainWindow.webContents.send("downloads-in-progress", serializedDownloads);
return;
}
performGracefulShutdown();
}
});
Expand Down Expand Up @@ -525,6 +538,25 @@ function setupIPCHandlers() {
ipcMain.handle('force-kill', () => {
forceKillAllProcesses();
});

// Add handler for force quit with active downloads
ipcMain.handle('force-quit-with-downloads', async () => {
try {
// Cancel all downloads first
if (downloadManager) {
const activeDownloads = downloadManager.getDownloads();
for (const download of activeDownloads) {
await downloadManager.pauseDownload(download.chainId);
}
}
// Then force quit
isShuttingDown = true;
app.exit(0);
} catch (error) {
console.error('Error during force quit:', error);
app.exit(1);
}
});
}

async function initialize() {
Expand All @@ -547,9 +579,9 @@ async function initialize() {
createWindow();

// Then initialize managers that need mainWindow
chainManager = new ChainManager(mainWindow, config);
updateManager = new UpdateManager(config, chainManager);
downloadManager = new DownloadManager(mainWindow, config);
chainManager = new ChainManager(mainWindow, config, downloadManager);
updateManager = new UpdateManager(config, chainManager);

// Finally setup IPC handlers after everything is initialized
setupIPCHandlers();
Expand Down Expand Up @@ -582,6 +614,20 @@ async function performGracefulShutdown() {
}, SHUTDOWN_TIMEOUT);

try {
// First handle any active downloads
if (downloadManager) {
const activeDownloads = downloadManager.getDownloads();
for (const download of activeDownloads) {
try {
console.log(`Canceling download for ${download.chainId}`);
await downloadManager.pauseDownload(download.chainId);
} catch (error) {
console.error(`Error canceling download for ${download.chainId}:`, error);
}
}
}

// Then stop running chains
if (chainManager) {
const runningChains = Object.keys(chainManager.runningProcesses);
await Promise.all(runningChains.map(chainId =>
Expand All @@ -600,6 +646,20 @@ async function performGracefulShutdown() {
}

function forceKillAllProcesses() {
// First cancel all downloads
if (downloadManager) {
const activeDownloads = downloadManager.getDownloads();
for (const download of activeDownloads) {
try {
console.log(`Force canceling download for ${download.chainId}`);
downloadManager.pauseDownload(download.chainId);
} catch (error) {
console.error(`Error force canceling download for ${download.chainId}:`, error);
}
}
}

// Then kill chain processes
if (chainManager) {
Object.entries(chainManager.runningProcesses).forEach(([chainId, process]) => {
try {
Expand Down
74 changes: 71 additions & 3 deletions public/modules/chainManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ const BitWindowClient = require("./bitWindowClient");
const EnforcerClient = require("./enforcerClient");

class ChainManager {
constructor(mainWindow, config) {
constructor(mainWindow, config, downloadManager) {
this.mainWindow = mainWindow;
this.config = config;
this.downloadManager = downloadManager;
this.runningProcesses = {};
this.chainStatuses = new Map(); // Tracks detailed chain statuses
this.bitcoinMonitor = new BitcoinMonitor(mainWindow);
Expand Down Expand Up @@ -546,12 +547,25 @@ class ChainManager {
const baseDir = chain.directories.base[platform];
if (!baseDir) throw new Error(`No base directory configured for platform ${platform}`);

this.chainStatuses.set(chainId, 'resetting');
// Set status to stopped immediately to avoid yellow flash
this.chainStatuses.set(chainId, 'stopped');
this.mainWindow.webContents.send("chain-status-update", {
chainId,
status: "resetting",
status: "stopped",
});

// Aggressively clean up any active downloads first
if (this.downloadManager) {
const platform = process.platform;
const extractDir = chain.extract_dir?.[platform];
if (extractDir) {
const downloadsDir = app.getPath("downloads");
const extractPath = path.join(downloadsDir, extractDir);
await this.downloadManager.cleanupChainDownloads(chainId, extractPath);
}
}

// Stop the chain if running
if (this.runningProcesses[chainId]) {
await this.stopChain(chainId);
await new Promise(resolve => setTimeout(resolve, 500));
Expand All @@ -575,6 +589,7 @@ class ChainManager {

await new Promise(resolve => setTimeout(resolve, 100));

// Now set to not_downloaded for final state
this.chainStatuses.set(chainId, 'not_downloaded');
this.mainWindow.webContents.send("chain-status-update", {
chainId,
Expand Down Expand Up @@ -668,6 +683,59 @@ class ChainManager {

return -1;
}

async resetAllChains() {
try {
// First clean up all downloads
if (this.downloadManager) {
await this.downloadManager.cleanupAllDownloads();
}

// Stop all running chains
const runningChains = Object.keys(this.runningProcesses);
for (const chainId of runningChains) {
await this.stopChain(chainId);
}
await new Promise(resolve => setTimeout(resolve, 500));

// Reset each chain's data
for (const chain of this.config.chains) {
const chainId = chain.id;
const platform = process.platform;

// Remove data directory
const baseDir = chain.directories.base[platform];
if (baseDir) {
const homeDir = app.getPath("home");
const fullPath = path.join(homeDir, baseDir);
await fs.remove(fullPath);
await fs.ensureDir(fullPath);
console.log(`Reset chain ${chainId}: removed and recreated data directory ${fullPath}`);
}

// Remove binaries directory
const extractDir = chain.extract_dir?.[platform];
if (extractDir) {
const downloadsDir = app.getPath("downloads");
const binariesPath = path.join(downloadsDir, extractDir);
await fs.remove(binariesPath);
console.log(`Reset chain ${chainId}: removed binaries directory ${binariesPath}`);
}

// Update chain status
this.chainStatuses.set(chainId, 'not_downloaded');
this.mainWindow.webContents.send("chain-status-update", {
chainId,
status: "not_downloaded",
});
}

return { success: true };
} catch (error) {
console.error("Failed to reset all chains:", error);
return { success: false, error: error.message };
}
}
}

module.exports = ChainManager;
Loading

0 comments on commit 8f776e4

Please sign in to comment.