From e92f3a7f17447ac3daa5a0edd8aea9b49f58227c Mon Sep 17 00:00:00 2001 From: master3395 Date: Mon, 26 Jan 2026 03:20:56 +0100 Subject: [PATCH] fix(plugins): Add verification and retry logic to uninstall process - Verify plugin directory is actually removed after removeFiles() - If directory still exists, retry with ProcessUtilities.normalExecutioner() - Return error if directory still exists after all attempts - Prevents silent failures where uninstall appears successful but plugin remains Fixes: Plugins showing as installed after 'successful' uninstall --- pluginHolder/views.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/pluginHolder/views.py b/pluginHolder/views.py index 5b2938bc2..59b8f9dcd 100644 --- a/pluginHolder/views.py +++ b/pluginHolder/views.py @@ -619,7 +619,31 @@ def uninstall_plugin(request, plugin_name): pluginInstaller.removeMigrations(plugin_name) # Remove installed directory (but keep source in /home/cyberpanel/plugins/) - pluginInstaller.removeFiles(plugin_name) + try: + pluginInstaller.removeFiles(plugin_name) + + # Verify removal succeeded + pluginInstalled = '/usr/local/CyberCP/' + plugin_name + if os.path.exists(pluginInstalled): + # Removal failed - try again with more aggressive method + logging.writeToFile(f'Plugin directory still exists after removeFiles, trying ProcessUtilities') + try: + from plogical.processUtilities import ProcessUtilities + rm_cmd = f'rm -rf {pluginInstalled}' + ProcessUtilities.normalExecutioner(rm_cmd) + + # Verify again + if os.path.exists(pluginInstalled): + raise Exception(f'Plugin directory still exists after ProcessUtilities removal: {pluginInstalled}') + except Exception as e2: + logging.writeToFile(f'ProcessUtilities removal also failed: {str(e2)}') + raise Exception(f'Failed to remove plugin directory. Tried removeFiles() and ProcessUtilities. Directory still exists: {pluginInstalled}') + except Exception as e: + logging.writeToFile(f'Error removing plugin files: {str(e)}') + return JsonResponse({ + 'success': False, + 'error': f'Failed to remove plugin directory: {str(e)}' + }, status=500) # DON'T call informCyberPanelRemoval - we want to keep the source directory # so users can reinstall the plugin later