Plugins: fix 404 - add plugin roots to sys.path before loading URLs

- pluginHolder/urls: insert /usr/local/CyberCP and source paths at top of module
  so __import__(plugin_name + '.urls') finds plugin packages (fixes No module
  named 'X.urls'). Register plugin routes before catch-all help route.
- pluginHolder/views: add debug_loaded_plugins API and log full traceback on
  plugin import failure. panelAccess/emailMarketing settings/ routes added
  earlier; cspManager resilient to missing DB table.
Author: master3395
This commit is contained in:
master3395
2026-02-15 23:24:04 +01:00
parent 48ebfc79b2
commit 197f355cf7
2 changed files with 34 additions and 2 deletions

View File

@@ -13,10 +13,17 @@ from django.urls import path, include
import os
import sys
# Ensure plugin roots are on sys.path first so __import__(plugin_name + '.urls') can find packages
_INSTALLED_PLUGINS_PATH = '/usr/local/CyberCP'
_PLUGIN_SOURCE_PATHS = ['/home/cyberpanel/plugins', '/home/cyberpanel-plugins']
for _p in [_INSTALLED_PLUGINS_PATH] + _PLUGIN_SOURCE_PATHS:
if _p and os.path.isdir(_p) and _p not in sys.path:
sys.path.insert(0, _p)
from . import views
# Installed plugins live under this path (must match pluginInstaller and pluginHolder.views)
INSTALLED_PLUGINS_PATH = '/usr/local/CyberCP'
INSTALLED_PLUGINS_PATH = _INSTALLED_PLUGINS_PATH
# Source paths for plugins (same as pluginHolder.views PLUGIN_SOURCE_PATHS)
# Checked when plugin is not under INSTALLED_PLUGINS_PATH so URLs still work
@@ -95,22 +102,29 @@ urlpatterns = [
path('api/store/upgrade/<str:plugin_name>/', views.upgrade_plugin, name='upgrade_plugin'),
path('api/backups/<str:plugin_name>/', views.get_plugin_backups, name='get_plugin_backups'),
path('api/revert/<str:plugin_name>/', views.revert_plugin, name='revert_plugin'),
path('api/debug-plugins/', views.debug_loaded_plugins, name='debug_loaded_plugins'),
]
# Include each installed plugin's URLs *before* the catch-all so /plugins/<name>/settings/ etc. match
_loaded_plugins = []
_failed_plugins = {}
for _plugin_name, _path_parent in _get_installed_plugin_list():
try:
if _path_parent not in sys.path:
sys.path.insert(0, _path_parent)
__import__(_plugin_name + '.urls')
urlpatterns.append(path(_plugin_name + '/', include(_plugin_name + '.urls')))
except (ImportError, AttributeError) as e:
_loaded_plugins.append(_plugin_name)
except Exception as e:
import traceback
_failed_plugins[_plugin_name] = str(e)
try:
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as _logging
_logging.writeToFile(
'pluginHolder.urls: Skipping plugin "%s" (urls not loadable): %s'
% (_plugin_name, e)
)
_logging.writeToFile(traceback.format_exc())
except Exception:
pass

View File

@@ -1854,6 +1854,24 @@ def install_from_store(request, plugin_name):
'error': str(e)
}, status=500)
@csrf_exempt
@require_http_methods(["GET"])
def debug_loaded_plugins(request):
"""Return which plugins have URL routes loaded and which failed (for diagnosing 404s)."""
try:
import pluginHolder.urls as urls_mod
loaded = list(getattr(urls_mod, '_loaded_plugins', []))
failed = dict(getattr(urls_mod, '_failed_plugins', {}))
return JsonResponse({
'success': True,
'loaded': loaded,
'failed': failed,
'loaded_count': len(loaded),
'failed_count': len(failed),
}, json_dumps_params={'indent': 2})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)}, status=500)
def plugin_help(request, plugin_name):
"""Plugin-specific help page - shows plugin information, version history, and help content"""
mailUtilities.checkHome()