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:
master3395
2026-03-24 22:28:08 +01:00
parent bd790c39a6
commit bb61871b28

View File

@@ -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