mirror of
				https://github.com/usmannasir/cyberpanel.git
				synced 2025-10-31 02:15:55 +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" /> | ||||
|   </component> | ||||
|   <component name="ChangeListManager"> | ||||
|     <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> | ||||
|     <list default="true" id="5251c5c9-f2a1-41f2-bc76-10b517091df1" name="Changes" comment="" /> | ||||
|     <option name="SHOW_DIALOG" value="false" /> | ||||
|     <option name="HIGHLIGHT_CONFLICTS" value="true" /> | ||||
|     <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> | ||||
|   | ||||
| @@ -2768,6 +2768,38 @@ app.controller('listWebsites', function ($scope, $http, $window) { | ||||
|         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 | ||||
|     $scope.getFurtherWebsitesFromDB = function () { | ||||
|         $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; | ||||
|     }; | ||||
|  | ||||
|     // 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 | ||||
|     $scope.getFurtherWebsitesFromDB = function () { | ||||
|         $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; | ||||
|     }; | ||||
|  | ||||
|     // 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 | ||||
|     $scope.getFurtherWebsitesFromDB = function () { | ||||
|         $scope.loading = true; // Set loading to true when starting fetch | ||||
|   | ||||
| @@ -240,6 +240,53 @@ | ||||
|             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 { | ||||
|             color: #5b5fcf; | ||||
|             margin-left: 8px; | ||||
| @@ -667,6 +714,25 @@ | ||||
|                                 <span ng-if="web.loading" class="loading-indicator"> | ||||
|                                     <i class="fa fa-spinner fa-spin"></i> | ||||
|                                 </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 class="row-actions"> | ||||
|                                 <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 | ||||
|             state = "Active" if website.state == 1 else "Suspended" | ||||
|  | ||||
|             # Get SSL status | ||||
|             ssl_status = self.getSSLStatus(website.domain) | ||||
|  | ||||
|             json_data.append({ | ||||
|                 'domain': website.domain, | ||||
|                 'adminEmail': website.adminEmail, | ||||
| @@ -2543,10 +2546,107 @@ Require valid-user | ||||
|                 'package': website.package.packageName, | ||||
|                 'admin': website.admin.userName, | ||||
|                 'wp_sites': wp_sites, | ||||
|                 'diskUsed': diskUsed | ||||
|                 'diskUsed': diskUsed, | ||||
|                 'ssl': ssl_status | ||||
|             }) | ||||
|         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): | ||||
|   | ||||
		Reference in New Issue
	
	Block a user