diff --git a/CyberCP/urls.py b/CyberCP/urls.py index 18467e668..e6ccfaa15 100644 --- a/CyberCP/urls.py +++ b/CyberCP/urls.py @@ -24,6 +24,13 @@ from firewall import views as firewall_views # includes each installed plugin (under /plugins//) so settings and # other plugin pages work for any installed plugin. +# Optional app: may be missing after clean clone or git clean -fd (not in all repo trees) +_optional_email_marketing = [] +try: + _optional_email_marketing.append(path('emailMarketing/', include('emailMarketing.urls'))) +except ModuleNotFoundError: + pass + urlpatterns = [ # Serve static files first (before catch-all routes) re_path(r'^static/(?P.*)$', serve, {'document_root': settings.STATIC_ROOT}), @@ -47,7 +54,7 @@ urlpatterns = [ path('api/', include('api.urls')), path('filemanager/', include('filemanager.urls')), path('emailPremium/', include('emailPremium.urls')), - path('emailMarketing/', include('emailMarketing.urls')), # Default-installed (sidebar links to it) + *_optional_email_marketing, path('manageservices/', include('manageServices.urls')), path('plugins/', include('pluginHolder.urls')), path('cloudAPI/', include('cloudAPI.urls')), diff --git a/baseTemplate/static/baseTemplate/custom-js/system-status.js b/baseTemplate/static/baseTemplate/custom-js/system-status.js index f26ebe047..eb72873e0 100644 --- a/baseTemplate/static/baseTemplate/custom-js/system-status.js +++ b/baseTemplate/static/baseTemplate/custom-js/system-status.js @@ -122,9 +122,17 @@ app.controller('systemStatusInfo', function ($scope, $http, $timeout) { $scope.uptimeLoaded = false; $scope.uptime = 'Loading...'; - + // Defaults so template never shows undefined (avoids raw {$ cpuUsage $} when API is slow or fails) + $scope.cpuUsage = 0; + $scope.ramUsage = 0; + $scope.diskUsage = 0; + $scope.cpuCores = 0; + $scope.ramTotalMB = 0; + $scope.diskTotalGB = 0; + $scope.diskFreeGB = 0; + getStuff(); - + $scope.getSystemStatus = function() { getStuff(); }; @@ -138,17 +146,15 @@ app.controller('systemStatusInfo', function ($scope, $http, $timeout) { function ListInitialData(response) { - $scope.cpuUsage = response.data.cpuUsage; - $scope.ramUsage = response.data.ramUsage; - $scope.diskUsage = response.data.diskUsage; - - // Total system information - $scope.cpuCores = response.data.cpuCores; - $scope.ramTotalMB = response.data.ramTotalMB; - $scope.diskTotalGB = response.data.diskTotalGB; - $scope.diskFreeGB = response.data.diskFreeGB; - - // Get uptime if available + $scope.cpuUsage = response.data.cpuUsage != null ? response.data.cpuUsage : 0; + $scope.ramUsage = response.data.ramUsage != null ? response.data.ramUsage : 0; + $scope.diskUsage = response.data.diskUsage != null ? response.data.diskUsage : 0; + + $scope.cpuCores = response.data.cpuCores != null ? response.data.cpuCores : 0; + $scope.ramTotalMB = response.data.ramTotalMB != null ? response.data.ramTotalMB : 0; + $scope.diskTotalGB = response.data.diskTotalGB != null ? response.data.diskTotalGB : 0; + $scope.diskFreeGB = response.data.diskFreeGB != null ? response.data.diskFreeGB : 0; + if (response.data.uptime) { $scope.uptime = response.data.uptime; $scope.uptimeLoaded = true; @@ -162,6 +168,9 @@ app.controller('systemStatusInfo', function ($scope, $http, $timeout) { function cantLoadInitialData(response) { $scope.uptime = 'Unavailable'; $scope.uptimeLoaded = true; + $scope.cpuUsage = 0; + $scope.ramUsage = 0; + $scope.diskUsage = 0; } $timeout(getStuff, 60000); // Update every minute @@ -902,7 +911,8 @@ app.controller('OnboardingCP', function ($scope, $http, $timeout, $window) { }); -app.controller('dashboardStatsController', function ($scope, $http, $timeout) { +// Single implementation registered under both names for compatibility (some templates/caches use newDashboardStat) +var dashboardStatsControllerFn = function ($scope, $http, $timeout) { console.log('dashboardStatsController initialized'); // Card values @@ -2455,4 +2465,6 @@ app.controller('dashboardStatsController', function ($scope, $http, $timeout) { $scope.closeSSHActivityModal(); } }; -}); \ No newline at end of file +}; +app.controller('dashboardStatsController', dashboardStatsControllerFn); +app.controller('newDashboardStat', dashboardStatsControllerFn); \ No newline at end of file diff --git a/baseTemplate/templates/baseTemplate/index.html b/baseTemplate/templates/baseTemplate/index.html index 0fe34127a..72909a0e2 100644 --- a/baseTemplate/templates/baseTemplate/index.html +++ b/baseTemplate/templates/baseTemplate/index.html @@ -296,38 +296,51 @@ align-items: center; justify-content: center; } + /* Notification dropdown: always light panel with dark text so readable in both light and dark mode */ .notification-center-dropdown { position: absolute; top: calc(100% + 10px); right: 0; - background: white; - border: 1px solid var(--border-color); + background: #ffffff; + border: 1px solid #e5e7eb; border-radius: 16px; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15); width: 520px; max-width: calc(100vw - 40px); max-height: 600px; - overflow-y: auto; - z-index: 10000; display: none; + flex-direction: column; + z-index: 10000; + overflow: hidden; + min-height: 0; } - .notification-center-dropdown.show { display: block; } - .notification-center-header { + .notification-center-dropdown.show { display: flex; } + .notification-center-dropdown .notification-center-header { padding: 1.25rem 1.5rem; - border-bottom: 1px solid var(--border-color); + border-bottom: 1px solid #e5e7eb; display: flex; justify-content: space-between; align-items: center; + flex-shrink: 0; } - .notification-center-header h3 { margin: 0; font-size: 1.25rem; font-weight: 700; } - .notification-center-list { padding: 1rem; } - .notification-center-empty { color: var(--text-secondary); padding: 1rem; } + .notification-center-dropdown .notification-center-header h3 { margin: 0; font-size: 1.25rem; font-weight: 700; color: #111827; } + .notification-center-list { + padding: 1rem; + flex: 1 1 0; + min-height: 0; + max-height: 480px; + overflow-y: auto; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; + } + #notification-center-dropdown .notification-center-empty { color: #4b5563; padding: 1rem; } .notification-center-item { padding: 1.5rem; - border: 1px solid var(--border-color); + border: 1px solid #e5e7eb; border-radius: 12px; margin-bottom: 1rem; - background: white; + background: #ffffff; + overflow: visible; } .notification-center-item.dismissed { opacity: 0.7; background: #f9fafb; } .notification-center-item-title { @@ -336,36 +349,69 @@ display: flex; align-items: center; gap: 0.75rem; + color: #111827; } .notification-center-item-title .dismissed-badge { font-size: 0.75rem; - color: #6b7280; + color: #374151; margin-left: auto; padding: 0.25rem 0.5rem; - background: #f3f4f6; + background: #e5e7eb; border-radius: 6px; } - .notification-center-item-text { color: var(--text-secondary); font-size: 0.95rem; margin-bottom: 1rem; line-height: 1.6; } - .notification-center-item-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; } - .notification-center-item-link { - display: inline-flex; - align-items: center; - gap: 0.5rem; - color: white; - background: linear-gradient(135deg, var(--accent-color) 0%, #6d6bd4 100%); - text-decoration: none; - font-size: 0.9rem; - font-weight: 600; - padding: 0.75rem 1.5rem; - border-radius: 10px; + #notification-center-dropdown .notification-center-item-text { color: #4b5563; font-size: 0.95rem; margin-bottom: 1rem; line-height: 1.6; } + #notification-center-dropdown .notification-center-item-actions { + display: flex; + flex-direction: column; + gap: 1.25rem; + align-items: flex-start; + margin-top: 0.75rem; + width: 100%; } - .notification-center-item-link-secondary { + #notification-center-dropdown .notification-center-item-actions .notification-center-item-link { display: inline-flex; align-items: center; - gap: 0.5rem; - color: var(--accent-color); + justify-content: center; + gap: 0.4rem; + color: #ffffff !important; + background: linear-gradient(135deg, #4f46e5 0%, #4338ca 100%) !important; text-decoration: none; - font-size: 0.9rem; + font-size: 0.875rem; + font-weight: 600; + padding: 0.5rem 1.25rem; + border-radius: 8px; + white-space: nowrap; + flex-shrink: 0; + min-width: 160px; + width: auto; + min-height: 2.25rem; + line-height: 1.3; + box-sizing: border-box; + } + #notification-center-dropdown .notification-center-item-actions .notification-center-item-link:hover { + filter: brightness(1.1); + color: #ffffff !important; + } + #notification-center-dropdown .notification-center-item-actions .notification-center-item-link-secondary { + display: inline-flex; + align-items: center; + gap: 0.4rem; + color: #4338ca !important; + text-decoration: none; + font-size: 0.875rem; + font-weight: 500; + padding: 0.4rem 0.75rem; + white-space: nowrap; + flex-shrink: 0; + min-width: 120px; + width: auto; + min-height: 1.75rem; + line-height: 1.3; + box-sizing: border-box; + } + #notification-center-dropdown .notification-center-item-actions .notification-center-item-link-secondary:hover { + text-decoration: underline; + color: #3730a3 !important; } /* Sidebar */ @@ -2510,6 +2556,10 @@ document.addEventListener('DOMContentLoaded', function() { loadNotificationCenter(); checkBackupStatus(); + // Optional: open notification dropdown for testing (e.g. ?showNotifications=1) + if (window.location.search.indexOf('showNotifications=1') !== -1) { + setTimeout(function() { document.getElementById('notification-center-dropdown').classList.add('show'); }, 400); + } // Show AI Scanner notification with a slight delay for better UX setTimeout(checkAIScannerStatus, 1000); // Show .htaccess notification with additional delay for staggered effect diff --git a/cyberpanel.sh b/cyberpanel.sh index 8632e0935..338293d2e 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -841,29 +841,27 @@ except: fi fi - # Download the working CyberPanel installation files - # Use master3395 fork which has our fixes - # Try to download the actual installer script (install/install.py) from the repository - echo "Downloading from: https://raw.githubusercontent.com/master3395/cyberpanel/v2.5.5-dev/cyberpanel.sh" + # Download the working CyberPanel installation files from upstream (usmannasir/cyberpanel) + echo "Downloading from: https://raw.githubusercontent.com/usmannasir/cyberpanel/v2.5.5-dev/cyberpanel.sh" # First, try to download the repository archive to get the correct installer # GitHub: branch archives use refs/heads/BRANCH; GitHub returns 302 redirect to codeload, so we must use -L local archive_url="" - local installer_url="https://raw.githubusercontent.com/master3395/cyberpanel/v2.5.5-dev/cyberpanel.sh" - if curl -s -L --head "https://github.com/master3395/cyberpanel/archive/refs/heads/v2.5.5-dev.tar.gz" | grep -q "200 OK"; then - archive_url="https://github.com/master3395/cyberpanel/archive/refs/heads/v2.5.5-dev.tar.gz" - echo " Using development branch (v2.5.5-dev) from master3395/cyberpanel" - elif curl -s -L --head "https://github.com/master3395/cyberpanel/archive/v2.5.5-dev.tar.gz" | grep -q "200 OK"; then - archive_url="https://github.com/master3395/cyberpanel/archive/v2.5.5-dev.tar.gz" - echo " Using development branch (v2.5.5-dev) from master3395/cyberpanel" + local installer_url="https://raw.githubusercontent.com/usmannasir/cyberpanel/v2.5.5-dev/cyberpanel.sh" + if curl -s -L --head "https://github.com/usmannasir/cyberpanel/archive/refs/heads/v2.5.5-dev.tar.gz" | grep -q "200 OK"; then + archive_url="https://github.com/usmannasir/cyberpanel/archive/refs/heads/v2.5.5-dev.tar.gz" + echo " Using development branch (v2.5.5-dev) from usmannasir/cyberpanel" + elif curl -s -L --head "https://github.com/usmannasir/cyberpanel/archive/v2.5.5-dev.tar.gz" | grep -q "200 OK"; then + archive_url="https://github.com/usmannasir/cyberpanel/archive/v2.5.5-dev.tar.gz" + echo " Using development branch (v2.5.5-dev) from usmannasir/cyberpanel" else echo " Development branch archive not available, trying installer script directly..." if ! curl -s -L --head "$installer_url" | grep -q "200 OK"; then echo " Development branch not available, falling back to stable" - installer_url="https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel.sh" - archive_url="https://github.com/master3395/cyberpanel/archive/stable.tar.gz" + installer_url="https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/cyberpanel.sh" + archive_url="https://github.com/usmannasir/cyberpanel/archive/stable.tar.gz" else - archive_url="https://github.com/master3395/cyberpanel/archive/refs/heads/v2.5.5-dev.tar.gz" + archive_url="https://github.com/usmannasir/cyberpanel/archive/refs/heads/v2.5.5-dev.tar.gz" fi fi @@ -884,8 +882,8 @@ except: # Download the install directory (use archive_url set above; may be branch or stable) echo "Downloading installation files..." - if [ -z "$archive_url" ] || [ "$installer_url" = "https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel.sh" ]; then - archive_url="https://github.com/master3395/cyberpanel/archive/stable.tar.gz" + if [ -z "$archive_url" ] || [ "$installer_url" = "https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/cyberpanel.sh" ]; then + archive_url="https://github.com/usmannasir/cyberpanel/archive/stable.tar.gz" fi # Append cache-bust so CDNs/proxies don't serve old installer (GitHub ignores query params) archive_url="${archive_url}?nocache=$(date +%s 2>/dev/null || echo 0)" @@ -904,7 +902,7 @@ except: fi # Copy install directory to current location - if [ "$installer_url" = "https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel.sh" ]; then + if [ "$installer_url" = "https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/cyberpanel.sh" ]; then if [ -d "cyberpanel-stable" ]; then cp -r cyberpanel-stable/install . 2>/dev/null || true cp -r cyberpanel-stable/install.sh . 2>/dev/null || true diff --git a/cyberpanel_upgrade.sh b/cyberpanel_upgrade.sh index 2527c3f0b..663eb6710 100644 --- a/cyberpanel_upgrade.sh +++ b/cyberpanel_upgrade.sh @@ -15,7 +15,7 @@ if [[ $(id -u) -ne 0 ]] 2>/dev/null; then echo "" echo "This script must be run as root." - echo "Run: sudo bash <(curl -sL https://raw.githubusercontent.com/master3395/cyberpanel/stable/cyberpanel_upgrade.sh) -b v2.5.5-dev" + echo "Run: sudo bash <(curl -sL https://raw.githubusercontent.com/usmannasir/cyberpanel/stable/cyberpanel_upgrade.sh) -b v2.5.5-dev" echo "Or: sudo su - then run the same command without sudo" echo "" exit 1 @@ -1395,6 +1395,50 @@ fi echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] Main_Upgrade function completed" | tee -a /var/log/cyberpanel_upgrade_debug.log } +# Sync /usr/local/CyberCP to the latest commit of the upgrade branch so Version Management +# page shows Current commit matching Latest (avoids "please upgrade" when upgrade already ran). +# Backs up and restores CyberCP/settings.py so production DB/config are not overwritten by the repo. +Sync_CyberCP_To_Latest() { + if [[ ! -d /usr/local/CyberCP/.git ]]; then + echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] No .git in /usr/local/CyberCP, skipping sync" | tee -a /var/log/cyberpanel_upgrade_debug.log + return 0 + fi + echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] Syncing /usr/local/CyberCP to latest commit for branch: $Branch_Name" | tee -a /var/log/cyberpanel_upgrade_debug.log + # Backup production settings so sync does not overwrite DB credentials / local config + if [[ -f /usr/local/CyberCP/CyberCP/settings.py ]]; then + cp /usr/local/CyberCP/CyberCP/settings.py /tmp/cyberpanel_settings_backup.py + echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] Backed up settings.py for restore after sync" | tee -a /var/log/cyberpanel_upgrade_debug.log + fi + ( + cd /usr/local/CyberCP + git fetch origin 2>&1 | tee -a /var/log/cyberpanel_upgrade_debug.log + if git show-ref -q "refs/remotes/origin/$Branch_Name"; then + git checkout -B "$Branch_Name" "origin/$Branch_Name" 2>&1 | tee -a /var/log/cyberpanel_upgrade_debug.log + else + git checkout "$Branch_Name" 2>/dev/null || true + git pull --ff-only origin "$Branch_Name" 2>&1 | tee -a /var/log/cyberpanel_upgrade_debug.log || true + fi + ) + local sync_code=$? + # Restore production settings so panel keeps working (DB, secrets, etc.) + if [[ -f /tmp/cyberpanel_settings_backup.py ]]; then + cp /tmp/cyberpanel_settings_backup.py /usr/local/CyberCP/CyberCP/settings.py + echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] Restored settings.py after sync" | tee -a /var/log/cyberpanel_upgrade_debug.log + fi + # LiteSpeed serves /static/ from public/static/; ensure it has latest baseTemplate static files (e.g. dashboard JS) + if [[ -d /usr/local/CyberCP/public/static ]] && [[ -d /usr/local/CyberCP/baseTemplate/static/baseTemplate ]]; then + rsync -a /usr/local/CyberCP/baseTemplate/static/baseTemplate/ /usr/local/CyberCP/public/static/baseTemplate/ 2>/dev/null || \ + cp -r /usr/local/CyberCP/baseTemplate/static/baseTemplate/* /usr/local/CyberCP/public/static/baseTemplate/ 2>/dev/null || true + echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] Synced baseTemplate static to public/static" | tee -a /var/log/cyberpanel_upgrade_debug.log + fi + if [[ $sync_code -eq 0 ]]; then + echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] Sync completed. Current HEAD: $(git -C /usr/local/CyberCP rev-parse HEAD 2>/dev/null || echo 'unknown')" | tee -a /var/log/cyberpanel_upgrade_debug.log + else + echo -e "[$(date +"%Y-%m-%d %H:%M:%S")] Sync returned code $sync_code (non-fatal)" | tee -a /var/log/cyberpanel_upgrade_debug.log + fi + return 0 +} + Post_Upgrade_System_Tweak() { if [[ "$Server_OS" = "CentOS" ]] ; then @@ -1816,6 +1860,7 @@ echo -e " 1. Access your CyberPanel at the URL above" echo -e " 2. Change the default admin password" echo -e " 3. Configure your domains and websites" echo -e " 4. Check system status in the dashboard" +echo -e " 5. Check DB version with: mariadb -V (use mariadb, not mysql, to avoid deprecation warning)" echo -e "\n🧹 Cleaning up temporary files..." rm -rf /root/cyberpanel_upgrade_tmp @@ -1883,6 +1928,8 @@ Pre_Upgrade_Required_Components Main_Upgrade +Sync_CyberCP_To_Latest + Post_Upgrade_System_Tweak Post_Install_Display_Final_Info diff --git a/databases/templates/databases/AutoLogin.html b/databases/templates/databases/AutoLogin.html index f8a897fb4..27ce557bd 100644 --- a/databases/templates/databases/AutoLogin.html +++ b/databases/templates/databases/AutoLogin.html @@ -10,6 +10,8 @@