mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-02-04 21:59:05 +01:00
Plugin upgrade: sync meta.xml from GitHub raw so version matches store; logging get_current_timestamp
- Add CyberCPLogFileWriter.get_current_timestamp() for errorSanitizer (fixes 500 logging) - After upgrade/install: fetch meta.xml from raw GitHub and overwrite installed file so store version (e.g. 1.1.0) is correct even when archive ZIP is cached/stale - Upgrade/install: discover ZIP top-level folder and match plugin folder case-insensitively - Improves redisManager/memcacheManager upgrade and all store installs
This commit is contained in:
@@ -7,6 +7,11 @@ import smtplib
|
||||
class CyberCPLogFileWriter:
|
||||
fileName = "/home/cyberpanel/error-logs.txt"
|
||||
|
||||
@staticmethod
|
||||
def get_current_timestamp():
|
||||
"""Return current timestamp in same format used for log lines (for errorSanitizer etc)."""
|
||||
return time.strftime("%m.%d.%Y_%H-%M-%S")
|
||||
|
||||
@staticmethod
|
||||
def AddFromHeader(sender, message):
|
||||
try:
|
||||
|
||||
@@ -887,6 +887,27 @@ def _get_installed_version(plugin_dir, plugin_install_dir):
|
||||
|
||||
return None
|
||||
|
||||
def _sync_meta_xml_from_github(plugin_name, plugin_install_dir='/usr/local/CyberCP'):
|
||||
"""
|
||||
Fetch meta.xml from GitHub raw (main) and overwrite installed meta.xml.
|
||||
Ensures installed version matches store even when archive ZIP is cached/stale.
|
||||
Returns True if synced, False on non-fatal failure (logged).
|
||||
"""
|
||||
meta_url = f'{GITHUB_RAW_BASE}/{plugin_name}/meta.xml'
|
||||
meta_path = os.path.join(plugin_install_dir, plugin_name, 'meta.xml')
|
||||
try:
|
||||
req = urllib.request.Request(meta_url, headers={'User-Agent': 'CyberPanel-Plugin-Store/1.0'})
|
||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||
content = resp.read()
|
||||
if content:
|
||||
with open(meta_path, 'wb') as f:
|
||||
f.write(content)
|
||||
logging.writeToFile(f"Synced meta.xml for {plugin_name} from GitHub raw")
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.writeToFile(f"Could not sync meta.xml for {plugin_name} from GitHub: {str(e)}")
|
||||
return False
|
||||
|
||||
def _create_plugin_backup(plugin_name, plugin_install_dir='/usr/local/CyberCP'):
|
||||
"""
|
||||
Create a backup of a plugin before upgrade
|
||||
@@ -1362,22 +1383,48 @@ def upgrade_plugin(request, plugin_name):
|
||||
|
||||
# Extract plugin directory from repository ZIP
|
||||
repo_zip = zipfile.ZipFile(io.BytesIO(repo_zip_data))
|
||||
namelist = repo_zip.namelist()
|
||||
|
||||
# Find plugin directory in ZIP
|
||||
plugin_prefix = f'cyberpanel-plugins-main/{plugin_name}/'
|
||||
plugin_files = [f for f in repo_zip.namelist() if f.startswith(plugin_prefix)]
|
||||
# Discover top-level folder (GitHub uses repo-name-branch, e.g. cyberpanel-plugins-main)
|
||||
top_level = None
|
||||
for name in namelist:
|
||||
if '/' in name:
|
||||
top_level = name.split('/')[0]
|
||||
break
|
||||
elif name and not name.endswith('/'):
|
||||
top_level = name
|
||||
break
|
||||
if not top_level:
|
||||
raise Exception('GitHub archive has no recognizable structure')
|
||||
|
||||
# Find plugin folder in ZIP (case-insensitive: repo may have RedisManager vs redisManager)
|
||||
plugin_prefix = None
|
||||
plugin_name_lower = plugin_name.lower()
|
||||
for name in namelist:
|
||||
if '/' not in name:
|
||||
continue
|
||||
parts = name.split('/')
|
||||
if len(parts) >= 2 and parts[0] == top_level and parts[1].lower() == plugin_name_lower:
|
||||
# Use the actual casing from the ZIP for reading
|
||||
plugin_prefix = f'{top_level}/{parts[1]}/'
|
||||
break
|
||||
if not plugin_prefix:
|
||||
sample = namelist[:15] if len(namelist) > 15 else namelist
|
||||
logging.writeToFile(f"Plugin {plugin_name} not in archive. Top-level={top_level}, sample paths: {sample}")
|
||||
raise Exception(f'Plugin {plugin_name} not found in GitHub repository (checked under {top_level}/)')
|
||||
|
||||
plugin_files = [f for f in namelist if f.startswith(plugin_prefix)]
|
||||
if not plugin_files:
|
||||
logging.writeToFile(f"Plugin {plugin_name}: no files under prefix {plugin_prefix}")
|
||||
raise Exception(f'Plugin {plugin_name} not found in GitHub repository')
|
||||
|
||||
logging.writeToFile(f"Found {len(plugin_files)} files for plugin {plugin_name} in GitHub")
|
||||
logging.writeToFile(f"Found {len(plugin_files)} files for plugin {plugin_name} in GitHub (prefix {plugin_prefix})")
|
||||
|
||||
# Create plugin ZIP file from GitHub with correct structure
|
||||
# Create plugin ZIP with correct structure: plugin_name/... for install to /usr/local/CyberCP/plugin_name/
|
||||
plugin_zip = zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED)
|
||||
|
||||
for file_path in plugin_files:
|
||||
relative_path = file_path[len(plugin_prefix):]
|
||||
if relative_path: # Skip directories
|
||||
if relative_path: # Skip directory-only entries
|
||||
file_data = repo_zip.read(file_path)
|
||||
arcname = os.path.join(plugin_name, relative_path)
|
||||
plugin_zip.writestr(arcname, file_data)
|
||||
@@ -1420,7 +1467,10 @@ def upgrade_plugin(request, plugin_name):
|
||||
if not os.path.exists(pluginInstalled):
|
||||
raise Exception(f'Plugin upgrade failed: {pluginInstalled} does not exist after upgrade')
|
||||
|
||||
# Get new version
|
||||
# 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')
|
||||
|
||||
# Get new version (now reflects meta.xml from main)
|
||||
new_version = _get_installed_version(plugin_name, '/usr/local/CyberCP')
|
||||
|
||||
logging.writeToFile(f"Plugin {plugin_name} upgraded successfully from {installed_version} to {new_version}")
|
||||
@@ -1593,32 +1643,46 @@ def install_from_store(request, plugin_name):
|
||||
|
||||
# Extract plugin directory from repository ZIP
|
||||
repo_zip = zipfile.ZipFile(io.BytesIO(repo_zip_data))
|
||||
namelist = repo_zip.namelist()
|
||||
|
||||
# Find plugin directory in ZIP
|
||||
plugin_prefix = f'cyberpanel-plugins-main/{plugin_name}/'
|
||||
plugin_files = [f for f in repo_zip.namelist() if f.startswith(plugin_prefix)]
|
||||
|
||||
if not plugin_files:
|
||||
# Discover top-level folder and find plugin (case-insensitive)
|
||||
top_level = None
|
||||
for name in namelist:
|
||||
if '/' in name:
|
||||
top_level = name.split('/')[0]
|
||||
break
|
||||
if not top_level:
|
||||
raise Exception('GitHub archive has no recognizable structure')
|
||||
plugin_prefix = None
|
||||
plugin_name_lower = plugin_name.lower()
|
||||
for name in namelist:
|
||||
if '/' not in name:
|
||||
continue
|
||||
parts = name.split('/')
|
||||
if len(parts) >= 2 and parts[0] == top_level and parts[1].lower() == plugin_name_lower:
|
||||
plugin_prefix = f'{top_level}/{parts[1]}/'
|
||||
break
|
||||
if not plugin_prefix:
|
||||
repo_zip.close()
|
||||
logging.writeToFile(f"Plugin {plugin_name} not found in GitHub repository, trying local source")
|
||||
use_local_fallback = True
|
||||
else:
|
||||
logging.writeToFile(f"Found {len(plugin_files)} files for plugin {plugin_name} in GitHub")
|
||||
|
||||
# Create plugin ZIP file from GitHub with correct structure
|
||||
# The ZIP must contain plugin_name/ directory structure for proper extraction
|
||||
plugin_zip = zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED)
|
||||
|
||||
for file_path in plugin_files:
|
||||
# Remove the repository root prefix
|
||||
relative_path = file_path[len(plugin_prefix):]
|
||||
if relative_path: # Skip directories
|
||||
file_data = repo_zip.read(file_path)
|
||||
# Add plugin_name prefix to maintain directory structure
|
||||
arcname = os.path.join(plugin_name, relative_path)
|
||||
plugin_zip.writestr(arcname, file_data)
|
||||
|
||||
plugin_zip.close()
|
||||
repo_zip.close()
|
||||
plugin_files = [f for f in namelist if f.startswith(plugin_prefix)]
|
||||
if not plugin_files:
|
||||
repo_zip.close()
|
||||
logging.writeToFile(f"Plugin {plugin_name} not found in GitHub repository, trying local source")
|
||||
use_local_fallback = True
|
||||
else:
|
||||
logging.writeToFile(f"Found {len(plugin_files)} files for plugin {plugin_name} in GitHub")
|
||||
plugin_zip = zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED)
|
||||
for file_path in plugin_files:
|
||||
relative_path = file_path[len(plugin_prefix):]
|
||||
if relative_path:
|
||||
file_data = repo_zip.read(file_path)
|
||||
arcname = os.path.join(plugin_name, relative_path)
|
||||
plugin_zip.writestr(arcname, file_data)
|
||||
plugin_zip.close()
|
||||
repo_zip.close()
|
||||
except Exception as github_error:
|
||||
logging.writeToFile(f"GitHub download failed for {plugin_name}: {str(github_error)}, trying local source")
|
||||
use_local_fallback = True
|
||||
@@ -1694,6 +1758,9 @@ def install_from_store(request, plugin_name):
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user