mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-05-07 02:35:30 +02:00
Plugin settings 404 and uninstall permission fixes
- Add plugin_settings_proxy for /plugins/<name>/settings/ so settings pages
work for all installed plugins (handles plugins installed after worker start)
- Add path('<str:plugin_name>/settings/', plugin_settings_proxy) in pluginHolder.urls
- removeFromSettings/removeFromURLs: use try/except for read/write, raise clear
PermissionError message; ensure panel user can write (chgrp lscpd; chmod g+w)
- Deploy: make CyberCP/settings.py, urls.py, baseTemplate index.html group-
writable by lscpd so uninstall can update them
This commit is contained in:
@@ -104,10 +104,11 @@ urlpatterns = [
|
||||
path('api/revert/<str:plugin_name>/', views.revert_plugin, name='revert_plugin'),
|
||||
path('api/debug-plugins/', views.debug_loaded_plugins, name='debug_loaded_plugins'),
|
||||
path('api/check-subscription/<str:plugin_name>/', views.check_plugin_subscription, name='check_plugin_subscription'),
|
||||
path('<str:plugin_name>/settings/', views.plugin_settings_proxy, name='plugin_settings_proxy'),
|
||||
path('<str:plugin_name>/help/', views.plugin_help, name='plugin_help'),
|
||||
]
|
||||
|
||||
# Include each installed plugin's URLs *before* the catch-all so /plugins/<name>/settings/ etc. match
|
||||
# Include each installed plugin's URLs *before* the catch-all so /plugins/<name>/... (other than settings/help) match
|
||||
_loaded_plugins = []
|
||||
_failed_plugins = {}
|
||||
for _plugin_name, _path_parent in _get_installed_plugin_list():
|
||||
|
||||
@@ -1862,6 +1862,38 @@ def debug_loaded_plugins(request):
|
||||
except Exception as e:
|
||||
return JsonResponse({'success': False, 'error': str(e)}, status=500)
|
||||
|
||||
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def plugin_settings_proxy(request, plugin_name):
|
||||
"""
|
||||
Proxy for /plugins/<plugin_name>/settings/ so plugin settings pages work even when
|
||||
the plugin was installed after the worker started (dynamic URL list is built at import time).
|
||||
"""
|
||||
mailUtilities.checkHome()
|
||||
plugin_path = '/usr/local/CyberCP/' + plugin_name
|
||||
urls_py = os.path.join(plugin_path, 'urls.py')
|
||||
if not plugin_name or not os.path.isdir(plugin_path) or not os.path.exists(urls_py):
|
||||
from django.http import HttpResponseNotFound
|
||||
return HttpResponseNotFound('Plugin not found or has no URL configuration.')
|
||||
if plugin_name in RESERVED_PLUGIN_DIRS or plugin_name in (
|
||||
'api', 'installed', 'help', 'emailMarketing', 'emailPremium', 'pluginHolder'
|
||||
):
|
||||
from django.http import HttpResponseNotFound
|
||||
return HttpResponseNotFound('Invalid plugin.')
|
||||
try:
|
||||
import importlib
|
||||
views_mod = importlib.import_module(plugin_name + '.views')
|
||||
settings_view = getattr(views_mod, 'settings', None)
|
||||
if not callable(settings_view):
|
||||
from django.http import HttpResponseNotFound
|
||||
return HttpResponseNotFound('Plugin has no settings view.')
|
||||
return settings_view(request)
|
||||
except Exception as e:
|
||||
logging.writeToFile(f"plugin_settings_proxy for {plugin_name}: {str(e)}")
|
||||
from django.http import HttpResponseServerError
|
||||
return HttpResponseServerError(f'Plugin settings error: {str(e)}')
|
||||
|
||||
|
||||
def plugin_help(request, plugin_name):
|
||||
"""Plugin-specific help page - shows plugin information, version history, and help content"""
|
||||
mailUtilities.checkHome()
|
||||
|
||||
@@ -467,41 +467,50 @@ class pluginInstaller:
|
||||
|
||||
@staticmethod
|
||||
def removeFromSettings(pluginName):
|
||||
data = open("/usr/local/CyberCP/CyberCP/settings.py", 'r', encoding='utf-8').readlines()
|
||||
writeToFile = open("/usr/local/CyberCP/CyberCP/settings.py", 'w', encoding='utf-8')
|
||||
|
||||
settings_path = "/usr/local/CyberCP/CyberCP/settings.py"
|
||||
try:
|
||||
with open(settings_path, 'r', encoding='utf-8') as f:
|
||||
data = f.readlines()
|
||||
except (OSError, IOError) as e:
|
||||
raise Exception(f'Cannot read {settings_path}: {e}. Ensure the panel user can read it.')
|
||||
in_installed_apps = False
|
||||
out_lines = []
|
||||
for i, items in enumerate(data):
|
||||
# Track if we're in INSTALLED_APPS section
|
||||
if 'INSTALLED_APPS' in items and '=' in items:
|
||||
in_installed_apps = True
|
||||
elif in_installed_apps and items.strip().startswith(']'):
|
||||
in_installed_apps = False
|
||||
|
||||
# More precise matching: look for plugin name in quotes (e.g., 'pluginName' or "pluginName")
|
||||
# Only match if we're in INSTALLED_APPS section to prevent false positives
|
||||
if in_installed_apps and (f"'{pluginName}'" in items or f'"{pluginName}"' in items):
|
||||
continue
|
||||
else:
|
||||
writeToFile.writelines(items)
|
||||
writeToFile.close()
|
||||
out_lines.append(items)
|
||||
try:
|
||||
with open(settings_path, 'w', encoding='utf-8') as writeToFile:
|
||||
writeToFile.writelines(out_lines)
|
||||
except (OSError, IOError) as e:
|
||||
raise Exception(
|
||||
f'Cannot write {settings_path}: {e}. '
|
||||
'Ensure the file is writable by the panel user (e.g. chgrp lscpd ... ; chmod g+w ...).'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def removeFromURLs(pluginName):
|
||||
data = open("/usr/local/CyberCP/CyberCP/urls.py", 'r', encoding='utf-8').readlines()
|
||||
writeToFile = open("/usr/local/CyberCP/CyberCP/urls.py", 'w', encoding='utf-8')
|
||||
|
||||
urls_path = "/usr/local/CyberCP/CyberCP/urls.py"
|
||||
try:
|
||||
with open(urls_path, 'r', encoding='utf-8') as f:
|
||||
data = f.readlines()
|
||||
except (OSError, IOError) as e:
|
||||
raise Exception(f'Cannot read {urls_path}: {e}.')
|
||||
out_lines = []
|
||||
for items in data:
|
||||
# More precise matching: look for plugin name in path() or include() calls
|
||||
# Match patterns like: path('plugins/pluginName/', include('pluginName.urls'))
|
||||
# This prevents partial matches
|
||||
if (f"plugins/{pluginName}/" in items or f"'{pluginName}.urls'" in items or f'"{pluginName}.urls"' in items or
|
||||
if (f"plugins/{pluginName}/" in items or f"'{pluginName}.urls'" in items or f'"{pluginName}.urls"' in items or
|
||||
f"include('{pluginName}.urls')" in items or f'include("{pluginName}.urls")' in items):
|
||||
continue
|
||||
else:
|
||||
writeToFile.writelines(items)
|
||||
|
||||
writeToFile.close()
|
||||
out_lines.append(items)
|
||||
try:
|
||||
with open(urls_path, 'w', encoding='utf-8') as f:
|
||||
f.writelines(out_lines)
|
||||
except (OSError, IOError) as e:
|
||||
raise Exception(f'Cannot write {urls_path}: {e}. Ensure the file is writable by the panel user (chgrp lscpd; chmod g+w).')
|
||||
|
||||
@staticmethod
|
||||
def informCyberPanelRemoval(pluginName):
|
||||
|
||||
Reference in New Issue
Block a user