mirror of
				https://github.com/usmannasir/cyberpanel.git
				synced 2025-10-31 10:26:01 +01:00 
			
		
		
		
	feature: ssl status in list websites
This commit is contained in:
		
							
								
								
									
										4
									
								
								.idea/workspace.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								.idea/workspace.xml
									
									
									
										generated
									
									
									
								
							| @@ -4,9 +4,7 @@ | |||||||
|     <option name="autoReloadType" value="SELECTIVE" /> |     <option name="autoReloadType" value="SELECTIVE" /> | ||||||
|   </component> |   </component> | ||||||
|   <component name="ChangeListManager"> |   <component name="ChangeListManager"> | ||||||
|     <list default="true" id="5251c5c9-f2a1-41f2-bc76-10b517091df1" name="Changes" comment=""> |     <list default="true" id="5251c5c9-f2a1-41f2-bc76-10b517091df1" name="Changes" comment="" /> | ||||||
|       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> |  | ||||||
|     </list> |  | ||||||
|     <option name="SHOW_DIALOG" value="false" /> |     <option name="SHOW_DIALOG" value="false" /> | ||||||
|     <option name="HIGHLIGHT_CONFLICTS" value="true" /> |     <option name="HIGHLIGHT_CONFLICTS" value="true" /> | ||||||
|     <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> |     <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> | ||||||
|   | |||||||
| @@ -2768,6 +2768,38 @@ app.controller('listWebsites', function ($scope, $http, $window) { | |||||||
|         return site.version !== undefined; |         return site.version !== undefined; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  |     // Function to get SSL tooltip text | ||||||
|  |     $scope.getSslTooltip = function(web) { | ||||||
|  |         if (!web.ssl) return ''; | ||||||
|  |          | ||||||
|  |         var tooltip = ''; | ||||||
|  |         if (web.ssl.issuer && web.ssl.issuer !== '') { | ||||||
|  |             tooltip += 'Issuer: ' + web.ssl.issuer; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (web.ssl.days !== undefined) { | ||||||
|  |             if (tooltip) tooltip += ' | '; | ||||||
|  |             if (web.ssl.days < 0) { | ||||||
|  |                 tooltip += 'Expired ' + Math.abs(web.ssl.days) + ' days ago'; | ||||||
|  |             } else { | ||||||
|  |                 tooltip += 'Valid for ' + web.ssl.days + ' days'; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (web.ssl.is_wildcard) { | ||||||
|  |             if (tooltip) tooltip += ' | '; | ||||||
|  |             tooltip += 'Wildcard Certificate'; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (web.ssl.status === 'none') { | ||||||
|  |             tooltip = 'No SSL certificate installed. Click "Issue SSL" to secure this site.'; | ||||||
|  |         } else if (web.ssl.status === 'self-signed') { | ||||||
|  |             tooltip = 'Self-signed certificate detected. Not trusted by browsers.'; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         return tooltip; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     // Initial fetch of websites |     // Initial fetch of websites | ||||||
|     $scope.getFurtherWebsitesFromDB = function () { |     $scope.getFurtherWebsitesFromDB = function () { | ||||||
|         $scope.loading = true; // Set loading to true when starting fetch |         $scope.loading = true; // Set loading to true when starting fetch | ||||||
| @@ -6057,6 +6089,38 @@ app.controller('listWebsites', function ($scope, $http, $window) { | |||||||
|         return site.version !== undefined; |         return site.version !== undefined; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  |     // Function to get SSL tooltip text | ||||||
|  |     $scope.getSslTooltip = function(web) { | ||||||
|  |         if (!web.ssl) return ''; | ||||||
|  |          | ||||||
|  |         var tooltip = ''; | ||||||
|  |         if (web.ssl.issuer && web.ssl.issuer !== '') { | ||||||
|  |             tooltip += 'Issuer: ' + web.ssl.issuer; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (web.ssl.days !== undefined) { | ||||||
|  |             if (tooltip) tooltip += ' | '; | ||||||
|  |             if (web.ssl.days < 0) { | ||||||
|  |                 tooltip += 'Expired ' + Math.abs(web.ssl.days) + ' days ago'; | ||||||
|  |             } else { | ||||||
|  |                 tooltip += 'Valid for ' + web.ssl.days + ' days'; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (web.ssl.is_wildcard) { | ||||||
|  |             if (tooltip) tooltip += ' | '; | ||||||
|  |             tooltip += 'Wildcard Certificate'; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (web.ssl.status === 'none') { | ||||||
|  |             tooltip = 'No SSL certificate installed. Click "Issue SSL" to secure this site.'; | ||||||
|  |         } else if (web.ssl.status === 'self-signed') { | ||||||
|  |             tooltip = 'Self-signed certificate detected. Not trusted by browsers.'; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         return tooltip; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     // Initial fetch of websites |     // Initial fetch of websites | ||||||
|     $scope.getFurtherWebsitesFromDB = function () { |     $scope.getFurtherWebsitesFromDB = function () { | ||||||
|         $scope.loading = true; // Set loading to true when starting fetch |         $scope.loading = true; // Set loading to true when starting fetch | ||||||
| @@ -9690,6 +9754,38 @@ app.controller('listWebsites', function ($scope, $http, $window) { | |||||||
|         return site.version !== undefined; |         return site.version !== undefined; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  |     // Function to get SSL tooltip text | ||||||
|  |     $scope.getSslTooltip = function(web) { | ||||||
|  |         if (!web.ssl) return ''; | ||||||
|  |          | ||||||
|  |         var tooltip = ''; | ||||||
|  |         if (web.ssl.issuer && web.ssl.issuer !== '') { | ||||||
|  |             tooltip += 'Issuer: ' + web.ssl.issuer; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (web.ssl.days !== undefined) { | ||||||
|  |             if (tooltip) tooltip += ' | '; | ||||||
|  |             if (web.ssl.days < 0) { | ||||||
|  |                 tooltip += 'Expired ' + Math.abs(web.ssl.days) + ' days ago'; | ||||||
|  |             } else { | ||||||
|  |                 tooltip += 'Valid for ' + web.ssl.days + ' days'; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (web.ssl.is_wildcard) { | ||||||
|  |             if (tooltip) tooltip += ' | '; | ||||||
|  |             tooltip += 'Wildcard Certificate'; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (web.ssl.status === 'none') { | ||||||
|  |             tooltip = 'No SSL certificate installed. Click "Issue SSL" to secure this site.'; | ||||||
|  |         } else if (web.ssl.status === 'self-signed') { | ||||||
|  |             tooltip = 'Self-signed certificate detected. Not trusted by browsers.'; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         return tooltip; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     // Initial fetch of websites |     // Initial fetch of websites | ||||||
|     $scope.getFurtherWebsitesFromDB = function () { |     $scope.getFurtherWebsitesFromDB = function () { | ||||||
|         $scope.loading = true; // Set loading to true when starting fetch |         $scope.loading = true; // Set loading to true when starting fetch | ||||||
|   | |||||||
| @@ -240,6 +240,53 @@ | |||||||
|             white-space: nowrap; |             white-space: nowrap; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         /* SSL Status Badge */ | ||||||
|  |         .ssl-badge { | ||||||
|  |             display: inline-flex; | ||||||
|  |             align-items: center; | ||||||
|  |             padding: 4px 10px; | ||||||
|  |             font-size: 11px; | ||||||
|  |             font-weight: 600; | ||||||
|  |             border-radius: 20px; | ||||||
|  |             gap: 5px; | ||||||
|  |             text-transform: uppercase; | ||||||
|  |             letter-spacing: 0.5px; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         .ssl-badge.valid { | ||||||
|  |             background: #f0fdf4; | ||||||
|  |             color: #10b981; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         .ssl-badge.warning { | ||||||
|  |             background: #fef3c7; | ||||||
|  |             color: #f59e0b; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         .ssl-badge.expiring, | ||||||
|  |         .ssl-badge.expired { | ||||||
|  |             background: #fee2e2; | ||||||
|  |             color: #ef4444; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         .ssl-badge.self-signed { | ||||||
|  |             background: #fef3c7; | ||||||
|  |             color: #f59e0b; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         .ssl-badge.none { | ||||||
|  |             background: #f3f4f6; | ||||||
|  |             color: #9ca3af; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         .ssl-badge .wildcard-indicator { | ||||||
|  |             background: rgba(255, 255, 255, 0.3); | ||||||
|  |             padding: 1px 4px; | ||||||
|  |             border-radius: 8px; | ||||||
|  |             font-size: 10px; | ||||||
|  |             margin-left: 2px; | ||||||
|  |         } | ||||||
|  |          | ||||||
|         .loading-indicator { |         .loading-indicator { | ||||||
|             color: #5b5fcf; |             color: #5b5fcf; | ||||||
|             margin-left: 8px; |             margin-left: 8px; | ||||||
| @@ -667,6 +714,25 @@ | |||||||
|                                 <span ng-if="web.loading" class="loading-indicator"> |                                 <span ng-if="web.loading" class="loading-indicator"> | ||||||
|                                     <i class="fa fa-spinner fa-spin"></i> |                                     <i class="fa fa-spinner fa-spin"></i> | ||||||
|                                 </span> |                                 </span> | ||||||
|  |                                 <!-- SSL Status Badge --> | ||||||
|  |                                 <span ng-if="web.ssl" class="ssl-badge" ng-class="web.ssl.status"  | ||||||
|  |                                       data-toggle="tooltip"  | ||||||
|  |                                       data-placement="top" | ||||||
|  |                                       title="{$ getSslTooltip(web) $}"> | ||||||
|  |                                     <i class="fas" ng-class="{ | ||||||
|  |                                         'fa-lock': web.ssl.status === 'valid', | ||||||
|  |                                         'fa-exclamation-triangle': web.ssl.status === 'warning' || web.ssl.status === 'self-signed', | ||||||
|  |                                         'fa-exclamation-circle': web.ssl.status === 'expiring' || web.ssl.status === 'expired', | ||||||
|  |                                         'fa-unlock': web.ssl.status === 'none' | ||||||
|  |                                     }"></i> | ||||||
|  |                                     <span ng-if="web.ssl.status === 'valid'">Secure</span> | ||||||
|  |                                     <span ng-if="web.ssl.status === 'warning'">SSL {$ web.ssl.days $}d</span> | ||||||
|  |                                     <span ng-if="web.ssl.status === 'expiring'">Expiring {$ web.ssl.days $}d</span> | ||||||
|  |                                     <span ng-if="web.ssl.status === 'expired'">Expired</span> | ||||||
|  |                                     <span ng-if="web.ssl.status === 'self-signed'">Self-Signed</span> | ||||||
|  |                                     <span ng-if="web.ssl.status === 'none'">No SSL</span> | ||||||
|  |                                     <span ng-if="web.ssl.is_wildcard" class="wildcard-indicator" title="Wildcard SSL Certificate">*</span> | ||||||
|  |                                 </span> | ||||||
|                             </div> |                             </div> | ||||||
|                             <div class="row-actions"> |                             <div class="row-actions"> | ||||||
|                                 <a href="/websites/{$ web.domain $}" class="btn btn-primary btn-sm" title="{% trans 'Manage' %}"> |                                 <a href="/websites/{$ web.domain $}" class="btn btn-primary btn-sm" title="{% trans 'Manage' %}"> | ||||||
|   | |||||||
| @@ -2534,6 +2534,9 @@ Require valid-user | |||||||
|             # Convert numeric state to text |             # Convert numeric state to text | ||||||
|             state = "Active" if website.state == 1 else "Suspended" |             state = "Active" if website.state == 1 else "Suspended" | ||||||
|  |  | ||||||
|  |             # Get SSL status | ||||||
|  |             ssl_status = self.getSSLStatus(website.domain) | ||||||
|  |  | ||||||
|             json_data.append({ |             json_data.append({ | ||||||
|                 'domain': website.domain, |                 'domain': website.domain, | ||||||
|                 'adminEmail': website.adminEmail, |                 'adminEmail': website.adminEmail, | ||||||
| @@ -2543,10 +2546,107 @@ Require valid-user | |||||||
|                 'package': website.package.packageName, |                 'package': website.package.packageName, | ||||||
|                 'admin': website.admin.userName, |                 'admin': website.admin.userName, | ||||||
|                 'wp_sites': wp_sites, |                 'wp_sites': wp_sites, | ||||||
|                 'diskUsed': diskUsed |                 'diskUsed': diskUsed, | ||||||
|  |                 'ssl': ssl_status | ||||||
|             }) |             }) | ||||||
|         return json.dumps(json_data) |         return json.dumps(json_data) | ||||||
|  |  | ||||||
|  |     def getSSLStatus(self, domain): | ||||||
|  |         """Get SSL status for a domain""" | ||||||
|  |         try: | ||||||
|  |             import OpenSSL | ||||||
|  |             from datetime import datetime | ||||||
|  |              | ||||||
|  |             # Check main domain certificate | ||||||
|  |             filePath = '/etc/letsencrypt/live/%s/fullchain.pem' % domain | ||||||
|  |              | ||||||
|  |             if not os.path.exists(filePath): | ||||||
|  |                 # Check for wildcard certificate in parent domain | ||||||
|  |                 parts = domain.split('.') | ||||||
|  |                 if len(parts) > 2:  # Subdomain like mail.example.com or ftp.example.com | ||||||
|  |                     parent_domain = '.'.join(parts[-2:]) | ||||||
|  |                     wildcard_path = '/etc/letsencrypt/live/%s/fullchain.pem' % parent_domain | ||||||
|  |                     if os.path.exists(wildcard_path): | ||||||
|  |                         # Check if it's actually a wildcard cert | ||||||
|  |                         try: | ||||||
|  |                             x509 = OpenSSL.crypto.load_certificate( | ||||||
|  |                                 OpenSSL.crypto.FILETYPE_PEM, | ||||||
|  |                                 open(wildcard_path, 'r').read() | ||||||
|  |                             ) | ||||||
|  |                             cn = None | ||||||
|  |                             for component in x509.get_subject().get_components(): | ||||||
|  |                                 if component[0].decode('utf-8') == 'CN': | ||||||
|  |                                     cn = component[1].decode('utf-8') | ||||||
|  |                                     break | ||||||
|  |                              | ||||||
|  |                             if cn and cn.startswith('*.'): | ||||||
|  |                                 filePath = wildcard_path | ||||||
|  |                                 is_wildcard = True | ||||||
|  |                             else: | ||||||
|  |                                 return {'status': 'none', 'days': 0, 'issuer': '', 'is_wildcard': False} | ||||||
|  |                         except: | ||||||
|  |                             return {'status': 'none', 'days': 0, 'issuer': '', 'is_wildcard': False} | ||||||
|  |                     else: | ||||||
|  |                         return {'status': 'none', 'days': 0, 'issuer': '', 'is_wildcard': False} | ||||||
|  |                 else: | ||||||
|  |                     return {'status': 'none', 'days': 0, 'issuer': '', 'is_wildcard': False} | ||||||
|  |             else: | ||||||
|  |                 is_wildcard = False | ||||||
|  |              | ||||||
|  |             # Load and analyze certificate | ||||||
|  |             x509 = OpenSSL.crypto.load_certificate( | ||||||
|  |                 OpenSSL.crypto.FILETYPE_PEM, | ||||||
|  |                 open(filePath, 'r').read() | ||||||
|  |             ) | ||||||
|  |              | ||||||
|  |             # Get expiration date | ||||||
|  |             expireData = x509.get_notAfter().decode('ascii') | ||||||
|  |             finalDate = datetime.strptime(expireData, '%Y%m%d%H%M%SZ') | ||||||
|  |             now = datetime.now() | ||||||
|  |             diff = finalDate - now | ||||||
|  |             days = diff.days | ||||||
|  |              | ||||||
|  |             # Get issuer | ||||||
|  |             issuer_org = None | ||||||
|  |             for component in x509.get_issuer().get_components(): | ||||||
|  |                 if component[0].decode('utf-8') == 'O': | ||||||
|  |                     issuer_org = component[1].decode('utf-8') | ||||||
|  |                     break | ||||||
|  |              | ||||||
|  |             if not issuer_org: | ||||||
|  |                 issuer_org = 'Unknown' | ||||||
|  |              | ||||||
|  |             # Check if it's a wildcard certificate | ||||||
|  |             if not is_wildcard: | ||||||
|  |                 cn = None | ||||||
|  |                 for component in x509.get_subject().get_components(): | ||||||
|  |                     if component[0].decode('utf-8') == 'CN': | ||||||
|  |                         cn = component[1].decode('utf-8') | ||||||
|  |                         break | ||||||
|  |                 if cn and cn.startswith('*.'): | ||||||
|  |                     is_wildcard = True | ||||||
|  |              | ||||||
|  |             # Determine status | ||||||
|  |             if issuer_org == 'Denial': | ||||||
|  |                 status = 'self-signed' | ||||||
|  |             elif days < 0: | ||||||
|  |                 status = 'expired' | ||||||
|  |             elif days <= 7: | ||||||
|  |                 status = 'expiring' | ||||||
|  |             elif days <= 30: | ||||||
|  |                 status = 'warning' | ||||||
|  |             else: | ||||||
|  |                 status = 'valid' | ||||||
|  |              | ||||||
|  |             return { | ||||||
|  |                 'status': status, | ||||||
|  |                 'days': days, | ||||||
|  |                 'issuer': issuer_org, | ||||||
|  |                 'is_wildcard': is_wildcard | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |         except Exception as e: | ||||||
|  |             return {'status': 'none', 'days': 0, 'issuer': '', 'is_wildcard': False} | ||||||
|  |  | ||||||
|  |  | ||||||
|     def findDockersitesListJson(self, Dockersite): |     def findDockersitesListJson(self, Dockersite): | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user