From 6f02d4035bc78acc7e8bea618d3face77419f40b Mon Sep 17 00:00:00 2001 From: arionrefat Date: Sat, 22 Feb 2025 15:07:19 +0600 Subject: [PATCH 1/4] feat: enhance podman uninstallation process on Linux - Added comprehensive cleanup of podman containers, pods, and images - Implemented removal of podman configuration folders - Improved error handling during uninstallation - Restored electron app path import for home directory access --- src/main/podman/uninstall/uninstallOnLinux.ts | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/main/podman/uninstall/uninstallOnLinux.ts b/src/main/podman/uninstall/uninstallOnLinux.ts index b3eb0986d..6b5e82431 100644 --- a/src/main/podman/uninstall/uninstallOnLinux.ts +++ b/src/main/podman/uninstall/uninstallOnLinux.ts @@ -1,6 +1,6 @@ +import { app } from 'electron'; import { reportEvent } from '../../events'; import { execAwait } from '../../execHelper'; -// import { app } from 'electron'; import logger from '../../logger'; import { type PackageManager, @@ -19,7 +19,44 @@ const uninstallOnLinux = async (): Promise => { logger.info('Uninstalling podman...'); try { // Returns /Users/ (Ex. /Users/johns) - // const userHome = app.getPath('home'); + const userHome = app.getPath('home'); + + // First cleanup containers and pods + try { + // Stop and remove all containers + await execAwait('podman stop -a', { log: true }); + await execAwait('podman rm -af', { log: true }); + + // Remove all pods + await execAwait('podman pod rm -af', { log: true }); + + // Remove all images + await execAwait('podman rmi -af', { log: true }); + + logger.info('Successfully cleaned up all podman containers, pods and images'); + } catch (cleanupErr) { + logger.error('Error during container cleanup, continuing with uninstall:', cleanupErr); + } + + // Remove podman configuration folders + const foldersToDelete = [ + `${userHome}/.config/containers`, + `${userHome}/.local/share/containers`, + `${userHome}/.ssh/*podman*`, + '/usr/local/podman', + ]; + + try { + const rmCommand = `rm -rf ${foldersToDelete.join(' ')}`; + await execAwait(rmCommand, { + log: true, + sudo: true, + }); + logger.info('Successfully removed podman configuration folders'); + } catch (rmErr) { + logger.error('Error removing configuration folders:', rmErr); + // Continue with uninstall even if folder removal fails + } // 1. (applies to mac, also linux?) This can throw return an error if a file or folder doesn't exist. // This is ok, because it will still delete the other folders that do exist. @@ -42,7 +79,7 @@ const uninstallOnLinux = async (): Promise => { log: true, sudo: true, }); - logger.info(`${uninstallScript} stdout, stderr ${stdout} ${stderr}`); + logger.info(`${uninstallScript} stdout, stderr ${stdout} ${stderr}`); return true; } catch (err: any) { logger.error(err); From de860e79aa8f16b3f7cf6acd6e431cd04df82c0c Mon Sep 17 00:00:00 2001 From: Refatul Islam Date: Tue, 18 Mar 2025 23:13:35 +0600 Subject: [PATCH 2/4] refactor(types): improve TypeScript types in rate limiter - Replace any types with proper TypeScript utility types - Add ThisParameterType, Parameters, and ReturnType generics - Create QueueItem type for better type safety in FIFO queue --- src/main/util/rateLimiter.ts | 55 +++++++++++++++++++++++++ src/main/util/sentryWinstonTransport.ts | 12 +++++- 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 src/main/util/rateLimiter.ts diff --git a/src/main/util/rateLimiter.ts b/src/main/util/rateLimiter.ts new file mode 100644 index 000000000..cc1e104f0 --- /dev/null +++ b/src/main/util/rateLimiter.ts @@ -0,0 +1,55 @@ +/** + * Rate limiting utility function + * @param limitCount Maximum number of calls allowed within the interval + * @param limitInterval Time interval in milliseconds + * @param fn Function to be rate limited + * @returns Rate limited version of the function + */ +export function rateLimit any>( + limitCount: number, + limitInterval: number, + fn: T +): T { + type Context = ThisParameterType; + type Args = Parameters; + type QueueItem = [Context, Args]; + + const fifo: QueueItem[] = []; + let count = limitCount; + + function callNext(args?: QueueItem) { + setTimeout(() => { + if (fifo.length > 0) { + callNext(); + } else { + count = count + 1; + } + }, limitInterval); + + const callArgs = fifo.shift(); + + // if there is no next item in the queue + // and we were called with args, trigger function immediately + if (!callArgs && args) { + fn.apply(args[0], args[1]); + return; + } + + if (callArgs) { + fn.apply(callArgs[0], callArgs[1]); + } + } + + return function rateLimitedFunction(this: Context, ...args: Args): ReturnType { + const ctx = this; + + if (count <= 0) { + fifo.push([ctx, args]); + return undefined as ReturnType; + } + + count = count - 1; + callNext([ctx, args]); + return undefined as ReturnType; + } as T; +} \ No newline at end of file diff --git a/src/main/util/sentryWinstonTransport.ts b/src/main/util/sentryWinstonTransport.ts index 6a9d1ab12..945ce48e6 100644 --- a/src/main/util/sentryWinstonTransport.ts +++ b/src/main/util/sentryWinstonTransport.ts @@ -3,6 +3,7 @@ */ import * as Sentry from '@sentry/electron/main'; import TransportStream from 'winston-transport'; +import { rateLimit } from './rateLimiter.js'; // import { LEVEL } from 'triple-beam'; enum SentrySeverity { @@ -47,8 +48,8 @@ class ExtendedError extends Error { export default class SentryTransport extends TransportStream { public silent = false; - private levelsMap: SeverityOptions = {}; + private rateLimitedLog: (info: any, callback: () => void) => void; public constructor(opts?: SentryTransportOptions) { super(opts); @@ -56,12 +57,15 @@ export default class SentryTransport extends TransportStream { this.levelsMap = this.setLevelsMap(opts?.levelsMap); this.silent = opts?.silent || false; + // Initialize rate limited log function - allow 10 logs per second + this.rateLimitedLog = rateLimit(10, 1000, this.processLog.bind(this)); + if (!opts || !opts.skipSentryInit) { Sentry.init(SentryTransport.withDefaults(opts?.sentry || {})); } } - public log(info: any, callback: () => void) { + private processLog(info: any, callback: () => void) { setImmediate(() => { this.emit('logged', info); }); @@ -114,6 +118,10 @@ export default class SentryTransport extends TransportStream { return callback(); } + public log(info: any, callback: () => void) { + this.rateLimitedLog(info, callback); + } + end(...args: any[]) { Sentry.flush().then(() => { super.end(...args); From f56c002f4d40df7b5bea15b148c38b7795f4c27f Mon Sep 17 00:00:00 2001 From: Refatul Islam Date: Tue, 18 Mar 2025 23:50:26 +0600 Subject: [PATCH 3/4] refactor(logging): rename rate limiter and adjust logging behavior - Renamed rateLimitedLog to normalLogRateLimiter for clarity - Updated logging logic to bypass rate limiting for errors and fatal logs - Ensured normal messages are still subject to rate limiting --- src/main/util/sentryWinstonTransport.ts | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/main/util/sentryWinstonTransport.ts b/src/main/util/sentryWinstonTransport.ts index 945ce48e6..eb74ce611 100644 --- a/src/main/util/sentryWinstonTransport.ts +++ b/src/main/util/sentryWinstonTransport.ts @@ -49,7 +49,7 @@ class ExtendedError extends Error { export default class SentryTransport extends TransportStream { public silent = false; private levelsMap: SeverityOptions = {}; - private rateLimitedLog: (info: any, callback: () => void) => void; + private normalLogRateLimiter: (info: any, callback: () => void) => void; public constructor(opts?: SentryTransportOptions) { super(opts); @@ -57,8 +57,8 @@ export default class SentryTransport extends TransportStream { this.levelsMap = this.setLevelsMap(opts?.levelsMap); this.silent = opts?.silent || false; - // Initialize rate limited log function - allow 10 logs per second - this.rateLimitedLog = rateLimit(10, 1000, this.processLog.bind(this)); + // Only rate limit normal logs, not errors + this.normalLogRateLimiter = rateLimit(10, 1000, this.processLog.bind(this)); if (!opts || !opts.skipSentryInit) { Sentry.init(SentryTransport.withDefaults(opts?.sentry || {})); @@ -103,23 +103,30 @@ export default class SentryTransport extends TransportStream { // // ... // }); - // Capturing Errors / Exceptions + // Capturing Errors / Exceptions - bypass rate limiting if (SentryTransport.shouldLogException(sentryLevel)) { const error = Object.values(info).find((value) => value instanceof Error) ?? new ExtendedError(info); Sentry.captureException(error, { tags }); - - return callback(); + callback(); + return; } - // Capturing Messages + // Normal messages go through rate limiting Sentry.captureMessage(message, sentryLevel); - return callback(); + callback(); } public log(info: any, callback: () => void) { - this.rateLimitedLog(info, callback); + // Errors and fatal logs bypass rate limiting + if (SentryTransport.shouldLogException(this.levelsMap[info.level])) { + this.processLog(info, callback); + return; + } + + // Normal logs are rate limited + this.normalLogRateLimiter(info, callback); } end(...args: any[]) { From 4b29a239d7606c3b278921d690abdbdda187c122 Mon Sep 17 00:00:00 2001 From: Refatul Islam Date: Wed, 19 Mar 2025 00:10:04 +0600 Subject: [PATCH 4/4] docs(rateLimiter): add license information and attribution comment - Included a comment attributing the original source of the rate limiting code - Added MIT License notice for clarity and compliance --- src/main/util/rateLimiter.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/util/rateLimiter.ts b/src/main/util/rateLimiter.ts index cc1e104f0..a97aba0f5 100644 --- a/src/main/util/rateLimiter.ts +++ b/src/main/util/rateLimiter.ts @@ -1,3 +1,8 @@ +/** + * Code initially taken from https://github.com/wankdanker/node-function-rate-limit + * MIT License - Copyright (c) 2012 Daniel L. VerWeire + */ + /** * Rate limiting utility function * @param limitCount Maximum number of calls allowed within the interval