mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-05-09 11:55:53 +02:00
pluginHolder: compat installPlugin() without zip_path (legacy pluginInstaller)
Detect inspect.signature for zip_path; fall back to chdir + pluginName.zip for store install, upload install, and upgrade from GitHub.
This commit is contained in:
@@ -17,6 +17,7 @@ import sys
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import time
|
||||
import inspect
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
from pluginInstaller.pluginInstaller import pluginInstaller
|
||||
from .patreon_verifier import PatreonVerifier
|
||||
@@ -43,6 +44,36 @@ PLUGIN_SOURCE_PATHS = ['/home/cyberpanel/plugins', '/home/cyberpanel-plugins']
|
||||
# These plugins show "Built-in" badge and only Settings button (no Deactivate/Uninstall)
|
||||
BUILTIN_PLUGINS = frozenset(['emailMarketing', 'emailPremium'])
|
||||
|
||||
|
||||
def _install_plugin_compat(plugin_name, zip_path_abs):
|
||||
"""
|
||||
Call pluginInstaller.installPlugin with zip_path when supported (newer CyberPanel).
|
||||
Older installs only accept installPlugin(pluginName) and expect pluginName.zip in CWD.
|
||||
"""
|
||||
zip_path_abs = os.path.abspath(zip_path_abs)
|
||||
work_dir = os.path.dirname(zip_path_abs)
|
||||
use_zip_kw = False
|
||||
try:
|
||||
sig = inspect.signature(pluginInstaller.installPlugin)
|
||||
use_zip_kw = 'zip_path' in sig.parameters
|
||||
except (TypeError, ValueError):
|
||||
use_zip_kw = False
|
||||
if use_zip_kw:
|
||||
pluginInstaller.installPlugin(plugin_name, zip_path=zip_path_abs)
|
||||
return
|
||||
prev_cwd = os.getcwd()
|
||||
try:
|
||||
os.chdir(work_dir)
|
||||
expected_zip = os.path.join(work_dir, plugin_name + '.zip')
|
||||
if zip_path_abs != expected_zip:
|
||||
shutil.copy2(zip_path_abs, expected_zip)
|
||||
pluginInstaller.installPlugin(plugin_name)
|
||||
finally:
|
||||
try:
|
||||
os.chdir(prev_cwd)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Core CyberPanel app dirs under /usr/local/CyberCP that must not be counted as "installed plugins"
|
||||
# (matches pluginHolder.urls so Installed count = store/plugin dirs only, not core apps)
|
||||
RESERVED_PLUGIN_DIRS = frozenset([
|
||||
@@ -638,13 +669,11 @@ def install_plugin(request, plugin_name):
|
||||
zip_path_abs = os.path.abspath(zip_path)
|
||||
if not os.path.exists(zip_path_abs):
|
||||
raise Exception(f'Zip file not found: {zip_path_abs}')
|
||||
original_cwd = os.getcwd()
|
||||
os.chdir(temp_dir)
|
||||
|
||||
try:
|
||||
# Install using pluginInstaller with explicit zip path (avoids cwd races)
|
||||
# Install using pluginInstaller (zip_path kw when supported; else legacy CWD + pluginName.zip)
|
||||
try:
|
||||
pluginInstaller.installPlugin(plugin_name, zip_path=zip_path_abs)
|
||||
_install_plugin_compat(plugin_name, zip_path_abs)
|
||||
except Exception as install_error:
|
||||
# Log the full error for debugging
|
||||
error_msg = str(install_error)
|
||||
@@ -680,7 +709,6 @@ def install_plugin(request, plugin_name):
|
||||
'message': f'Plugin {plugin_name} installed successfully'
|
||||
})
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
# Cleanup
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
|
||||
@@ -1520,55 +1548,50 @@ def upgrade_plugin(request, plugin_name):
|
||||
zip_path_abs = os.path.abspath(zip_path)
|
||||
if not os.path.exists(zip_path_abs):
|
||||
raise Exception(f'Zip file not found: {zip_path_abs}')
|
||||
original_cwd = os.getcwd()
|
||||
os.chdir(temp_dir)
|
||||
|
||||
logging.writeToFile(f"Upgrading plugin using pluginInstaller (zip={zip_path_abs})")
|
||||
|
||||
# Install using pluginInstaller (zip_path kw when supported; else legacy)
|
||||
try:
|
||||
logging.writeToFile(f"Upgrading plugin using pluginInstaller (zip={zip_path_abs})")
|
||||
|
||||
# Install using pluginInstaller with explicit zip path (this will overwrite existing files)
|
||||
try:
|
||||
pluginInstaller.installPlugin(plugin_name, zip_path=zip_path_abs)
|
||||
except Exception as install_error:
|
||||
error_msg = str(install_error)
|
||||
logging.writeToFile(f"pluginInstaller.installPlugin raised exception: {error_msg}")
|
||||
# Check if plugin directory exists despite the error
|
||||
if not os.path.exists(pluginInstalled):
|
||||
raise Exception(f'Plugin upgrade failed: {error_msg}')
|
||||
|
||||
# Wait for file system to sync
|
||||
import time
|
||||
time.sleep(3)
|
||||
|
||||
# Verify plugin was upgraded
|
||||
_install_plugin_compat(plugin_name, zip_path_abs)
|
||||
except Exception as install_error:
|
||||
error_msg = str(install_error)
|
||||
logging.writeToFile(f"pluginInstaller.installPlugin raised exception: {error_msg}")
|
||||
# Check if plugin directory exists despite the error
|
||||
if not os.path.exists(pluginInstalled):
|
||||
raise Exception(f'Plugin upgrade failed: {pluginInstalled} does not exist after upgrade')
|
||||
|
||||
# Sync meta.xml from GitHub raw so version matches store (archive ZIP can be cached/stale)
|
||||
raise Exception(f'Plugin upgrade failed: {error_msg}')
|
||||
|
||||
# Wait for file system to sync
|
||||
import time
|
||||
time.sleep(3)
|
||||
|
||||
# Verify plugin was upgraded
|
||||
if not os.path.exists(pluginInstalled):
|
||||
raise Exception(f'Plugin upgrade failed: {pluginInstalled} does not exist after upgrade')
|
||||
|
||||
# Sync meta.xml from GitHub raw so version matches store (archive ZIP can be cached/stale)
|
||||
_sync_meta_xml_from_github(plugin_name, '/usr/local/CyberCP')
|
||||
new_version = _get_installed_version(plugin_name, '/usr/local/CyberCP')
|
||||
# If version unchanged, meta sync may have failed (e.g. network); retry once
|
||||
if new_version == installed_version:
|
||||
logging.writeToFile(f"Plugin {plugin_name}: version unchanged after first meta sync, retrying sync")
|
||||
_sync_meta_xml_from_github(plugin_name, '/usr/local/CyberCP')
|
||||
new_version = _get_installed_version(plugin_name, '/usr/local/CyberCP')
|
||||
# If version unchanged, meta sync may have failed (e.g. network); retry once
|
||||
if new_version == installed_version:
|
||||
logging.writeToFile(f"Plugin {plugin_name}: version unchanged after first meta sync, retrying sync")
|
||||
_sync_meta_xml_from_github(plugin_name, '/usr/local/CyberCP')
|
||||
new_version = _get_installed_version(plugin_name, '/usr/local/CyberCP')
|
||||
if new_version == installed_version:
|
||||
logging.writeToFile(f"Plugin {plugin_name}: version still {installed_version} after upgrade; meta.xml may not have been updated from GitHub")
|
||||
|
||||
logging.writeToFile(f"Plugin {plugin_name} upgraded successfully from {installed_version} to {new_version}")
|
||||
|
||||
backup_message = ''
|
||||
if backup_path:
|
||||
backup_message = f' Backup created at: {backup_info.get("timestamp", "unknown")}'
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'message': f'Plugin {plugin_name} upgraded successfully from {installed_version} to {new_version}.{backup_message}',
|
||||
'backup_created': backup_path is not None,
|
||||
'backup_path': backup_path if backup_path else None
|
||||
})
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
if new_version == installed_version:
|
||||
logging.writeToFile(f"Plugin {plugin_name}: version still {installed_version} after upgrade; meta.xml may not have been updated from GitHub")
|
||||
|
||||
logging.writeToFile(f"Plugin {plugin_name} upgraded successfully from {installed_version} to {new_version}")
|
||||
|
||||
backup_message = ''
|
||||
if backup_path:
|
||||
backup_message = f' Backup created at: {backup_info.get("timestamp", "unknown")}'
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'message': f'Plugin {plugin_name} upgraded successfully from {installed_version} to {new_version}.{backup_message}',
|
||||
'backup_created': backup_path is not None,
|
||||
'backup_path': backup_path if backup_path else None
|
||||
})
|
||||
|
||||
finally:
|
||||
# Cleanup
|
||||
@@ -1792,55 +1815,50 @@ def install_from_store(request, plugin_name):
|
||||
|
||||
# Pass absolute path so extraction does not depend on cwd (installPlugin may change cwd)
|
||||
zip_path_abs = os.path.abspath(zip_path)
|
||||
original_cwd = os.getcwd()
|
||||
os.chdir(temp_dir)
|
||||
|
||||
logging.writeToFile(f"Installing plugin using pluginInstaller (zip={zip_path_abs})")
|
||||
|
||||
# Install using pluginInstaller (zip_path kw when supported; else legacy)
|
||||
try:
|
||||
logging.writeToFile(f"Installing plugin using pluginInstaller (zip={zip_path_abs})")
|
||||
|
||||
# Install using pluginInstaller with explicit zip path (avoids cwd races)
|
||||
try:
|
||||
pluginInstaller.installPlugin(plugin_name, zip_path=zip_path_abs)
|
||||
except Exception as install_error:
|
||||
# Log the full error for debugging
|
||||
error_msg = str(install_error)
|
||||
logging.writeToFile(f"pluginInstaller.installPlugin raised exception: {error_msg}")
|
||||
# Check if plugin directory exists despite the error
|
||||
pluginInstalled = '/usr/local/CyberCP/' + plugin_name
|
||||
if os.path.exists(pluginInstalled):
|
||||
logging.writeToFile(f"Plugin directory exists despite error, continuing...")
|
||||
else:
|
||||
raise Exception(f'Plugin installation failed: {error_msg}')
|
||||
|
||||
# Wait a moment for file system to sync and service to restart
|
||||
import time
|
||||
time.sleep(3) # Increased wait time for file system sync
|
||||
|
||||
# Verify plugin was actually installed
|
||||
_install_plugin_compat(plugin_name, zip_path_abs)
|
||||
except Exception as install_error:
|
||||
# Log the full error for debugging
|
||||
error_msg = str(install_error)
|
||||
logging.writeToFile(f"pluginInstaller.installPlugin raised exception: {error_msg}")
|
||||
# Check if plugin directory exists despite the error
|
||||
pluginInstalled = '/usr/local/CyberCP/' + plugin_name
|
||||
if not os.path.exists(pluginInstalled):
|
||||
# Exclude README.md - main CyberPanel repo has it at root
|
||||
root_files = ['apps.py', 'meta.xml', 'urls.py', 'views.py']
|
||||
found_root_files = [f for f in root_files if os.path.exists(os.path.join('/usr/local/CyberCP', f))]
|
||||
if found_root_files:
|
||||
raise Exception(f'Plugin installation failed: Files extracted to wrong location. Found {found_root_files} in /usr/local/CyberCP/ root instead of {pluginInstalled}/')
|
||||
raise Exception(f'Plugin installation failed: {pluginInstalled} does not exist after installation')
|
||||
|
||||
# Sync meta.xml from GitHub raw so version matches store
|
||||
_sync_meta_xml_from_github(plugin_name, '/usr/local/CyberCP')
|
||||
|
||||
logging.writeToFile(f"Plugin {plugin_name} installed successfully")
|
||||
|
||||
# Set plugin to enabled by default after installation
|
||||
_set_plugin_state(plugin_name, True)
|
||||
|
||||
_ensure_plugin_meta_xml(plugin_name)
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'message': f'Plugin {plugin_name} installed successfully from store'
|
||||
})
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
if os.path.exists(pluginInstalled):
|
||||
logging.writeToFile(f"Plugin directory exists despite error, continuing...")
|
||||
else:
|
||||
raise Exception(f'Plugin installation failed: {error_msg}')
|
||||
|
||||
# Wait a moment for file system to sync and service to restart
|
||||
import time
|
||||
time.sleep(3) # Increased wait time for file system sync
|
||||
|
||||
# Verify plugin was actually installed
|
||||
pluginInstalled = '/usr/local/CyberCP/' + plugin_name
|
||||
if not os.path.exists(pluginInstalled):
|
||||
# Exclude README.md - main CyberPanel repo has it at root
|
||||
root_files = ['apps.py', 'meta.xml', 'urls.py', 'views.py']
|
||||
found_root_files = [f for f in root_files if os.path.exists(os.path.join('/usr/local/CyberCP', f))]
|
||||
if found_root_files:
|
||||
raise Exception(f'Plugin installation failed: Files extracted to wrong location. Found {found_root_files} in /usr/local/CyberCP/ root instead of {pluginInstalled}/')
|
||||
raise Exception(f'Plugin installation failed: {pluginInstalled} does not exist after installation')
|
||||
|
||||
# Sync meta.xml from GitHub raw so version matches store
|
||||
_sync_meta_xml_from_github(plugin_name, '/usr/local/CyberCP')
|
||||
|
||||
logging.writeToFile(f"Plugin {plugin_name} installed successfully")
|
||||
|
||||
# Set plugin to enabled by default after installation
|
||||
_set_plugin_state(plugin_name, True)
|
||||
|
||||
_ensure_plugin_meta_xml(plugin_name)
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'message': f'Plugin {plugin_name} installed successfully from store'
|
||||
})
|
||||
|
||||
finally:
|
||||
# Cleanup
|
||||
|
||||
Reference in New Issue
Block a user