This commit is contained in:
usmannasir
2025-05-18 13:30:29 +05:00
27 changed files with 4622 additions and 1649 deletions

View File

@@ -1,60 +1,32 @@
:root { #logo {
/* Modern color palette */ width: 200px;
--primary-color: #4f46e5; height: auto;
--primary-hover: #4338ca;
--secondary-color: #6b7280;
--background-light: #f9fafb;
--background-lighter: #ffffff;
--text-primary: #111827;
--text-secondary: #4b5563;
--border-color: #e5e7eb;
--success-color: #10b981;
--danger-color: #ef4444;
--warning-color: #f59e0b;
--hover-bg: #f3f4f6;
--logo-bg: #f8fafc;
/* Enhanced shadows */
--card-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--card-shadow-hover: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--button-shadow: 0 2px 4px 0 rgb(0 0 0 / 0.05);
--button-shadow-hover: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
/* Modern transitions */
--transition-base: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
--transition-smooth: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
--transition-bounce: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
} }
body { /*#navBar{
color: var(--text-primary); background: -moz-linear-gradient(#a4dbf5, #8cc5e0);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background: -webkit-linear-gradient(#a4dbf5, #8cc5e0);
background-color: var(--background-light); background: -o-linear-gradient(#a4dbf5, #8cc5e0);
line-height: 1.6; }*/
}
/* Enhanced Navbar Styling */
#navBar { #navBar {
background: var(--background-lighter); background: linear-gradient(135deg, #4158D0 0%, #C850C0 46%, #FFCC70 100%);
box-shadow: var(--card-shadow); box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 1.25rem; padding: 0.5rem 0;
border-bottom: 1px solid var(--border-color); }
position: sticky; .navbar-brand {
top: 0; margin: 0 1rem;
z-index: 100; color: #fff !important;
backdrop-filter: blur(12px); font-weight: 500;
-webkit-backdrop-filter: blur(12px); }
#mainRow {
margin: 2rem 1.5rem;
} }
/* Modern Header Logo */ #tableHead {
.header-logo { background: #f8f9fa;
display: flex; color: #2c3e50;
align-items: center; font-weight: 500;
gap: 1rem; border-radius: 8px 8px 0 0;
padding: 0.875rem 1.25rem;
background: var(--logo-bg);
border-radius: 1.25rem;
transition: var(--transition-bounce);
} }
.header-logo:hover { .header-logo:hover {
@@ -68,11 +40,24 @@ body {
filter: brightness(0.8) contrast(1.2); filter: brightness(0.8) contrast(1.2);
} }
.header-logo a { .my-drop-zone {
display: flex; border: 2px dashed #cbd5e0;
align-items: center; border-radius: 8px;
gap: 0.75rem; padding: 2rem;
text-decoration: none; background: #f7fafc;
transition: all 0.3s ease;
margin-bottom: 2%;
}
.my-drop-zone:hover {
border-color: #4158D0;
background: #f0f5ff;
}
#queueProg {
margin-bottom: 2%;
height: 6px;
border-radius: 3px;
} }
.header-logo span { .header-logo span {
@@ -291,20 +276,14 @@ body {
border-radius: 1rem 1rem 0 0; border-radius: 1rem 1rem 0 0;
} }
.path-nav-item { a.nav-link {
display: inline-flex; color: rgba(255,255,255,0.9) !important;
align-items: center;
padding: 0.5rem 1rem;
font-size: 0.875rem;
color: var(--text-secondary);
border-radius: 0.75rem;
transition: var(--transition-bounce);
font-weight: 500; font-weight: 500;
padding: 0.5rem 1rem;
transition: all 0.2s ease;
} }
a.nav-link:hover {
.path-nav-item:hover { color: #ffffff !important;
color: var(--primary-color);
background: var(--hover-bg);
transform: translateY(-1px); transform: translateY(-1px);
} }
@@ -344,267 +323,142 @@ i.fa.fa-folder {
font-size: 1.25rem; font-size: 1.25rem;
transition: var(--transition-smooth); transition: var(--transition-smooth);
} }
.form-control {
i.fa.fa-file {
color: var(--secondary-color) !important;
margin-right: 0.75rem;
font-size: 1.25rem;
transition: var(--transition-smooth);
}
/* Modern Permissions Display */
.permissions {
font-family: 'SF Mono', 'Roboto Mono', monospace;
font-size: 0.875rem;
color: var(--text-secondary);
background: var(--background-light);
padding: 0.375rem 0.75rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
}
/* Enhanced Navigation Actions */
.nav-actions {
display: flex;
align-items: center;
gap: 1rem;
padding: 1.25rem;
background: var(--background-lighter);
border-bottom: 1.5px solid var(--border-color);
border-radius: 1.25rem 1.25rem 0 0;
}
.nav-action {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.75rem 1.5rem;
color: var(--text-secondary);
font-size: 0.875rem;
text-decoration: none;
border-radius: 0.75rem;
transition: var(--transition-bounce);
border: 1.5px solid var(--border-color);
background: var(--background-lighter);
min-width: 130px;
font-weight: 600;
box-shadow: var(--button-shadow);
}
.nav-action:hover {
color: var(--primary-color);
background: var(--background-lighter);
border-color: var(--primary-color);
transform: translateY(-2px);
box-shadow: var(--button-shadow-hover);
}
.nav-action i {
margin-right: 0.5rem;
font-size: 1rem;
}
.nav-action.home-action {
color: var(--primary-color);
background: var(--background-lighter);
border-color: var(--primary-color);
}
.nav-action.home-action:hover {
background: rgba(37, 99, 235, 0.1);
}
.nav-action.back-action i,
.nav-action.refresh-action i {
margin-right: 0.25rem;
}
.nav-action.select-action,
.nav-action.unselect-action {
background: var(--background-light);
border: 1px solid var(--border-color);
}
.nav-action.select-action:hover,
.nav-action.unselect-action:hover {
background: var(--hover-bg);
border-color: var(--primary-color);
color: var(--primary-color);
}
/* File Table Header */
.file-table-header {
display: grid;
grid-template-columns: minmax(200px, 2fr) minmax(100px, 1fr) minmax(150px, 1fr) minmax(100px, 1fr);
gap: 1rem;
padding: 0.75rem 1rem;
background: var(--background-light);
border-bottom: 1px solid var(--border-color);
}
.file-table-header th {
color: var(--text-secondary);
font-weight: 600;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* File Table Row */
.file-table-row {
display: grid;
grid-template-columns: minmax(200px, 2fr) minmax(100px, 1fr) minmax(150px, 1fr) minmax(100px, 1fr);
gap: 1rem;
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--border-color);
transition: background-color 0.15s ease;
}
.file-table-row:hover {
background-color: var(--hover-bg);
}
.file-name-cell {
display: flex;
align-items: center;
gap: 0.5rem;
}
.file-icon {
color: var(--warning-color);
font-size: 1rem;
}
.file-name {
color: var(--text-primary);
font-size: 0.875rem;
}
.file-size,
.file-date,
.file-permissions {
color: var(--text-secondary);
font-size: 0.875rem;
}
.file-permissions {
font-family: monospace;
}
/* Responsive Enhancements */
@media (max-width: 768px) {
.nav-item .nav-link {
padding: 0.625rem 1rem;
}
#treeView {
height: auto;
max-height: 350px;
}
.table td, .table th {
padding: 1rem;
}
.file-actions {
opacity: 1;
}
.nav-action {
min-width: auto;
padding: 0.625rem 1rem;
}
}
/* File Manager Navigation */
.file-manager-nav {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
background: var(--background-lighter);
border-bottom: 1px solid var(--border-color);
}
.file-manager-nav a {
display: inline-flex;
align-items: center;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
border: 1px solid #e2e8f0;
border-radius: 6px;
color: #4a5568;
font-size: 0.95rem;
transition: all 0.3s ease;
}
.form-control:focus {
border-color: #4158D0;
box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.15);
}
.form-control[readonly] {
background-color: #f8f9fa;
}
a {
color: #4158D0;
transition: color 0.2s ease;
}
a:hover {
color: #C850C0;
}
.table {
background: white;
border-radius: 8px;
box-shadow: 0 0 20px rgba(0,0,0,0.05);
margin-bottom: 2rem;
}
.table td, .table th {
padding: .75rem;
vertical-align: middle;
border-top: 1px solid #edf2f7;
}
.table thead th {
border-bottom: 2px solid #edf2f7;
font-weight: 600;
font-size: 0.875rem; font-size: 0.875rem;
color: var(--text-secondary); text-transform: uppercase;
text-decoration: none; letter-spacing: 0.5px;
border: 1px solid var(--border-color);
border-radius: 0.375rem;
transition: all 0.15s ease;
background: var(--background-lighter);
} }
.file-manager-nav a:hover { .table td {
color: var(--primary-color); font-size: 0.9rem;
border-color: var(--primary-color); color: #4a5568;
background: var(--hover-bg); }
.list-group-item {
padding: 0.75rem 1.25rem;
border: 1px solid #edf2f7;
margin-bottom: -1px;
background-color: white;
transition: all 0.2s ease;
}
.list-group-item:hover {
background-color: #f7fafc;
} }
.file-manager-nav a i { i.fa.fa-file,
margin-right: 0.5rem; i.fa.fa-minus,
i.fa.fa-plus {
color: #4158D0 !important;
}
.bg-lightgray {
background: #f8f9fa;
border-radius: 8px;
} }
.file-manager-nav a.home-link { .progress-bar {
color: var(--primary-color); background: linear-gradient(135deg, #4158D0 0%, #C850C0 100%);
border-color: var(--primary-color);
} }
.file-manager-nav a.home-link:hover { /* Card styles */
background: rgba(37, 99, 235, 0.1); .card {
border: none;
border-radius: 8px;
box-shadow: 0 0 20px rgba(0,0,0,0.05);
} }
/* Table Header */ .card-header {
.table-header { background: #f8f9fa;
display: grid; border-bottom: 1px solid #edf2f7;
grid-template-columns: minmax(300px, 2fr) minmax(100px, 1fr) minmax(150px, 1fr) minmax(120px, 1fr); padding: 1rem 1.25rem;
padding: 0.75rem 1rem;
background: var(--background-light);
border-bottom: 1px solid var(--border-color);
font-weight: 500; font-weight: 500;
color: var(--text-secondary); border-radius: 8px 8px 0 0;
} }
.table-header div { /* Navigation link styles */
font-size: 0.875rem; a.nav-link {
text-transform: uppercase; color: rgba(255,255,255,0.9) !important;
letter-spacing: 0.05em; font-weight: 500;
}
.col-sm-9 .nav {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
background: var(--background-lighter);
border-bottom: 1px solid var(--border-color);
margin-bottom: 0 !important;
}
.col-sm-9 .nav .nav-item a {
display: inline-flex;
align-items: center;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
color: var(--text-secondary); transition: all 0.2s ease;
text-decoration: none;
border: 1px solid var(--border-color);
border-radius: 0.375rem;
transition: all 0.15s ease;
background: var(--background-lighter);
margin: 0 !important;
} }
.col-sm-9 .nav .nav-item a:hover { a.nav-link:hover {
color: var(--primary-color); color: #ffffff !important;
border-color: var(--primary-color); transform: translateY(-1px);
background: var(--hover-bg);
text-decoration: none;
} }
.col-sm-9 .nav .nav-item a i { /* Utility classes */
margin-right: 0.5rem; .bg-lightgray {
background: #f8f9fa;
border-radius: 8px;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: #c8c8c8;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
.btn {
padding: 0.5rem 1rem;
border-radius: 6px;
font-weight: 500;
transition: all 0.3s ease;
}
.btn-primary {
background: linear-gradient(135deg, #4158D0 0%, #C850C0 100%);
border: none;
}
.btn-primary:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(65, 88, 208, 0.15);
} }

View File

@@ -2329,8 +2329,26 @@ milter_default_action = accept
command = "chmod +x /usr/local/CyberCP/cli/cyberPanel.py" command = "chmod +x /usr/local/CyberCP/cli/cyberPanel.py"
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
def setupPHPSymlink(self):
try:
# Remove existing PHP symlink if it exists
if os.path.exists('/usr/bin/php'):
os.remove('/usr/bin/php')
# Create symlink to PHP 8.0
command = 'ln -s /usr/local/lsws/lsphp80/bin/php /usr/bin/php'
preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR)
logging.InstallLog.writeToFile("[setupPHPSymlink] PHP symlink created successfully.")
except OSError as msg:
logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [setupPHPSymlink]")
return 0
def setupPHPAndComposer(self): def setupPHPAndComposer(self):
try: try:
# First setup the PHP symlink
self.setupPHPSymlink()
if self.distro == ubuntu: if self.distro == ubuntu:
if not os.access('/usr/local/lsws/lsphp70/bin/php', os.R_OK): if not os.access('/usr/local/lsws/lsphp70/bin/php', os.R_OK):

View File

@@ -12,21 +12,54 @@ app.controller('sslIssueCtrl', function ($scope, $http) {
$scope.canNotIssue = true; $scope.canNotIssue = true;
$scope.sslIssued = true; $scope.sslIssued = true;
$scope.couldNotConnect = true; $scope.couldNotConnect = true;
$scope.sslDetails = null;
$scope.showbtn = function () { $scope.showbtn = function () {
$scope.issueSSLBtn = false; $scope.issueSSLBtn = false;
$scope.fetchSSLDetails();
};
$scope.fetchSSLDetails = function() {
if (!$scope.virtualHost) return;
var url = "/manageSSL/getSSLDetails";
var data = {
virtualHost: $scope.virtualHost
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(function(response) {
if (response.data.status === 1) {
$scope.sslDetails = response.data;
} else {
$scope.sslDetails = null;
new PNotify({
title: 'Error',
text: response.data.error_message,
type: 'error'
});
}
}, function(response) {
$scope.sslDetails = null;
new PNotify({
title: 'Error',
text: 'Could not fetch SSL details',
type: 'error'
});
});
}; };
$scope.issueSSL = function () { $scope.issueSSL = function () {
$scope.manageSSLLoading = false; $scope.manageSSLLoading = false;
var url = "/manageSSL/issueSSL"; var url = "/manageSSL/issueSSL";
var data = { var data = {
virtualHost: $scope.virtualHost, virtualHost: $scope.virtualHost,
}; };
var config = { var config = {
headers: { headers: {
'X-CSRFToken': getCookie('csrftoken') 'X-CSRFToken': getCookie('csrftoken')
@@ -35,22 +68,16 @@ app.controller('sslIssueCtrl', function ($scope, $http) {
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) { function ListInitialDatas(response) {
if (response.data.SSL == 1) { if (response.data.SSL == 1) {
$scope.sslIssueCtrl = true; $scope.sslIssueCtrl = true;
$scope.manageSSLLoading = true; $scope.manageSSLLoading = true;
$scope.issueSSLBtn = false; $scope.issueSSLBtn = false;
$scope.canNotIssue = true; $scope.canNotIssue = true;
$scope.sslIssued = false; $scope.sslIssued = false;
$scope.couldNotConnect = true; $scope.couldNotConnect = true;
$scope.sslDomain = $scope.virtualHost; $scope.sslDomain = $scope.virtualHost;
$scope.fetchSSLDetails(); // Refresh SSL details after issuing
} else { } else {
$scope.sslIssueCtrl = true; $scope.sslIssueCtrl = true;
$scope.manageSSLLoading = true; $scope.manageSSLLoading = true;
@@ -59,10 +86,7 @@ app.controller('sslIssueCtrl', function ($scope, $http) {
$scope.sslIssued = true; $scope.sslIssued = true;
$scope.couldNotConnect = true; $scope.couldNotConnect = true;
$scope.errorMessage = response.data.error_message; $scope.errorMessage = response.data.error_message;
} }
} }
function cantLoadInitialDatas(response) { function cantLoadInitialDatas(response) {
@@ -72,10 +96,7 @@ app.controller('sslIssueCtrl', function ($scope, $http) {
$scope.canNotIssue = true; $scope.canNotIssue = true;
$scope.sslIssued = true; $scope.sslIssued = true;
$scope.couldNotConnect = false; $scope.couldNotConnect = false;
} }
}; };
}); });
@@ -85,21 +106,54 @@ app.controller('sslIssueCtrl', function ($scope, $http) {
app.controller('sslIssueCtrlV2', function ($scope, $http) { app.controller('sslIssueCtrlV2', function ($scope, $http) {
$scope.manageSSLLoading = true; $scope.manageSSLLoading = true;
$scope.sslDetails = null;
$scope.showbtn = function () { $scope.showbtn = function () {
$scope.issueSSLBtn = false; $scope.issueSSLBtn = false;
$scope.fetchSSLDetails();
};
$scope.fetchSSLDetails = function() {
if (!$scope.virtualHost) return;
var url = "/manageSSL/getSSLDetails";
var data = {
virtualHost: $scope.virtualHost
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(function(response) {
if (response.data.status === 1) {
$scope.sslDetails = response.data;
} else {
$scope.sslDetails = null;
new PNotify({
title: 'Error',
text: response.data.error_message,
type: 'error'
});
}
}, function(response) {
$scope.sslDetails = null;
new PNotify({
title: 'Error',
text: 'Could not fetch SSL details',
type: 'error'
});
});
}; };
$scope.issueSSL = function () { $scope.issueSSL = function () {
$scope.manageSSLLoading = false; $scope.manageSSLLoading = false;
var url = "/manageSSL/v2IssueSSL"; var url = "/manageSSL/v2IssueSSL";
var data = { var data = {
virtualHost: $scope.virtualHost, virtualHost: $scope.virtualHost,
}; };
var config = { var config = {
headers: { headers: {
'X-CSRFToken': getCookie('csrftoken') 'X-CSRFToken': getCookie('csrftoken')
@@ -108,24 +162,16 @@ app.controller('sslIssueCtrlV2', function ($scope, $http) {
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) { function ListInitialDatas(response) {
$scope.manageSSLLoading = true; $scope.manageSSLLoading = true;
if (response.data.SSL === 1) { if (response.data.SSL === 1) {
$scope.sslStatus = 'Issued.'; $scope.sslStatus = 'Issued.';
$scope.sslLogs = response.data.sslLogs; $scope.sslLogs = response.data.sslLogs;
$scope.fetchSSLDetails(); // Refresh SSL details after issuing
} else { } else {
$scope.sslStatus = 'Failed.'; $scope.sslStatus = 'Failed.';
$scope.sslLogs = response.data.sslLogs; $scope.sslLogs = response.data.sslLogs;
} }
} }
function cantLoadInitialDatas(response) { function cantLoadInitialDatas(response) {
@@ -135,12 +181,8 @@ app.controller('sslIssueCtrlV2', function ($scope, $http) {
$scope.canNotIssue = true; $scope.canNotIssue = true;
$scope.sslIssued = true; $scope.sslIssued = true;
$scope.couldNotConnect = false; $scope.couldNotConnect = false;
} }
}; };
}); });
/* Java script code to issue SSL V2 ends here */ /* Java script code to issue SSL V2 ends here */

View File

@@ -14,7 +14,7 @@
style="height: 23px;line-height: 21px;" style="height: 23px;line-height: 21px;"
class="btn btn-border btn-alt border-red btn-link font-red" class="btn btn-border btn-alt border-red btn-link font-red"
title=""><span>{% trans "SSL Docs" %}</span></a></h2> title=""><span>{% trans "SSL Docs" %}</span></a></h2>
<p>{% trans "This page can be used to issue Lets Encrypt SSL for existing websites on server." %}</p> <p>{% trans "This page can be used to issue Let's Encrypt SSL for existing websites on server." %}</p>
</div> </div>
<div ng-controller="sslIssueCtrl" class="panel"> <div ng-controller="sslIssueCtrl" class="panel">
@@ -39,6 +39,28 @@
</div> </div>
</div> </div>
<div class="form-group" ng-if="sslDetails">
<div class="col-sm-12">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">SSL Details</h3>
</div>
<div class="panel-body">
<div ng-if="sslDetails.hasSSL">
<p><strong>Status:</strong> Active</p>
<p><strong>Issued By:</strong> {$ sslDetails.authority $}</p>
<p><strong>Expiry Date:</strong> {$ sslDetails.expiryDate $}</p>
<p><strong>Days Remaining:</strong> {$ sslDetails.days $}</p>
</div>
<div ng-if="!sslDetails.hasSSL">
<p><strong>Status:</strong> No SSL Certificate</p>
<p ng-if="sslDetails.error_message"><strong>Error:</strong> {$ sslDetails.error_message $}</p>
</div>
</div>
</div>
</div>
</div>
<div ng-hide="issueSSLBtn" class="form-group"> <div ng-hide="issueSSLBtn" class="form-group">
<label class="col-sm-3 control-label"></label> <label class="col-sm-3 control-label"></label>
<div class="col-sm-4"> <div class="col-sm-4">

View File

@@ -66,7 +66,7 @@
<label class="col-sm-2 control-label">{% trans "Select Domain" %} </label> <label class="col-sm-2 control-label">{% trans "Select Domain" %} </label>
<div class="col-sm-6"> <div class="col-sm-6">
<select ng-model="virtualHost" <select ng-model="virtualHost"
class="form-control"> class="form-control" ng-change="showbtn()">
{% for items in websiteList %} {% for items in websiteList %}
<option>{{ items }}</option> <option>{{ items }}</option>
{% endfor %} {% endfor %}
@@ -83,8 +83,29 @@
</div> </div>
<div class="form-group"> <div class="form-group" ng-if="sslDetails">
<div class="col-sm-12">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">SSL Details</h3>
</div>
<div class="panel-body">
<div ng-if="sslDetails.hasSSL">
<p><strong>Status:</strong> Active</p>
<p><strong>Issued By:</strong> {$ sslDetails.authority $}</p>
<p><strong>Expiry Date:</strong> {$ sslDetails.expiryDate $}</p>
<p><strong>Days Remaining:</strong> {$ sslDetails.days $}</p>
</div>
<div ng-if="!sslDetails.hasSSL">
<p><strong>Status:</strong> No SSL Certificate</p>
<p ng-if="sslDetails.error_message"><strong>Error:</strong> {$ sslDetails.error_message $}</p>
</div>
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<h3 style="margin: 2%">SSL Status: {$ sslStatus $}</h3> <h3 style="margin: 2%">SSL Status: {$ sslStatus $}</h3>
<div class="col-sm-12"> <div class="col-sm-12">

View File

@@ -6,6 +6,7 @@ urlpatterns = [
path('manageSSL', views.manageSSL, name='manageSSL'), path('manageSSL', views.manageSSL, name='manageSSL'),
path('issueSSL', views.issueSSL, name='issueSSL'), path('issueSSL', views.issueSSL, name='issueSSL'),
path('getSSLDetails', views.getSSLDetails, name='getSSLDetails'),
path('sslForHostName', views.sslForHostName, name='sslForHostName'), path('sslForHostName', views.sslForHostName, name='sslForHostName'),
path('obtainHostNameSSL', views.obtainHostNameSSL, name='obtainHostNameSSL'), path('obtainHostNameSSL', views.obtainHostNameSSL, name='obtainHostNameSSL'),

View File

@@ -333,3 +333,73 @@ def obtainMailServerSSL(request):
'error_message': str(msg)} 'error_message': str(msg)}
json_data = json.dumps(data_ret) json_data = json.dumps(data_ret)
return HttpResponse(json_data) return HttpResponse(json_data)
def getSSLDetails(request):
try:
userID = request.session['userID']
admin = Administrator.objects.get(pk=userID)
try:
if request.method == 'POST':
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
elif currentACL['manageSSL'] == 1:
pass
else:
return ACLManager.loadErrorJson('SSL', 0)
data = json.loads(request.body)
virtualHost = data['virtualHost']
if ACLManager.checkOwnership(virtualHost, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
try:
website = ChildDomains.objects.get(domain=virtualHost)
except:
website = Websites.objects.get(domain=virtualHost)
try:
import OpenSSL
from datetime import datetime
filePath = '/etc/letsencrypt/live/%s/fullchain.pem' % (virtualHost)
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
open(filePath, 'r').read())
expireData = x509.get_notAfter().decode('ascii')
finalDate = datetime.strptime(expireData, '%Y%m%d%H%M%SZ')
now = datetime.now()
diff = finalDate - now
data_ret = {
'status': 1,
'hasSSL': True,
'days': str(diff.days),
'authority': x509.get_issuer().get_components()[1][1].decode('utf-8'),
'expiryDate': finalDate.strftime('%Y-%m-%d %H:%M:%S')
}
if data_ret['authority'] == 'Denial':
data_ret['authority'] = 'SELF-SIGNED SSL'
except BaseException as msg:
data_ret = {
'status': 1,
'hasSSL': False,
'error_message': str(msg)
}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
except BaseException as msg:
data_ret = {'status': 0, 'error_message': str(msg)}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)
except KeyError:
data_ret = {'status': 0, 'error_message': 'Not logged in'}
json_data = json.dumps(data_ret)
return HttpResponse(json_data)

View File

@@ -14,7 +14,7 @@ django.setup()
from loginSystem.models import Administrator, ACL from loginSystem.models import Administrator, ACL
from django.shortcuts import HttpResponse from django.shortcuts import HttpResponse
from packages.models import Package from packages.models import Package
from websiteFunctions.models import Websites, ChildDomains, aliasDomains, DockerSites from websiteFunctions.models import Websites, ChildDomains, aliasDomains, DockerSites, WPSites
import json import json
from subprocess import call, CalledProcessError from subprocess import call, CalledProcessError
from shlex import split from shlex import split
@@ -582,24 +582,44 @@ class ACLManager:
@staticmethod @staticmethod
def searchWebsiteObjects(currentACL, userID, searchTerm): def searchWebsiteObjects(currentACL, userID, searchTerm):
if currentACL['admin'] == 1: if currentACL['admin'] == 1:
return Websites.objects.filter(domain__istartswith=searchTerm) # Get websites that match the search term
websites = Websites.objects.filter(domain__istartswith=searchTerm)
# Get WordPress sites that match the search term
wp_sites = WPSites.objects.filter(title__icontains=searchTerm)
# Add WordPress sites' parent websites to the results
for wp in wp_sites:
if wp.owner not in websites:
websites = websites | Websites.objects.filter(pk=wp.owner.pk)
return websites
else: else:
websiteList = [] websiteList = []
admin = Administrator.objects.get(pk=userID) admin = Administrator.objects.get(pk=userID)
# Get websites that match the search term
websites = admin.websites_set.filter(domain__istartswith=searchTerm) websites = admin.websites_set.filter(domain__istartswith=searchTerm)
for items in websites: for items in websites:
websiteList.append(items) websiteList.append(items)
admins = Administrator.objects.filter(owner=admin.pk) # Get WordPress sites that match the search term
wp_sites = WPSites.objects.filter(title__icontains=searchTerm)
for wp in wp_sites:
if wp.owner.admin == admin and wp.owner not in websiteList:
websiteList.append(wp.owner)
admins = Administrator.objects.filter(owner=admin.pk)
for items in admins: for items in admins:
# Get websites that match the search term
webs = items.websites_set.filter(domain__istartswith=searchTerm) webs = items.websites_set.filter(domain__istartswith=searchTerm)
for web in webs: for web in webs:
websiteList.append(web) if web not in websiteList:
websiteList.append(web)
# Get WordPress sites that match the search term
wp_sites = WPSites.objects.filter(title__icontains=searchTerm)
for wp in wp_sites:
if wp.owner.admin == items and wp.owner not in websiteList:
websiteList.append(wp.owner)
return websiteList return websiteList

934
plogical/customACME.py Normal file
View File

@@ -0,0 +1,934 @@
import json
import os
import time
import requests
import base64
import hashlib
import logging
from cryptography import x509
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import padding
import OpenSSL
from plogical import CyberCPLogFileWriter as logging
from plogical.processUtilities import ProcessUtilities
import socket
class CustomACME:
def __init__(self, domain, admin_email, staging=False, provider='letsencrypt'):
"""Initialize CustomACME"""
logging.CyberCPLogFileWriter.writeToFile(f'Initializing CustomACME for domain: {domain}, email: {admin_email}, staging: {staging}, provider: {provider}')
self.domain = domain
self.admin_email = admin_email
self.staging = staging
self.provider = provider
# Set the ACME directory URL based on provider and staging flag
if provider == 'zerossl':
if staging:
self.acme_directory = "https://acme-staging.zerossl.com/v2/DV90"
logging.CyberCPLogFileWriter.writeToFile('Using ZeroSSL staging ACME directory')
else:
self.acme_directory = "https://acme.zerossl.com/v2/DV90"
logging.CyberCPLogFileWriter.writeToFile('Using ZeroSSL production ACME directory')
else: # letsencrypt
if staging:
self.acme_directory = "https://acme-staging-v02.api.letsencrypt.org/directory"
logging.CyberCPLogFileWriter.writeToFile('Using Let\'s Encrypt staging ACME directory')
else:
self.acme_directory = "https://acme-v02.api.letsencrypt.org/directory"
logging.CyberCPLogFileWriter.writeToFile('Using Let\'s Encrypt production ACME directory')
self.account_key = None
self.account_url = None
self.directory = None
self.nonce = None
self.order_url = None
self.authorizations = []
self.finalize_url = None
self.certificate_url = None
# Initialize paths
self.cert_path = f'/etc/letsencrypt/live/{domain}'
self.challenge_path = '/usr/local/lsws/Example/html/.well-known/acme-challenge'
self.account_key_path = f'/etc/letsencrypt/accounts/{domain}.key'
logging.CyberCPLogFileWriter.writeToFile(f'Certificate path: {self.cert_path}, Challenge path: {self.challenge_path}')
# Create accounts directory if it doesn't exist
os.makedirs('/etc/letsencrypt/accounts', exist_ok=True)
def _generate_account_key(self):
"""Generate RSA account key"""
try:
logging.CyberCPLogFileWriter.writeToFile('Generating RSA account key...')
key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
self.account_key = key
logging.CyberCPLogFileWriter.writeToFile('Successfully generated RSA account key')
return True
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error generating account key: {str(e)}')
return False
def _get_directory(self):
"""Get ACME directory"""
try:
logging.CyberCPLogFileWriter.writeToFile(f'Fetching ACME directory from {self.acme_directory}')
response = requests.get(self.acme_directory)
self.directory = response.json()
logging.CyberCPLogFileWriter.writeToFile(f'Successfully fetched ACME directory: {json.dumps(self.directory)}')
return True
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error getting directory: {str(e)}')
return False
def _get_nonce(self):
"""Get new nonce from ACME server"""
try:
logging.CyberCPLogFileWriter.writeToFile('Getting new nonce...')
response = requests.head(self.directory['newNonce'])
self.nonce = response.headers['Replay-Nonce']
logging.CyberCPLogFileWriter.writeToFile(f'Successfully got nonce: {self.nonce}')
return True
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error getting nonce: {str(e)}')
return False
def _create_jws(self, payload, url):
"""Create JWS (JSON Web Signature)"""
try:
logging.CyberCPLogFileWriter.writeToFile(f'Creating JWS for URL: {url}')
if payload is not None:
logging.CyberCPLogFileWriter.writeToFile(f'Payload: {json.dumps(payload)}')
# Get a fresh nonce for this request
if not self._get_nonce():
logging.CyberCPLogFileWriter.writeToFile('Failed to get fresh nonce')
return None
# Get the private key numbers
logging.CyberCPLogFileWriter.writeToFile('Getting private key numbers...')
private_numbers = self.account_key.private_numbers()
public_numbers = private_numbers.public_numbers
# Convert numbers to bytes
logging.CyberCPLogFileWriter.writeToFile('Converting RSA numbers to bytes...')
n_bytes = public_numbers.n.to_bytes((public_numbers.n.bit_length() + 7) // 8, 'big')
e_bytes = public_numbers.e.to_bytes((public_numbers.e.bit_length() + 7) // 8, 'big')
# Create JWK
logging.CyberCPLogFileWriter.writeToFile('Creating JWK...')
jwk_key = {
"kty": "RSA",
"n": base64.urlsafe_b64encode(n_bytes).decode('utf-8').rstrip('='),
"e": base64.urlsafe_b64encode(e_bytes).decode('utf-8').rstrip('='),
"alg": "RS256"
}
logging.CyberCPLogFileWriter.writeToFile(f'Created JWK: {json.dumps(jwk_key)}')
# Create protected header
protected = {
"alg": "RS256",
"url": url,
"nonce": self.nonce
}
# Add either JWK or Key ID based on whether we have an account URL
if self.account_url and url != self.directory['newAccount']:
protected["kid"] = self.account_url
logging.CyberCPLogFileWriter.writeToFile(f'Using Key ID: {self.account_url}')
else:
protected["jwk"] = jwk_key
logging.CyberCPLogFileWriter.writeToFile('Using JWK for new account')
# Encode protected header
logging.CyberCPLogFileWriter.writeToFile('Encoding protected header...')
protected_b64 = base64.urlsafe_b64encode(
json.dumps(protected).encode('utf-8')
).decode('utf-8').rstrip('=')
# For POST-as-GET requests, payload_b64 should be empty string
if payload is None:
payload_b64 = ""
logging.CyberCPLogFileWriter.writeToFile('Using empty payload for POST-as-GET request')
else:
# Encode payload
logging.CyberCPLogFileWriter.writeToFile('Encoding payload...')
payload_b64 = base64.urlsafe_b64encode(
json.dumps(payload).encode('utf-8')
).decode('utf-8').rstrip('=')
# Create signature input
logging.CyberCPLogFileWriter.writeToFile('Creating signature input...')
signature_input = f"{protected_b64}.{payload_b64}".encode('utf-8')
# Sign the input
logging.CyberCPLogFileWriter.writeToFile('Signing input...')
signature = self.account_key.sign(
signature_input,
padding.PKCS1v15(),
hashes.SHA256()
)
# Encode signature
logging.CyberCPLogFileWriter.writeToFile('Encoding signature...')
signature_b64 = base64.urlsafe_b64encode(signature).decode('utf-8').rstrip('=')
# Create final JWS
logging.CyberCPLogFileWriter.writeToFile('Creating final JWS...')
jws = {
"protected": protected_b64,
"signature": signature_b64
}
# Only add payload if it exists
if payload is not None:
jws["payload"] = payload_b64
# Ensure the JWS is properly formatted
jws_str = json.dumps(jws, separators=(',', ':'))
logging.CyberCPLogFileWriter.writeToFile(f'Final JWS: {jws_str}')
return jws_str
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error creating JWS: {str(e)}')
return None
def _load_account_key(self):
"""Load existing account key if available"""
try:
if os.path.exists(self.account_key_path):
logging.CyberCPLogFileWriter.writeToFile('Loading existing account key...')
with open(self.account_key_path, 'rb') as f:
key_data = f.read()
self.account_key = serialization.load_pem_private_key(
key_data,
password=None,
backend=default_backend()
)
logging.CyberCPLogFileWriter.writeToFile('Successfully loaded existing account key')
return True
return False
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error loading account key: {str(e)}')
return False
def _save_account_key(self):
"""Save account key for future use"""
try:
logging.CyberCPLogFileWriter.writeToFile('Saving account key...')
key_data = self.account_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
with open(self.account_key_path, 'wb') as f:
f.write(key_data)
logging.CyberCPLogFileWriter.writeToFile('Successfully saved account key')
return True
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error saving account key: {str(e)}')
return False
def _create_account(self):
"""Create new ACME account"""
try:
logging.CyberCPLogFileWriter.writeToFile('Creating new ACME account...')
payload = {
"termsOfServiceAgreed": True,
"contact": [f"mailto:{self.admin_email}"]
}
jws = self._create_jws(payload, self.directory['newAccount'])
if not jws:
logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for account creation')
return False
logging.CyberCPLogFileWriter.writeToFile('Sending account creation request...')
headers = {
'Content-Type': 'application/jose+json'
}
response = requests.post(self.directory['newAccount'], data=jws, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Account creation response status: {response.status_code}')
logging.CyberCPLogFileWriter.writeToFile(f'Account creation response: {response.text}')
if response.status_code == 201:
self.account_url = response.headers['Location']
logging.CyberCPLogFileWriter.writeToFile(f'Successfully created account. Account URL: {self.account_url}')
# Save the account key for future use
self._save_account_key()
return True
elif response.status_code == 429:
logging.CyberCPLogFileWriter.writeToFile('Rate limit hit for account creation. Using staging environment...')
self.staging = True
self.acme_directory = "https://acme-staging-v02.api.letsencrypt.org/directory"
# Get new directory and nonce for staging
if not self._get_directory():
return False
if not self._get_nonce():
return False
# Try one more time with staging
return self._create_account()
elif response.status_code == 400 and "badNonce" in response.text:
logging.CyberCPLogFileWriter.writeToFile('Bad nonce, getting new nonce and retrying...')
if not self._get_nonce():
return False
return self._create_account()
return False
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error creating account: {str(e)}')
return False
def _create_order(self, domains):
"""Create new order for domains"""
try:
logging.CyberCPLogFileWriter.writeToFile(f'Creating new order for domains: {domains}')
identifiers = [{"type": "dns", "value": domain} for domain in domains]
payload = {
"identifiers": identifiers
}
jws = self._create_jws(payload, self.directory['newOrder'])
if not jws:
logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for order creation')
return False
logging.CyberCPLogFileWriter.writeToFile('Sending order creation request...')
headers = {
'Content-Type': 'application/jose+json'
}
response = requests.post(self.directory['newOrder'], data=jws, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Order creation response status: {response.status_code}')
logging.CyberCPLogFileWriter.writeToFile(f'Order creation response: {response.text}')
if response.status_code == 201:
self.order_url = response.headers['Location']
self.authorizations = response.json()['authorizations']
self.finalize_url = response.json()['finalize']
logging.CyberCPLogFileWriter.writeToFile(f'Successfully created order. Order URL: {self.order_url}')
logging.CyberCPLogFileWriter.writeToFile(f'Authorizations: {self.authorizations}')
logging.CyberCPLogFileWriter.writeToFile(f'Finalize URL: {self.finalize_url}')
return True
return False
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error creating order: {str(e)}')
return False
def _handle_http_challenge(self, challenge):
"""Handle HTTP-01 challenge"""
try:
logging.CyberCPLogFileWriter.writeToFile(f'Handling HTTP challenge: {json.dumps(challenge)}')
# Get key authorization
key_auth = self._get_key_authorization(challenge)
if not key_auth:
logging.CyberCPLogFileWriter.writeToFile('Failed to get key authorization')
return False
# Create challenge directory if it doesn't exist
if not os.path.exists(self.challenge_path):
logging.CyberCPLogFileWriter.writeToFile(f'Creating challenge directory: {self.challenge_path}')
os.makedirs(self.challenge_path)
# Write challenge file
challenge_file = os.path.join(self.challenge_path, challenge['token'])
logging.CyberCPLogFileWriter.writeToFile(f'Writing challenge file: {challenge_file}')
# Write only the key authorization to the file
with open(challenge_file, 'w') as f:
f.write(key_auth)
logging.CyberCPLogFileWriter.writeToFile('Successfully handled HTTP challenge')
return True
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error handling HTTP challenge: {str(e)}')
return False
def _handle_dns_challenge(self, challenge):
"""Handle DNS-01 challenge (Cloudflare)"""
try:
logging.CyberCPLogFileWriter.writeToFile(f'Handling DNS challenge: {json.dumps(challenge)}')
# This is a placeholder - implement Cloudflare API integration
# You'll need to add your Cloudflare API credentials and implementation
pass
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error handling DNS challenge: {str(e)}')
return False
def _get_key_authorization(self, challenge):
"""Get key authorization for challenge"""
try:
logging.CyberCPLogFileWriter.writeToFile('Getting key authorization...')
# Get the private key numbers
private_numbers = self.account_key.private_numbers()
public_numbers = private_numbers.public_numbers
# Convert numbers to bytes
n_bytes = public_numbers.n.to_bytes((public_numbers.n.bit_length() + 7) // 8, 'big')
e_bytes = public_numbers.e.to_bytes((public_numbers.e.bit_length() + 7) // 8, 'big')
# Create JWK without alg field
jwk_key = {
"kty": "RSA",
"n": base64.urlsafe_b64encode(n_bytes).decode('utf-8').rstrip('='),
"e": base64.urlsafe_b64encode(e_bytes).decode('utf-8').rstrip('=')
}
# Calculate the JWK thumbprint according to RFC 7638
# The thumbprint is a hash of the JWK (JSON Web Key) in a specific format
# First, we create a dictionary with the required JWK parameters
jwk = {
"e": base64.urlsafe_b64encode(public_numbers.e.to_bytes(3, 'big')).decode('utf-8').rstrip('='),
"kty": "RSA", # Key type
"n": base64.urlsafe_b64encode(public_numbers.n.to_bytes(256, 'big')).decode('utf-8').rstrip('=')
}
# Sort the JWK parameters alphabetically by key name
# This ensures consistent thumbprint calculation regardless of parameter order
sorted_jwk = json.dumps(jwk, sort_keys=True, separators=(',', ':'))
# Calculate the SHA-256 hash of the sorted JWK
# Example of what sorted_jwk might look like:
# {"e":"AQAB","kty":"RSA","n":"tVKUtcx_n9rt5afY_2WFNVAu9fjD4xqX4Xm3dJz3XYb"}
# The thumbprint will be a 32-byte SHA-256 hash of this string
# For example, it might look like: b'x\x9c\x1d\x8f\x8b\x1b\x1e\x8b\x1b\x1e\x8b\x1b\x1e\x8b\x1b\x1e'
thumbprint = hashlib.sha256(sorted_jwk.encode('utf-8')).digest()
# Encode the thumbprint in base64url format (RFC 4648)
# This removes padding characters (=) and replaces + and / with - and _
# Example final thumbprint: "xJ0dj8sbHosbHosbHosbHos"
thumbprint = base64.urlsafe_b64encode(thumbprint).decode('utf-8').rstrip('=')
# Combine token and key authorization
key_auth = f"{challenge['token']}.{thumbprint}"
logging.CyberCPLogFileWriter.writeToFile(f'Key authorization: {key_auth}')
return key_auth
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error getting key authorization: {str(e)}')
return None
def _verify_challenge(self, challenge_url):
"""Verify challenge completion with the ACME server
This function sends a POST request to the ACME server to verify that the challenge
has been completed successfully. The challenge URL is provided by the ACME server
when the challenge is created.
Example challenge_url:
"https://acme-v02.api.letsencrypt.org/acme/challenge/example.com/123456"
The verification process:
1. Creates an empty payload (POST-as-GET request)
2. Creates a JWS (JSON Web Signature) with the payload
3. Sends the request to the ACME server
4. Checks the response status
Returns:
bool: True if challenge is verified successfully, False otherwise
"""
try:
logging.CyberCPLogFileWriter.writeToFile(f'Verifying challenge at URL: {challenge_url}')
# Create empty payload for POST-as-GET request
# This is a special type of request where we want to GET a resource
# but need to include a signature, so we use POST with an empty payload
payload = {}
# Create JWS (JSON Web Signature) for the request
# Example JWS might look like:
# {
# "protected": "eyJhbGciOiJSUzI1NiIsIm5vbmNlIjoiMTIzNDU2Nzg5MCIsInVybCI6Imh0dHBzOi8vYWNtZS12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2NoYWxsZW5nZS9leGFtcGxlLmNvbS8xMjM0NTYifQ",
# "signature": "c2lnbmF0dXJlX2hlcmU",
# "payload": ""
# }
jws = self._create_jws(payload, challenge_url)
if not jws:
logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for challenge verification')
return False
logging.CyberCPLogFileWriter.writeToFile('Sending challenge verification request...')
# Set headers for the request
# Content-Type: application/jose+json indicates we're sending a JWS
headers = {
'Content-Type': 'application/jose+json'
}
# Send the verification request to the ACME server
# Example response might look like:
# {
# "type": "http-01",
# "status": "valid",
# "validated": "2024-03-20T12:00:00Z",
# "url": "https://acme-v02.api.letsencrypt.org/acme/challenge/example.com/123456"
# }
response = requests.post(challenge_url, data=jws, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Challenge verification response status: {response.status_code}')
logging.CyberCPLogFileWriter.writeToFile(f'Challenge verification response: {response.text}')
# Check if the challenge was verified successfully
# Status code 200 indicates success
# The response will contain the challenge status and validation time
if response.status_code == 200:
logging.CyberCPLogFileWriter.writeToFile('Successfully verified challenge')
return True
return False
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error verifying challenge: {str(e)}')
return False
def _finalize_order(self, csr):
"""Finalize order and get certificate"""
try:
logging.CyberCPLogFileWriter.writeToFile('Finalizing order...')
payload = {
"csr": base64.urlsafe_b64encode(csr).decode('utf-8').rstrip('=')
}
jws = self._create_jws(payload, self.finalize_url)
if not jws:
logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for order finalization')
return False
logging.CyberCPLogFileWriter.writeToFile('Sending order finalization request...')
headers = {
'Content-Type': 'application/jose+json'
}
response = requests.post(self.finalize_url, data=jws, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Order finalization response status: {response.status_code}')
logging.CyberCPLogFileWriter.writeToFile(f'Order finalization response: {response.text}')
if response.status_code == 200:
# Wait for order to be processed
max_attempts = 30
delay = 2
for attempt in range(max_attempts):
if not self._get_nonce():
logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for order status check')
return False
response = requests.get(self.order_url, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Order status check response: {response.text}')
if response.status_code == 200:
order_status = response.json().get('status')
if order_status == 'valid':
self.certificate_url = response.json().get('certificate')
logging.CyberCPLogFileWriter.writeToFile(f'Successfully finalized order. Certificate URL: {self.certificate_url}')
return True
elif order_status == 'invalid':
logging.CyberCPLogFileWriter.writeToFile('Order validation failed')
return False
elif order_status == 'processing':
logging.CyberCPLogFileWriter.writeToFile(f'Order still processing, attempt {attempt + 1}/{max_attempts}')
time.sleep(delay)
continue
logging.CyberCPLogFileWriter.writeToFile(f'Order status check failed, attempt {attempt + 1}/{max_attempts}')
time.sleep(delay)
logging.CyberCPLogFileWriter.writeToFile('Order processing timed out')
return False
return False
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error finalizing order: {str(e)}')
return False
def _download_certificate(self):
"""Download certificate from ACME server"""
try:
logging.CyberCPLogFileWriter.writeToFile('Downloading certificate...')
logging.CyberCPLogFileWriter.writeToFile(f'Certificate URL: {self.certificate_url}')
# For certificate downloads, we can use a simple GET request
response = requests.get(self.certificate_url)
logging.CyberCPLogFileWriter.writeToFile(f'Certificate download response status: {response.status_code}')
logging.CyberCPLogFileWriter.writeToFile(f'Certificate download response headers: {response.headers}')
logging.CyberCPLogFileWriter.writeToFile(f'Certificate download response content: {response.text}')
if response.status_code == 200:
logging.CyberCPLogFileWriter.writeToFile('Successfully downloaded certificate')
return response.content
return None
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error downloading certificate: {str(e)}')
return None
def _wait_for_challenge_validation(self, challenge_url, max_attempts=30, delay=2):
"""Wait for challenge to be validated by the ACME server"""
try:
logging.CyberCPLogFileWriter.writeToFile(f'Waiting for challenge validation at URL: {challenge_url}')
for attempt in range(max_attempts):
if not self._get_nonce():
logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for challenge status check')
return False
headers = {
'Content-Type': 'application/jose+json'
}
response = requests.get(challenge_url, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Challenge status check response: {response.text}')
if response.status_code == 200:
challenge_status = response.json().get('status')
if challenge_status == 'valid':
logging.CyberCPLogFileWriter.writeToFile('Challenge validated successfully')
return True
elif challenge_status == 'invalid':
logging.CyberCPLogFileWriter.writeToFile('Challenge validation failed')
return False
logging.CyberCPLogFileWriter.writeToFile(f'Challenge still pending, attempt {attempt + 1}/{max_attempts}')
time.sleep(delay)
logging.CyberCPLogFileWriter.writeToFile('Challenge validation timed out')
return False
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error waiting for challenge validation: {str(e)}')
return False
def _check_dns_record(self, domain):
"""Check if a domain has valid DNS records
This function performs multiple DNS checks to ensure the domain has valid DNS records.
It includes:
1. A record (IPv4) check
2. AAAA record (IPv6) check
3. DNS caching prevention
4. Multiple DNS server checks
Args:
domain (str): The domain to check
Returns:
bool: True if valid DNS records are found, False otherwise
"""
try:
logging.CyberCPLogFileWriter.writeToFile(f'Checking DNS records for domain: {domain}')
# List of public DNS servers to check against
dns_servers = [
'8.8.8.8', # Google DNS
'1.1.1.1', # Cloudflare DNS
'208.67.222.222' # OpenDNS
]
# Function to check DNS record with specific DNS server
def check_with_dns_server(server, record_type='A'):
try:
# Create a new socket for each check
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(5) # 5 second timeout
# Set the DNS server
sock.connect((server, 53))
# Create DNS query
query = bytearray()
# DNS header
query += b'\x00\x01' # Transaction ID
query += b'\x01\x00' # Flags: Standard query
query += b'\x00\x01' # Questions: 1
query += b'\x00\x00' # Answer RRs: 0
query += b'\x00\x00' # Authority RRs: 0
query += b'\x00\x00' # Additional RRs: 0
# Domain name
for part in domain.split('.'):
query.append(len(part))
query.extend(part.encode())
query += b'\x00' # End of domain name
# Query type and class
if record_type == 'A':
query += b'\x00\x01' # Type: A
else: # AAAA
query += b'\x00\x1c' # Type: AAAA
query += b'\x00\x01' # Class: IN
# Send query
sock.send(query)
# Receive response
response = sock.recv(1024)
# Check if we got a valid response
if len(response) > 12: # Minimum DNS response size
# Check if there are answers in the response
answer_count = int.from_bytes(response[6:8], 'big')
if answer_count > 0:
return True
return False
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error checking DNS with server {server}: {str(e)}')
return False
finally:
sock.close()
# Check A records (IPv4) with multiple DNS servers
a_record_found = False
for server in dns_servers:
if check_with_dns_server(server, 'A'):
a_record_found = True
break
# Check AAAA records (IPv6) with multiple DNS servers
aaaa_record_found = False
for server in dns_servers:
if check_with_dns_server(server, 'AAAA'):
aaaa_record_found = True
break
# Also check with system's DNS resolver as a fallback
try:
# Try to resolve A record (IPv4)
socket.gethostbyname(domain)
a_record_found = True
except socket.gaierror:
pass
try:
# Try to resolve AAAA record (IPv6)
socket.getaddrinfo(domain, None, socket.AF_INET6)
aaaa_record_found = True
except socket.gaierror:
pass
# Log the results
if a_record_found:
logging.CyberCPLogFileWriter.writeToFile(f'IPv4 DNS record found for domain: {domain}')
if aaaa_record_found:
logging.CyberCPLogFileWriter.writeToFile(f'IPv6 DNS record found for domain: {domain}')
# Return True if either A or AAAA record is found
return a_record_found or aaaa_record_found
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error checking DNS records: {str(e)}')
return False
def _wait_for_order_processing(self, max_attempts=30, delay=2):
"""Wait for order to be processed"""
try:
logging.CyberCPLogFileWriter.writeToFile('Waiting for order processing...')
for attempt in range(max_attempts):
if not self._get_nonce():
logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for order status check')
return False
headers = {
'Content-Type': 'application/jose+json'
}
response = requests.get(self.order_url, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Order status check response: {response.text}')
if response.status_code == 200:
order_status = response.json().get('status')
if order_status == 'valid':
self.certificate_url = response.json().get('certificate')
logging.CyberCPLogFileWriter.writeToFile('Order validated successfully')
return True
elif order_status == 'invalid':
logging.CyberCPLogFileWriter.writeToFile('Order validation failed')
return False
elif order_status == 'processing':
logging.CyberCPLogFileWriter.writeToFile(f'Order still processing, attempt {attempt + 1}/{max_attempts}')
time.sleep(delay)
continue
logging.CyberCPLogFileWriter.writeToFile(f'Order status check failed, attempt {attempt + 1}/{max_attempts}')
time.sleep(delay)
logging.CyberCPLogFileWriter.writeToFile('Order processing timed out')
return False
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error waiting for order processing: {str(e)}')
return False
def issue_certificate(self, domains, use_dns=False):
"""Main method to issue certificate"""
try:
logging.CyberCPLogFileWriter.writeToFile(f'Starting certificate issuance for domains: {domains}, use_dns: {use_dns}')
# Try to load existing account key first
if self._load_account_key():
logging.CyberCPLogFileWriter.writeToFile('Using existing account key')
else:
logging.CyberCPLogFileWriter.writeToFile('No existing account key found, will create new one')
# Filter domains to only include those with valid DNS records
valid_domains = []
for domain in domains:
if self._check_dns_record(domain):
valid_domains.append(domain)
else:
logging.CyberCPLogFileWriter.writeToFile(f'Skipping domain {domain} due to missing DNS records')
if not valid_domains:
logging.CyberCPLogFileWriter.writeToFile('No valid domains found with DNS records')
return False
# Initialize ACME
logging.CyberCPLogFileWriter.writeToFile('Step 1: Generating account key')
if not self._generate_account_key():
logging.CyberCPLogFileWriter.writeToFile('Failed to generate account key')
return False
logging.CyberCPLogFileWriter.writeToFile('Step 2: Getting ACME directory')
if not self._get_directory():
logging.CyberCPLogFileWriter.writeToFile('Failed to get ACME directory')
return False
logging.CyberCPLogFileWriter.writeToFile('Step 3: Getting nonce')
if not self._get_nonce():
logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce')
return False
logging.CyberCPLogFileWriter.writeToFile('Step 4: Creating account')
if not self._create_account():
logging.CyberCPLogFileWriter.writeToFile('Failed to create account')
# If we failed to create account and we're not in staging, try staging
if not self.staging:
logging.CyberCPLogFileWriter.writeToFile('Switching to staging environment...')
self.staging = True
self.acme_directory = "https://acme-staging-v02.api.letsencrypt.org/directory"
if not self._get_directory():
return False
if not self._get_nonce():
return False
if not self._create_account():
return False
else:
return False
# Create order with only valid domains
logging.CyberCPLogFileWriter.writeToFile('Step 5: Creating order')
if not self._create_order(valid_domains):
logging.CyberCPLogFileWriter.writeToFile('Failed to create order')
return False
# Handle challenges
logging.CyberCPLogFileWriter.writeToFile('Step 6: Handling challenges')
for auth_url in self.authorizations:
logging.CyberCPLogFileWriter.writeToFile(f'Processing authorization URL: {auth_url}')
if not self._get_nonce():
logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for authorization')
return False
# Get authorization details with GET request
headers = {
'Content-Type': 'application/jose+json'
}
response = requests.get(auth_url, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Authorization response status: {response.status_code}')
logging.CyberCPLogFileWriter.writeToFile(f'Authorization response: {response.text}')
if response.status_code != 200:
logging.CyberCPLogFileWriter.writeToFile('Failed to get authorization')
return False
challenges = response.json()['challenges']
for challenge in challenges:
logging.CyberCPLogFileWriter.writeToFile(f'Processing challenge: {json.dumps(challenge)}')
# Only handle the challenge type we're using
if use_dns and challenge['type'] == 'dns-01':
if not self._handle_dns_challenge(challenge):
logging.CyberCPLogFileWriter.writeToFile('Failed to handle DNS challenge')
return False
if not self._verify_challenge(challenge['url']):
logging.CyberCPLogFileWriter.writeToFile('Failed to verify DNS challenge')
return False
if not self._wait_for_challenge_validation(challenge['url']):
logging.CyberCPLogFileWriter.writeToFile('DNS challenge validation failed')
return False
elif not use_dns and challenge['type'] == 'http-01':
if not self._handle_http_challenge(challenge):
logging.CyberCPLogFileWriter.writeToFile('Failed to handle HTTP challenge')
return False
if not self._verify_challenge(challenge['url']):
logging.CyberCPLogFileWriter.writeToFile('Failed to verify HTTP challenge')
return False
if not self._wait_for_challenge_validation(challenge['url']):
logging.CyberCPLogFileWriter.writeToFile('HTTP challenge validation failed')
return False
else:
logging.CyberCPLogFileWriter.writeToFile(f'Skipping {challenge["type"]} challenge')
# Generate CSR
logging.CyberCPLogFileWriter.writeToFile('Step 7: Generating CSR')
key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
# Get the domain from the order response
order_response = requests.get(self.order_url, headers=headers).json()
order_domains = [identifier['value'] for identifier in order_response['identifiers']]
logging.CyberCPLogFileWriter.writeToFile(f'Order domains: {order_domains}')
# Create CSR with exactly the domains from the order
csr = x509.CertificateSigningRequestBuilder().subject_name(
x509.Name([
x509.NameAttribute(x509.NameOID.COMMON_NAME, order_domains[0])
])
).add_extension(
x509.SubjectAlternativeName([
x509.DNSName(domain) for domain in order_domains
]),
critical=False
).sign(key, hashes.SHA256(), default_backend())
# Finalize order
logging.CyberCPLogFileWriter.writeToFile('Step 8: Finalizing order')
if not self._finalize_order(csr.public_bytes(serialization.Encoding.DER)):
logging.CyberCPLogFileWriter.writeToFile('Failed to finalize order')
return False
# Wait for order processing
logging.CyberCPLogFileWriter.writeToFile('Step 9: Waiting for order processing')
if not self._wait_for_order_processing():
logging.CyberCPLogFileWriter.writeToFile('Failed to process order')
return False
# Download certificate
logging.CyberCPLogFileWriter.writeToFile('Step 10: Downloading certificate')
certificate = self._download_certificate()
if not certificate:
logging.CyberCPLogFileWriter.writeToFile('Failed to download certificate')
return False
# Save certificate
logging.CyberCPLogFileWriter.writeToFile('Step 11: Saving certificate')
if not os.path.exists(self.cert_path):
logging.CyberCPLogFileWriter.writeToFile(f'Creating certificate directory: {self.cert_path}')
os.makedirs(self.cert_path)
cert_file = os.path.join(self.cert_path, 'fullchain.pem')
key_file = os.path.join(self.cert_path, 'privkey.pem')
logging.CyberCPLogFileWriter.writeToFile(f'Saving certificate to: {cert_file}')
with open(cert_file, 'wb') as f:
f.write(certificate)
logging.CyberCPLogFileWriter.writeToFile(f'Saving private key to: {key_file}')
with open(key_file, 'wb') as f:
f.write(key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
))
logging.CyberCPLogFileWriter.writeToFile('Successfully completed certificate issuance')
return True
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error issuing certificate: {str(e)}')
return False

View File

@@ -16,24 +16,31 @@ from plogical import CyberCPLogFileWriter as logging
from plogical.processUtilities import ProcessUtilities from plogical.processUtilities import ProcessUtilities
import socket import socket
class CustomACME: class CustomACME:
def __init__(self, domain, admin_email, staging=False): def __init__(self, domain, admin_email, staging=False, provider='letsencrypt'):
"""Initialize CustomACME""" """Initialize CustomACME"""
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(f'Initializing CustomACME for domain: {domain}, email: {admin_email}, staging: {staging}, provider: {provider}')
f'Initializing CustomACME for domain: {domain}, email: {admin_email}, staging: {staging}')
self.domain = domain self.domain = domain
self.admin_email = admin_email self.admin_email = admin_email
self.staging = staging self.staging = staging
self.provider = provider
# Set the ACME directory URL based on staging flag
if staging: # Set the ACME directory URL based on provider and staging flag
self.acme_directory = "https://acme-staging-v02.api.letsencrypt.org/directory" if provider == 'zerossl':
logging.CyberCPLogFileWriter.writeToFile('Using staging ACME directory') if staging:
else: self.acme_directory = "https://acme-staging.zerossl.com/v2/DV90"
self.acme_directory = "https://acme-v02.api.letsencrypt.org/directory" logging.CyberCPLogFileWriter.writeToFile('Using ZeroSSL staging ACME directory')
logging.CyberCPLogFileWriter.writeToFile('Using production ACME directory') else:
self.acme_directory = "https://acme.zerossl.com/v2/DV90"
logging.CyberCPLogFileWriter.writeToFile('Using ZeroSSL production ACME directory')
else: # letsencrypt
if staging:
self.acme_directory = "https://acme-staging-v02.api.letsencrypt.org/directory"
logging.CyberCPLogFileWriter.writeToFile('Using Let\'s Encrypt staging ACME directory')
else:
self.acme_directory = "https://acme-v02.api.letsencrypt.org/directory"
logging.CyberCPLogFileWriter.writeToFile('Using Let\'s Encrypt production ACME directory')
self.account_key = None self.account_key = None
self.account_url = None self.account_url = None
self.directory = None self.directory = None
@@ -42,17 +49,16 @@ class CustomACME:
self.authorizations = [] self.authorizations = []
self.finalize_url = None self.finalize_url = None
self.certificate_url = None self.certificate_url = None
# Initialize paths # Initialize paths
self.cert_path = f'/etc/letsencrypt/live/{domain}' self.cert_path = f'/etc/letsencrypt/live/{domain}'
self.challenge_path = '/usr/local/lsws/Example/html/.well-known/acme-challenge' self.challenge_path = '/usr/local/lsws/Example/html/.well-known/acme-challenge'
self.account_key_path = f'/etc/letsencrypt/accounts/{domain}.key' self.account_key_path = f'/etc/letsencrypt/accounts/{domain}.key'
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(f'Certificate path: {self.cert_path}, Challenge path: {self.challenge_path}')
f'Certificate path: {self.cert_path}, Challenge path: {self.challenge_path}')
# Create accounts directory if it doesn't exist # Create accounts directory if it doesn't exist
os.makedirs('/etc/letsencrypt/accounts', exist_ok=True) os.makedirs('/etc/letsencrypt/accounts', exist_ok=True)
def _generate_account_key(self): def _generate_account_key(self):
"""Generate RSA account key""" """Generate RSA account key"""
try: try:
@@ -75,8 +81,7 @@ class CustomACME:
logging.CyberCPLogFileWriter.writeToFile(f'Fetching ACME directory from {self.acme_directory}') logging.CyberCPLogFileWriter.writeToFile(f'Fetching ACME directory from {self.acme_directory}')
response = requests.get(self.acme_directory) response = requests.get(self.acme_directory)
self.directory = response.json() self.directory = response.json()
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(f'Successfully fetched ACME directory: {json.dumps(self.directory)}')
f'Successfully fetched ACME directory: {json.dumps(self.directory)}')
return True return True
except Exception as e: except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error getting directory: {str(e)}') logging.CyberCPLogFileWriter.writeToFile(f'Error getting directory: {str(e)}')
@@ -100,22 +105,22 @@ class CustomACME:
logging.CyberCPLogFileWriter.writeToFile(f'Creating JWS for URL: {url}') logging.CyberCPLogFileWriter.writeToFile(f'Creating JWS for URL: {url}')
if payload is not None: if payload is not None:
logging.CyberCPLogFileWriter.writeToFile(f'Payload: {json.dumps(payload)}') logging.CyberCPLogFileWriter.writeToFile(f'Payload: {json.dumps(payload)}')
# Get a fresh nonce for this request # Get a fresh nonce for this request
if not self._get_nonce(): if not self._get_nonce():
logging.CyberCPLogFileWriter.writeToFile('Failed to get fresh nonce') logging.CyberCPLogFileWriter.writeToFile('Failed to get fresh nonce')
return None return None
# Get the private key numbers # Get the private key numbers
logging.CyberCPLogFileWriter.writeToFile('Getting private key numbers...') logging.CyberCPLogFileWriter.writeToFile('Getting private key numbers...')
private_numbers = self.account_key.private_numbers() private_numbers = self.account_key.private_numbers()
public_numbers = private_numbers.public_numbers public_numbers = private_numbers.public_numbers
# Convert numbers to bytes # Convert numbers to bytes
logging.CyberCPLogFileWriter.writeToFile('Converting RSA numbers to bytes...') logging.CyberCPLogFileWriter.writeToFile('Converting RSA numbers to bytes...')
n_bytes = public_numbers.n.to_bytes((public_numbers.n.bit_length() + 7) // 8, 'big') n_bytes = public_numbers.n.to_bytes((public_numbers.n.bit_length() + 7) // 8, 'big')
e_bytes = public_numbers.e.to_bytes((public_numbers.e.bit_length() + 7) // 8, 'big') e_bytes = public_numbers.e.to_bytes((public_numbers.e.bit_length() + 7) // 8, 'big')
# Create JWK # Create JWK
logging.CyberCPLogFileWriter.writeToFile('Creating JWK...') logging.CyberCPLogFileWriter.writeToFile('Creating JWK...')
jwk_key = { jwk_key = {
@@ -125,14 +130,14 @@ class CustomACME:
"alg": "RS256" "alg": "RS256"
} }
logging.CyberCPLogFileWriter.writeToFile(f'Created JWK: {json.dumps(jwk_key)}') logging.CyberCPLogFileWriter.writeToFile(f'Created JWK: {json.dumps(jwk_key)}')
# Create protected header # Create protected header
protected = { protected = {
"alg": "RS256", "alg": "RS256",
"url": url, "url": url,
"nonce": self.nonce "nonce": self.nonce
} }
# Add either JWK or Key ID based on whether we have an account URL # Add either JWK or Key ID based on whether we have an account URL
if self.account_url and url != self.directory['newAccount']: if self.account_url and url != self.directory['newAccount']:
protected["kid"] = self.account_url protected["kid"] = self.account_url
@@ -140,13 +145,13 @@ class CustomACME:
else: else:
protected["jwk"] = jwk_key protected["jwk"] = jwk_key
logging.CyberCPLogFileWriter.writeToFile('Using JWK for new account') logging.CyberCPLogFileWriter.writeToFile('Using JWK for new account')
# Encode protected header # Encode protected header
logging.CyberCPLogFileWriter.writeToFile('Encoding protected header...') logging.CyberCPLogFileWriter.writeToFile('Encoding protected header...')
protected_b64 = base64.urlsafe_b64encode( protected_b64 = base64.urlsafe_b64encode(
json.dumps(protected).encode('utf-8') json.dumps(protected).encode('utf-8')
).decode('utf-8').rstrip('=') ).decode('utf-8').rstrip('=')
# For POST-as-GET requests, payload_b64 should be empty string # For POST-as-GET requests, payload_b64 should be empty string
if payload is None: if payload is None:
payload_b64 = "" payload_b64 = ""
@@ -157,11 +162,11 @@ class CustomACME:
payload_b64 = base64.urlsafe_b64encode( payload_b64 = base64.urlsafe_b64encode(
json.dumps(payload).encode('utf-8') json.dumps(payload).encode('utf-8')
).decode('utf-8').rstrip('=') ).decode('utf-8').rstrip('=')
# Create signature input # Create signature input
logging.CyberCPLogFileWriter.writeToFile('Creating signature input...') logging.CyberCPLogFileWriter.writeToFile('Creating signature input...')
signature_input = f"{protected_b64}.{payload_b64}".encode('utf-8') signature_input = f"{protected_b64}.{payload_b64}".encode('utf-8')
# Sign the input # Sign the input
logging.CyberCPLogFileWriter.writeToFile('Signing input...') logging.CyberCPLogFileWriter.writeToFile('Signing input...')
signature = self.account_key.sign( signature = self.account_key.sign(
@@ -169,26 +174,26 @@ class CustomACME:
padding.PKCS1v15(), padding.PKCS1v15(),
hashes.SHA256() hashes.SHA256()
) )
# Encode signature # Encode signature
logging.CyberCPLogFileWriter.writeToFile('Encoding signature...') logging.CyberCPLogFileWriter.writeToFile('Encoding signature...')
signature_b64 = base64.urlsafe_b64encode(signature).decode('utf-8').rstrip('=') signature_b64 = base64.urlsafe_b64encode(signature).decode('utf-8').rstrip('=')
# Create final JWS # Create final JWS
logging.CyberCPLogFileWriter.writeToFile('Creating final JWS...') logging.CyberCPLogFileWriter.writeToFile('Creating final JWS...')
jws = { jws = {
"protected": protected_b64, "protected": protected_b64,
"signature": signature_b64 "signature": signature_b64
} }
# Only add payload if it exists # Only add payload if it exists
if payload is not None: if payload is not None:
jws["payload"] = payload_b64 jws["payload"] = payload_b64
# Ensure the JWS is properly formatted # Ensure the JWS is properly formatted
jws_str = json.dumps(jws, separators=(',', ':')) jws_str = json.dumps(jws, separators=(',', ':'))
logging.CyberCPLogFileWriter.writeToFile(f'Final JWS: {jws_str}') logging.CyberCPLogFileWriter.writeToFile(f'Final JWS: {jws_str}')
return jws_str return jws_str
except Exception as e: except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error creating JWS: {str(e)}') logging.CyberCPLogFileWriter.writeToFile(f'Error creating JWS: {str(e)}')
@@ -238,12 +243,12 @@ class CustomACME:
"termsOfServiceAgreed": True, "termsOfServiceAgreed": True,
"contact": [f"mailto:{self.admin_email}"] "contact": [f"mailto:{self.admin_email}"]
} }
jws = self._create_jws(payload, self.directory['newAccount']) jws = self._create_jws(payload, self.directory['newAccount'])
if not jws: if not jws:
logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for account creation') logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for account creation')
return False return False
logging.CyberCPLogFileWriter.writeToFile('Sending account creation request...') logging.CyberCPLogFileWriter.writeToFile('Sending account creation request...')
headers = { headers = {
'Content-Type': 'application/jose+json' 'Content-Type': 'application/jose+json'
@@ -251,17 +256,15 @@ class CustomACME:
response = requests.post(self.directory['newAccount'], data=jws, headers=headers) response = requests.post(self.directory['newAccount'], data=jws, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Account creation response status: {response.status_code}') logging.CyberCPLogFileWriter.writeToFile(f'Account creation response status: {response.status_code}')
logging.CyberCPLogFileWriter.writeToFile(f'Account creation response: {response.text}') logging.CyberCPLogFileWriter.writeToFile(f'Account creation response: {response.text}')
if response.status_code == 201: if response.status_code == 201:
self.account_url = response.headers['Location'] self.account_url = response.headers['Location']
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(f'Successfully created account. Account URL: {self.account_url}')
f'Successfully created account. Account URL: {self.account_url}')
# Save the account key for future use # Save the account key for future use
self._save_account_key() self._save_account_key()
return True return True
elif response.status_code == 429: elif response.status_code == 429:
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile('Rate limit hit for account creation. Using staging environment...')
'Rate limit hit for account creation. Using staging environment...')
self.staging = True self.staging = True
self.acme_directory = "https://acme-staging-v02.api.letsencrypt.org/directory" self.acme_directory = "https://acme-staging-v02.api.letsencrypt.org/directory"
# Get new directory and nonce for staging # Get new directory and nonce for staging
@@ -289,12 +292,12 @@ class CustomACME:
payload = { payload = {
"identifiers": identifiers "identifiers": identifiers
} }
jws = self._create_jws(payload, self.directory['newOrder']) jws = self._create_jws(payload, self.directory['newOrder'])
if not jws: if not jws:
logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for order creation') logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for order creation')
return False return False
logging.CyberCPLogFileWriter.writeToFile('Sending order creation request...') logging.CyberCPLogFileWriter.writeToFile('Sending order creation request...')
headers = { headers = {
'Content-Type': 'application/jose+json' 'Content-Type': 'application/jose+json'
@@ -302,7 +305,7 @@ class CustomACME:
response = requests.post(self.directory['newOrder'], data=jws, headers=headers) response = requests.post(self.directory['newOrder'], data=jws, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Order creation response status: {response.status_code}') logging.CyberCPLogFileWriter.writeToFile(f'Order creation response status: {response.status_code}')
logging.CyberCPLogFileWriter.writeToFile(f'Order creation response: {response.text}') logging.CyberCPLogFileWriter.writeToFile(f'Order creation response: {response.text}')
if response.status_code == 201: if response.status_code == 201:
self.order_url = response.headers['Location'] self.order_url = response.headers['Location']
self.authorizations = response.json()['authorizations'] self.authorizations = response.json()['authorizations']
@@ -320,26 +323,26 @@ class CustomACME:
"""Handle HTTP-01 challenge""" """Handle HTTP-01 challenge"""
try: try:
logging.CyberCPLogFileWriter.writeToFile(f'Handling HTTP challenge: {json.dumps(challenge)}') logging.CyberCPLogFileWriter.writeToFile(f'Handling HTTP challenge: {json.dumps(challenge)}')
# Get key authorization # Get key authorization
key_auth = self._get_key_authorization(challenge) key_auth = self._get_key_authorization(challenge)
if not key_auth: if not key_auth:
logging.CyberCPLogFileWriter.writeToFile('Failed to get key authorization') logging.CyberCPLogFileWriter.writeToFile('Failed to get key authorization')
return False return False
# Create challenge directory if it doesn't exist # Create challenge directory if it doesn't exist
if not os.path.exists(self.challenge_path): if not os.path.exists(self.challenge_path):
logging.CyberCPLogFileWriter.writeToFile(f'Creating challenge directory: {self.challenge_path}') logging.CyberCPLogFileWriter.writeToFile(f'Creating challenge directory: {self.challenge_path}')
os.makedirs(self.challenge_path) os.makedirs(self.challenge_path)
# Write challenge file # Write challenge file
challenge_file = os.path.join(self.challenge_path, challenge['token']) challenge_file = os.path.join(self.challenge_path, challenge['token'])
logging.CyberCPLogFileWriter.writeToFile(f'Writing challenge file: {challenge_file}') logging.CyberCPLogFileWriter.writeToFile(f'Writing challenge file: {challenge_file}')
# Write only the key authorization to the file # Write only the key authorization to the file
with open(challenge_file, 'w') as f: with open(challenge_file, 'w') as f:
f.write(key_auth) f.write(key_auth)
logging.CyberCPLogFileWriter.writeToFile('Successfully handled HTTP challenge') logging.CyberCPLogFileWriter.writeToFile('Successfully handled HTTP challenge')
return True return True
except Exception as e: except Exception as e:
@@ -361,22 +364,22 @@ class CustomACME:
"""Get key authorization for challenge""" """Get key authorization for challenge"""
try: try:
logging.CyberCPLogFileWriter.writeToFile('Getting key authorization...') logging.CyberCPLogFileWriter.writeToFile('Getting key authorization...')
# Get the private key numbers # Get the private key numbers
private_numbers = self.account_key.private_numbers() private_numbers = self.account_key.private_numbers()
public_numbers = private_numbers.public_numbers public_numbers = private_numbers.public_numbers
# Convert numbers to bytes # Convert numbers to bytes
n_bytes = public_numbers.n.to_bytes((public_numbers.n.bit_length() + 7) // 8, 'big') n_bytes = public_numbers.n.to_bytes((public_numbers.n.bit_length() + 7) // 8, 'big')
e_bytes = public_numbers.e.to_bytes((public_numbers.e.bit_length() + 7) // 8, 'big') e_bytes = public_numbers.e.to_bytes((public_numbers.e.bit_length() + 7) // 8, 'big')
# Create JWK without alg field # Create JWK without alg field
jwk_key = { jwk_key = {
"kty": "RSA", "kty": "RSA",
"n": base64.urlsafe_b64encode(n_bytes).decode('utf-8').rstrip('='), "n": base64.urlsafe_b64encode(n_bytes).decode('utf-8').rstrip('='),
"e": base64.urlsafe_b64encode(e_bytes).decode('utf-8').rstrip('=') "e": base64.urlsafe_b64encode(e_bytes).decode('utf-8').rstrip('=')
} }
# Calculate the JWK thumbprint according to RFC 7638 # Calculate the JWK thumbprint according to RFC 7638
# The thumbprint is a hash of the JWK (JSON Web Key) in a specific format # The thumbprint is a hash of the JWK (JSON Web Key) in a specific format
# First, we create a dictionary with the required JWK parameters # First, we create a dictionary with the required JWK parameters
@@ -385,23 +388,23 @@ class CustomACME:
"kty": "RSA", # Key type "kty": "RSA", # Key type
"n": base64.urlsafe_b64encode(public_numbers.n.to_bytes(256, 'big')).decode('utf-8').rstrip('=') "n": base64.urlsafe_b64encode(public_numbers.n.to_bytes(256, 'big')).decode('utf-8').rstrip('=')
} }
# Sort the JWK parameters alphabetically by key name # Sort the JWK parameters alphabetically by key name
# This ensures consistent thumbprint calculation regardless of parameter order # This ensures consistent thumbprint calculation regardless of parameter order
sorted_jwk = json.dumps(jwk, sort_keys=True, separators=(',', ':')) sorted_jwk = json.dumps(jwk, sort_keys=True, separators=(',', ':'))
# Calculate the SHA-256 hash of the sorted JWK # Calculate the SHA-256 hash of the sorted JWK
# Example of what sorted_jwk might look like: # Example of what sorted_jwk might look like:
# {"e":"AQAB","kty":"RSA","n":"tVKUtcx_n9rt5afY_2WFNVAu9fjD4xqX4Xm3dJz3XYb"} # {"e":"AQAB","kty":"RSA","n":"tVKUtcx_n9rt5afY_2WFNVAu9fjD4xqX4Xm3dJz3XYb"}
# The thumbprint will be a 32-byte SHA-256 hash of this string # The thumbprint will be a 32-byte SHA-256 hash of this string
# For example, it might look like: b'x\x9c\x1d\x8f\x8b\x1b\x1e\x8b\x1b\x1e\x8b\x1b\x1e\x8b\x1b\x1e' # For example, it might look like: b'x\x9c\x1d\x8f\x8b\x1b\x1e\x8b\x1b\x1e\x8b\x1b\x1e\x8b\x1b\x1e'
thumbprint = hashlib.sha256(sorted_jwk.encode('utf-8')).digest() thumbprint = hashlib.sha256(sorted_jwk.encode('utf-8')).digest()
# Encode the thumbprint in base64url format (RFC 4648) # Encode the thumbprint in base64url format (RFC 4648)
# This removes padding characters (=) and replaces + and / with - and _ # This removes padding characters (=) and replaces + and / with - and _
# Example final thumbprint: "xJ0dj8sbHosbHosbHosbHos" # Example final thumbprint: "xJ0dj8sbHosbHosbHosbHos"
thumbprint = base64.urlsafe_b64encode(thumbprint).decode('utf-8').rstrip('=') thumbprint = base64.urlsafe_b64encode(thumbprint).decode('utf-8').rstrip('=')
# Combine token and key authorization # Combine token and key authorization
key_auth = f"{challenge['token']}.{thumbprint}" key_auth = f"{challenge['token']}.{thumbprint}"
logging.CyberCPLogFileWriter.writeToFile(f'Key authorization: {key_auth}') logging.CyberCPLogFileWriter.writeToFile(f'Key authorization: {key_auth}')
@@ -412,31 +415,31 @@ class CustomACME:
def _verify_challenge(self, challenge_url): def _verify_challenge(self, challenge_url):
"""Verify challenge completion with the ACME server """Verify challenge completion with the ACME server
This function sends a POST request to the ACME server to verify that the challenge This function sends a POST request to the ACME server to verify that the challenge
has been completed successfully. The challenge URL is provided by the ACME server has been completed successfully. The challenge URL is provided by the ACME server
when the challenge is created. when the challenge is created.
Example challenge_url: Example challenge_url:
"https://acme-v02.api.letsencrypt.org/acme/challenge/example.com/123456" "https://acme-v02.api.letsencrypt.org/acme/challenge/example.com/123456"
The verification process: The verification process:
1. Creates an empty payload (POST-as-GET request) 1. Creates an empty payload (POST-as-GET request)
2. Creates a JWS (JSON Web Signature) with the payload 2. Creates a JWS (JSON Web Signature) with the payload
3. Sends the request to the ACME server 3. Sends the request to the ACME server
4. Checks the response status 4. Checks the response status
Returns: Returns:
bool: True if challenge is verified successfully, False otherwise bool: True if challenge is verified successfully, False otherwise
""" """
try: try:
logging.CyberCPLogFileWriter.writeToFile(f'Verifying challenge at URL: {challenge_url}') logging.CyberCPLogFileWriter.writeToFile(f'Verifying challenge at URL: {challenge_url}')
# Create empty payload for POST-as-GET request # Create empty payload for POST-as-GET request
# This is a special type of request where we want to GET a resource # This is a special type of request where we want to GET a resource
# but need to include a signature, so we use POST with an empty payload # but need to include a signature, so we use POST with an empty payload
payload = {} payload = {}
# Create JWS (JSON Web Signature) for the request # Create JWS (JSON Web Signature) for the request
# Example JWS might look like: # Example JWS might look like:
# { # {
@@ -448,15 +451,15 @@ class CustomACME:
if not jws: if not jws:
logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for challenge verification') logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for challenge verification')
return False return False
logging.CyberCPLogFileWriter.writeToFile('Sending challenge verification request...') logging.CyberCPLogFileWriter.writeToFile('Sending challenge verification request...')
# Set headers for the request # Set headers for the request
# Content-Type: application/jose+json indicates we're sending a JWS # Content-Type: application/jose+json indicates we're sending a JWS
headers = { headers = {
'Content-Type': 'application/jose+json' 'Content-Type': 'application/jose+json'
} }
# Send the verification request to the ACME server # Send the verification request to the ACME server
# Example response might look like: # Example response might look like:
# { # {
@@ -468,7 +471,7 @@ class CustomACME:
response = requests.post(challenge_url, data=jws, headers=headers) response = requests.post(challenge_url, data=jws, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Challenge verification response status: {response.status_code}') logging.CyberCPLogFileWriter.writeToFile(f'Challenge verification response status: {response.status_code}')
logging.CyberCPLogFileWriter.writeToFile(f'Challenge verification response: {response.text}') logging.CyberCPLogFileWriter.writeToFile(f'Challenge verification response: {response.text}')
# Check if the challenge was verified successfully # Check if the challenge was verified successfully
# Status code 200 indicates success # Status code 200 indicates success
# The response will contain the challenge status and validation time # The response will contain the challenge status and validation time
@@ -487,12 +490,12 @@ class CustomACME:
payload = { payload = {
"csr": base64.urlsafe_b64encode(csr).decode('utf-8').rstrip('=') "csr": base64.urlsafe_b64encode(csr).decode('utf-8').rstrip('=')
} }
jws = self._create_jws(payload, self.finalize_url) jws = self._create_jws(payload, self.finalize_url)
if not jws: if not jws:
logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for order finalization') logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for order finalization')
return False return False
logging.CyberCPLogFileWriter.writeToFile('Sending order finalization request...') logging.CyberCPLogFileWriter.writeToFile('Sending order finalization request...')
headers = { headers = {
'Content-Type': 'application/jose+json' 'Content-Type': 'application/jose+json'
@@ -500,7 +503,7 @@ class CustomACME:
response = requests.post(self.finalize_url, data=jws, headers=headers) response = requests.post(self.finalize_url, data=jws, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Order finalization response status: {response.status_code}') logging.CyberCPLogFileWriter.writeToFile(f'Order finalization response status: {response.status_code}')
logging.CyberCPLogFileWriter.writeToFile(f'Order finalization response: {response.text}') logging.CyberCPLogFileWriter.writeToFile(f'Order finalization response: {response.text}')
if response.status_code == 200: if response.status_code == 200:
# Wait for order to be processed # Wait for order to be processed
max_attempts = 30 max_attempts = 30
@@ -509,30 +512,27 @@ class CustomACME:
if not self._get_nonce(): if not self._get_nonce():
logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for order status check') logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for order status check')
return False return False
response = requests.get(self.order_url, headers=headers) response = requests.get(self.order_url, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Order status check response: {response.text}') logging.CyberCPLogFileWriter.writeToFile(f'Order status check response: {response.text}')
if response.status_code == 200: if response.status_code == 200:
order_status = response.json().get('status') order_status = response.json().get('status')
if order_status == 'valid': if order_status == 'valid':
self.certificate_url = response.json().get('certificate') self.certificate_url = response.json().get('certificate')
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(f'Successfully finalized order. Certificate URL: {self.certificate_url}')
f'Successfully finalized order. Certificate URL: {self.certificate_url}')
return True return True
elif order_status == 'invalid': elif order_status == 'invalid':
logging.CyberCPLogFileWriter.writeToFile('Order validation failed') logging.CyberCPLogFileWriter.writeToFile('Order validation failed')
return False return False
elif order_status == 'processing': elif order_status == 'processing':
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(f'Order still processing, attempt {attempt + 1}/{max_attempts}')
f'Order still processing, attempt {attempt + 1}/{max_attempts}')
time.sleep(delay) time.sleep(delay)
continue continue
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(f'Order status check failed, attempt {attempt + 1}/{max_attempts}')
f'Order status check failed, attempt {attempt + 1}/{max_attempts}')
time.sleep(delay) time.sleep(delay)
logging.CyberCPLogFileWriter.writeToFile('Order processing timed out') logging.CyberCPLogFileWriter.writeToFile('Order processing timed out')
return False return False
return False return False
@@ -545,13 +545,13 @@ class CustomACME:
try: try:
logging.CyberCPLogFileWriter.writeToFile('Downloading certificate...') logging.CyberCPLogFileWriter.writeToFile('Downloading certificate...')
logging.CyberCPLogFileWriter.writeToFile(f'Certificate URL: {self.certificate_url}') logging.CyberCPLogFileWriter.writeToFile(f'Certificate URL: {self.certificate_url}')
# For certificate downloads, we can use a simple GET request # For certificate downloads, we can use a simple GET request
response = requests.get(self.certificate_url) response = requests.get(self.certificate_url)
logging.CyberCPLogFileWriter.writeToFile(f'Certificate download response status: {response.status_code}') logging.CyberCPLogFileWriter.writeToFile(f'Certificate download response status: {response.status_code}')
logging.CyberCPLogFileWriter.writeToFile(f'Certificate download response headers: {response.headers}') logging.CyberCPLogFileWriter.writeToFile(f'Certificate download response headers: {response.headers}')
logging.CyberCPLogFileWriter.writeToFile(f'Certificate download response content: {response.text}') logging.CyberCPLogFileWriter.writeToFile(f'Certificate download response content: {response.text}')
if response.status_code == 200: if response.status_code == 200:
logging.CyberCPLogFileWriter.writeToFile('Successfully downloaded certificate') logging.CyberCPLogFileWriter.writeToFile('Successfully downloaded certificate')
return response.content return response.content
@@ -568,13 +568,13 @@ class CustomACME:
if not self._get_nonce(): if not self._get_nonce():
logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for challenge status check') logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for challenge status check')
return False return False
headers = { headers = {
'Content-Type': 'application/jose+json' 'Content-Type': 'application/jose+json'
} }
response = requests.get(challenge_url, headers=headers) response = requests.get(challenge_url, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Challenge status check response: {response.text}') logging.CyberCPLogFileWriter.writeToFile(f'Challenge status check response: {response.text}')
if response.status_code == 200: if response.status_code == 200:
challenge_status = response.json().get('status') challenge_status = response.json().get('status')
if challenge_status == 'valid': if challenge_status == 'valid':
@@ -583,11 +583,10 @@ class CustomACME:
elif challenge_status == 'invalid': elif challenge_status == 'invalid':
logging.CyberCPLogFileWriter.writeToFile('Challenge validation failed') logging.CyberCPLogFileWriter.writeToFile('Challenge validation failed')
return False return False
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(f'Challenge still pending, attempt {attempt + 1}/{max_attempts}')
f'Challenge still pending, attempt {attempt + 1}/{max_attempts}')
time.sleep(delay) time.sleep(delay)
logging.CyberCPLogFileWriter.writeToFile('Challenge validation timed out') logging.CyberCPLogFileWriter.writeToFile('Challenge validation timed out')
return False return False
except Exception as e: except Exception as e:
@@ -596,40 +595,40 @@ class CustomACME:
def _check_dns_record(self, domain): def _check_dns_record(self, domain):
"""Check if a domain has valid DNS records """Check if a domain has valid DNS records
This function performs multiple DNS checks to ensure the domain has valid DNS records. This function performs multiple DNS checks to ensure the domain has valid DNS records.
It includes: It includes:
1. A record (IPv4) check 1. A record (IPv4) check
2. AAAA record (IPv6) check 2. AAAA record (IPv6) check
3. DNS caching prevention 3. DNS caching prevention
4. Multiple DNS server checks 4. Multiple DNS server checks
Args: Args:
domain (str): The domain to check domain (str): The domain to check
Returns: Returns:
bool: True if valid DNS records are found, False otherwise bool: True if valid DNS records are found, False otherwise
""" """
try: try:
logging.CyberCPLogFileWriter.writeToFile(f'Checking DNS records for domain: {domain}') logging.CyberCPLogFileWriter.writeToFile(f'Checking DNS records for domain: {domain}')
# List of public DNS servers to check against # List of public DNS servers to check against
dns_servers = [ dns_servers = [
'8.8.8.8', # Google DNS '8.8.8.8', # Google DNS
'1.1.1.1', # Cloudflare DNS '1.1.1.1', # Cloudflare DNS
'208.67.222.222' # OpenDNS '208.67.222.222' # OpenDNS
] ]
# Function to check DNS record with specific DNS server # Function to check DNS record with specific DNS server
def check_with_dns_server(server, record_type='A'): def check_with_dns_server(server, record_type='A'):
try: try:
# Create a new socket for each check # Create a new socket for each check
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(5) # 5 second timeout sock.settimeout(5) # 5 second timeout
# Set the DNS server # Set the DNS server
sock.connect((server, 53)) sock.connect((server, 53))
# Create DNS query # Create DNS query
query = bytearray() query = bytearray()
# DNS header # DNS header
@@ -639,54 +638,54 @@ class CustomACME:
query += b'\x00\x00' # Answer RRs: 0 query += b'\x00\x00' # Answer RRs: 0
query += b'\x00\x00' # Authority RRs: 0 query += b'\x00\x00' # Authority RRs: 0
query += b'\x00\x00' # Additional RRs: 0 query += b'\x00\x00' # Additional RRs: 0
# Domain name # Domain name
for part in domain.split('.'): for part in domain.split('.'):
query.append(len(part)) query.append(len(part))
query.extend(part.encode()) query.extend(part.encode())
query += b'\x00' # End of domain name query += b'\x00' # End of domain name
# Query type and class # Query type and class
if record_type == 'A': if record_type == 'A':
query += b'\x00\x01' # Type: A query += b'\x00\x01' # Type: A
else: # AAAA else: # AAAA
query += b'\x00\x1c' # Type: AAAA query += b'\x00\x1c' # Type: AAAA
query += b'\x00\x01' # Class: IN query += b'\x00\x01' # Class: IN
# Send query # Send query
sock.send(query) sock.send(query)
# Receive response # Receive response
response = sock.recv(1024) response = sock.recv(1024)
# Check if we got a valid response # Check if we got a valid response
if len(response) > 12: # Minimum DNS response size if len(response) > 12: # Minimum DNS response size
# Check if there are answers in the response # Check if there are answers in the response
answer_count = int.from_bytes(response[6:8], 'big') answer_count = int.from_bytes(response[6:8], 'big')
if answer_count > 0: if answer_count > 0:
return True return True
return False return False
except Exception as e: except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error checking DNS with server {server}: {str(e)}') logging.CyberCPLogFileWriter.writeToFile(f'Error checking DNS with server {server}: {str(e)}')
return False return False
finally: finally:
sock.close() sock.close()
# Check A records (IPv4) with multiple DNS servers # Check A records (IPv4) with multiple DNS servers
a_record_found = False a_record_found = False
for server in dns_servers: for server in dns_servers:
if check_with_dns_server(server, 'A'): if check_with_dns_server(server, 'A'):
a_record_found = True a_record_found = True
break break
# Check AAAA records (IPv6) with multiple DNS servers # Check AAAA records (IPv6) with multiple DNS servers
aaaa_record_found = False aaaa_record_found = False
for server in dns_servers: for server in dns_servers:
if check_with_dns_server(server, 'AAAA'): if check_with_dns_server(server, 'AAAA'):
aaaa_record_found = True aaaa_record_found = True
break break
# Also check with system's DNS resolver as a fallback # Also check with system's DNS resolver as a fallback
try: try:
# Try to resolve A record (IPv4) # Try to resolve A record (IPv4)
@@ -694,23 +693,23 @@ class CustomACME:
a_record_found = True a_record_found = True
except socket.gaierror: except socket.gaierror:
pass pass
try: try:
# Try to resolve AAAA record (IPv6) # Try to resolve AAAA record (IPv6)
socket.getaddrinfo(domain, None, socket.AF_INET6) socket.getaddrinfo(domain, None, socket.AF_INET6)
aaaa_record_found = True aaaa_record_found = True
except socket.gaierror: except socket.gaierror:
pass pass
# Log the results # Log the results
if a_record_found: if a_record_found:
logging.CyberCPLogFileWriter.writeToFile(f'IPv4 DNS record found for domain: {domain}') logging.CyberCPLogFileWriter.writeToFile(f'IPv4 DNS record found for domain: {domain}')
if aaaa_record_found: if aaaa_record_found:
logging.CyberCPLogFileWriter.writeToFile(f'IPv6 DNS record found for domain: {domain}') logging.CyberCPLogFileWriter.writeToFile(f'IPv6 DNS record found for domain: {domain}')
# Return True if either A or AAAA record is found # Return True if either A or AAAA record is found
return a_record_found or aaaa_record_found return a_record_found or aaaa_record_found
except Exception as e: except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error checking DNS records: {str(e)}') logging.CyberCPLogFileWriter.writeToFile(f'Error checking DNS records: {str(e)}')
return False return False
@@ -723,13 +722,13 @@ class CustomACME:
if not self._get_nonce(): if not self._get_nonce():
logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for order status check') logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for order status check')
return False return False
headers = { headers = {
'Content-Type': 'application/jose+json' 'Content-Type': 'application/jose+json'
} }
response = requests.get(self.order_url, headers=headers) response = requests.get(self.order_url, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Order status check response: {response.text}') logging.CyberCPLogFileWriter.writeToFile(f'Order status check response: {response.text}')
if response.status_code == 200: if response.status_code == 200:
order_status = response.json().get('status') order_status = response.json().get('status')
if order_status == 'valid': if order_status == 'valid':
@@ -740,15 +739,13 @@ class CustomACME:
logging.CyberCPLogFileWriter.writeToFile('Order validation failed') logging.CyberCPLogFileWriter.writeToFile('Order validation failed')
return False return False
elif order_status == 'processing': elif order_status == 'processing':
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(f'Order still processing, attempt {attempt + 1}/{max_attempts}')
f'Order still processing, attempt {attempt + 1}/{max_attempts}')
time.sleep(delay) time.sleep(delay)
continue continue
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(f'Order status check failed, attempt {attempt + 1}/{max_attempts}')
f'Order status check failed, attempt {attempt + 1}/{max_attempts}')
time.sleep(delay) time.sleep(delay)
logging.CyberCPLogFileWriter.writeToFile('Order processing timed out') logging.CyberCPLogFileWriter.writeToFile('Order processing timed out')
return False return False
except Exception as e: except Exception as e:
@@ -758,15 +755,14 @@ class CustomACME:
def issue_certificate(self, domains, use_dns=False): def issue_certificate(self, domains, use_dns=False):
"""Main method to issue certificate""" """Main method to issue certificate"""
try: try:
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(f'Starting certificate issuance for domains: {domains}, use_dns: {use_dns}')
f'Starting certificate issuance for domains: {domains}, use_dns: {use_dns}')
# Try to load existing account key first # Try to load existing account key first
if self._load_account_key(): if self._load_account_key():
logging.CyberCPLogFileWriter.writeToFile('Using existing account key') logging.CyberCPLogFileWriter.writeToFile('Using existing account key')
else: else:
logging.CyberCPLogFileWriter.writeToFile('No existing account key found, will create new one') logging.CyberCPLogFileWriter.writeToFile('No existing account key found, will create new one')
# Filter domains to only include those with valid DNS records # Filter domains to only include those with valid DNS records
valid_domains = [] valid_domains = []
for domain in domains: for domain in domains:
@@ -774,27 +770,27 @@ class CustomACME:
valid_domains.append(domain) valid_domains.append(domain)
else: else:
logging.CyberCPLogFileWriter.writeToFile(f'Skipping domain {domain} due to missing DNS records') logging.CyberCPLogFileWriter.writeToFile(f'Skipping domain {domain} due to missing DNS records')
if not valid_domains: if not valid_domains:
logging.CyberCPLogFileWriter.writeToFile('No valid domains found with DNS records') logging.CyberCPLogFileWriter.writeToFile('No valid domains found with DNS records')
return False return False
# Initialize ACME # Initialize ACME
logging.CyberCPLogFileWriter.writeToFile('Step 1: Generating account key') logging.CyberCPLogFileWriter.writeToFile('Step 1: Generating account key')
if not self._generate_account_key(): if not self._generate_account_key():
logging.CyberCPLogFileWriter.writeToFile('Failed to generate account key') logging.CyberCPLogFileWriter.writeToFile('Failed to generate account key')
return False return False
logging.CyberCPLogFileWriter.writeToFile('Step 2: Getting ACME directory') logging.CyberCPLogFileWriter.writeToFile('Step 2: Getting ACME directory')
if not self._get_directory(): if not self._get_directory():
logging.CyberCPLogFileWriter.writeToFile('Failed to get ACME directory') logging.CyberCPLogFileWriter.writeToFile('Failed to get ACME directory')
return False return False
logging.CyberCPLogFileWriter.writeToFile('Step 3: Getting nonce') logging.CyberCPLogFileWriter.writeToFile('Step 3: Getting nonce')
if not self._get_nonce(): if not self._get_nonce():
logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce') logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce')
return False return False
logging.CyberCPLogFileWriter.writeToFile('Step 4: Creating account') logging.CyberCPLogFileWriter.writeToFile('Step 4: Creating account')
if not self._create_account(): if not self._create_account():
logging.CyberCPLogFileWriter.writeToFile('Failed to create account') logging.CyberCPLogFileWriter.writeToFile('Failed to create account')
@@ -811,13 +807,13 @@ class CustomACME:
return False return False
else: else:
return False return False
# Create order with only valid domains # Create order with only valid domains
logging.CyberCPLogFileWriter.writeToFile('Step 5: Creating order') logging.CyberCPLogFileWriter.writeToFile('Step 5: Creating order')
if not self._create_order(valid_domains): if not self._create_order(valid_domains):
logging.CyberCPLogFileWriter.writeToFile('Failed to create order') logging.CyberCPLogFileWriter.writeToFile('Failed to create order')
return False return False
# Handle challenges # Handle challenges
logging.CyberCPLogFileWriter.writeToFile('Step 6: Handling challenges') logging.CyberCPLogFileWriter.writeToFile('Step 6: Handling challenges')
for auth_url in self.authorizations: for auth_url in self.authorizations:
@@ -825,7 +821,7 @@ class CustomACME:
if not self._get_nonce(): if not self._get_nonce():
logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for authorization') logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for authorization')
return False return False
# Get authorization details with GET request # Get authorization details with GET request
headers = { headers = {
'Content-Type': 'application/jose+json' 'Content-Type': 'application/jose+json'
@@ -833,15 +829,15 @@ class CustomACME:
response = requests.get(auth_url, headers=headers) response = requests.get(auth_url, headers=headers)
logging.CyberCPLogFileWriter.writeToFile(f'Authorization response status: {response.status_code}') logging.CyberCPLogFileWriter.writeToFile(f'Authorization response status: {response.status_code}')
logging.CyberCPLogFileWriter.writeToFile(f'Authorization response: {response.text}') logging.CyberCPLogFileWriter.writeToFile(f'Authorization response: {response.text}')
if response.status_code != 200: if response.status_code != 200:
logging.CyberCPLogFileWriter.writeToFile('Failed to get authorization') logging.CyberCPLogFileWriter.writeToFile('Failed to get authorization')
return False return False
challenges = response.json()['challenges'] challenges = response.json()['challenges']
for challenge in challenges: for challenge in challenges:
logging.CyberCPLogFileWriter.writeToFile(f'Processing challenge: {json.dumps(challenge)}') logging.CyberCPLogFileWriter.writeToFile(f'Processing challenge: {json.dumps(challenge)}')
# Only handle the challenge type we're using # Only handle the challenge type we're using
if use_dns and challenge['type'] == 'dns-01': if use_dns and challenge['type'] == 'dns-01':
if not self._handle_dns_challenge(challenge): if not self._handle_dns_challenge(challenge):
@@ -865,7 +861,7 @@ class CustomACME:
return False return False
else: else:
logging.CyberCPLogFileWriter.writeToFile(f'Skipping {challenge["type"]} challenge') logging.CyberCPLogFileWriter.writeToFile(f'Skipping {challenge["type"]} challenge')
# Generate CSR # Generate CSR
logging.CyberCPLogFileWriter.writeToFile('Step 7: Generating CSR') logging.CyberCPLogFileWriter.writeToFile('Step 7: Generating CSR')
key = rsa.generate_private_key( key = rsa.generate_private_key(
@@ -873,12 +869,12 @@ class CustomACME:
key_size=2048, key_size=2048,
backend=default_backend() backend=default_backend()
) )
# Get the domain from the order response # Get the domain from the order response
order_response = requests.get(self.order_url, headers=headers).json() order_response = requests.get(self.order_url, headers=headers).json()
order_domains = [identifier['value'] for identifier in order_response['identifiers']] order_domains = [identifier['value'] for identifier in order_response['identifiers']]
logging.CyberCPLogFileWriter.writeToFile(f'Order domains: {order_domains}') logging.CyberCPLogFileWriter.writeToFile(f'Order domains: {order_domains}')
# Create CSR with exactly the domains from the order # Create CSR with exactly the domains from the order
csr = x509.CertificateSigningRequestBuilder().subject_name( csr = x509.CertificateSigningRequestBuilder().subject_name(
x509.Name([ x509.Name([
@@ -890,39 +886,39 @@ class CustomACME:
]), ]),
critical=False critical=False
).sign(key, hashes.SHA256(), default_backend()) ).sign(key, hashes.SHA256(), default_backend())
# Finalize order # Finalize order
logging.CyberCPLogFileWriter.writeToFile('Step 8: Finalizing order') logging.CyberCPLogFileWriter.writeToFile('Step 8: Finalizing order')
if not self._finalize_order(csr.public_bytes(serialization.Encoding.DER)): if not self._finalize_order(csr.public_bytes(serialization.Encoding.DER)):
logging.CyberCPLogFileWriter.writeToFile('Failed to finalize order') logging.CyberCPLogFileWriter.writeToFile('Failed to finalize order')
return False return False
# Wait for order processing # Wait for order processing
logging.CyberCPLogFileWriter.writeToFile('Step 9: Waiting for order processing') logging.CyberCPLogFileWriter.writeToFile('Step 9: Waiting for order processing')
if not self._wait_for_order_processing(): if not self._wait_for_order_processing():
logging.CyberCPLogFileWriter.writeToFile('Failed to process order') logging.CyberCPLogFileWriter.writeToFile('Failed to process order')
return False return False
# Download certificate # Download certificate
logging.CyberCPLogFileWriter.writeToFile('Step 10: Downloading certificate') logging.CyberCPLogFileWriter.writeToFile('Step 10: Downloading certificate')
certificate = self._download_certificate() certificate = self._download_certificate()
if not certificate: if not certificate:
logging.CyberCPLogFileWriter.writeToFile('Failed to download certificate') logging.CyberCPLogFileWriter.writeToFile('Failed to download certificate')
return False return False
# Save certificate # Save certificate
logging.CyberCPLogFileWriter.writeToFile('Step 11: Saving certificate') logging.CyberCPLogFileWriter.writeToFile('Step 11: Saving certificate')
if not os.path.exists(self.cert_path): if not os.path.exists(self.cert_path):
logging.CyberCPLogFileWriter.writeToFile(f'Creating certificate directory: {self.cert_path}') logging.CyberCPLogFileWriter.writeToFile(f'Creating certificate directory: {self.cert_path}')
os.makedirs(self.cert_path) os.makedirs(self.cert_path)
cert_file = os.path.join(self.cert_path, 'fullchain.pem') cert_file = os.path.join(self.cert_path, 'fullchain.pem')
key_file = os.path.join(self.cert_path, 'privkey.pem') key_file = os.path.join(self.cert_path, 'privkey.pem')
logging.CyberCPLogFileWriter.writeToFile(f'Saving certificate to: {cert_file}') logging.CyberCPLogFileWriter.writeToFile(f'Saving certificate to: {cert_file}')
with open(cert_file, 'wb') as f: with open(cert_file, 'wb') as f:
f.write(certificate) f.write(certificate)
logging.CyberCPLogFileWriter.writeToFile(f'Saving private key to: {key_file}') logging.CyberCPLogFileWriter.writeToFile(f'Saving private key to: {key_file}')
with open(key_file, 'wb') as f: with open(key_file, 'wb') as f:
f.write(key.private_bytes( f.write(key.private_bytes(
@@ -930,9 +926,9 @@ class CustomACME:
format=serialization.PrivateFormat.PKCS8, format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption() encryption_algorithm=serialization.NoEncryption()
)) ))
logging.CyberCPLogFileWriter.writeToFile('Successfully completed certificate issuance') logging.CyberCPLogFileWriter.writeToFile('Successfully completed certificate issuance')
return True return True
except Exception as e: except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error issuing certificate: {str(e)}') logging.CyberCPLogFileWriter.writeToFile(f'Error issuing certificate: {str(e)}')
return False return False

View File

@@ -169,27 +169,31 @@ class ProcessUtilities(multi.Thread):
distroPath = '/etc/lsb-release' distroPath = '/etc/lsb-release'
distroPathAlma = '/etc/redhat-release' distroPathAlma = '/etc/redhat-release'
if os.path.exists(distroPath): # First check if we're on Ubuntu
if os.path.exists('/etc/os-release'):
## this is check only with open('/etc/os-release', 'r') as f:
if open(distroPath, 'r').read().find('22.04') > -1: content = f.read()
ProcessUtilities.ubuntu22Check = 1 if 'Ubuntu' in content:
if '22.04' in content:
ProcessUtilities.ubuntu22Check = 1
return ProcessUtilities.ubuntu20
elif '20.04' in content:
return ProcessUtilities.ubuntu20
return ProcessUtilities.ubuntu
if open(distroPath, 'r').read().find('20.04') > -1 or open(distroPath, 'r').read().find('22.04'): # Check for RedHat-based distributions
return ProcessUtilities.ubuntu20 if os.path.exists(distroPathAlma):
return ProcessUtilities.ubuntu with open(distroPathAlma, 'r') as f:
else: content = f.read()
if open('/etc/redhat-release', 'r').read().find('CentOS Linux release 8') > -1 or open('/etc/redhat-release', 'r').read().find('AlmaLinux release 8') > -1 \ if any(x in content for x in ['CentOS Linux release 8', 'AlmaLinux release 8', 'Rocky Linux release 8',
or open('/etc/redhat-release', 'r').read().find('Rocky Linux release 8') > -1 \ 'Rocky Linux release 9', 'AlmaLinux release 9', 'CloudLinux release 9',
or open('/etc/redhat-release', 'r').read().find('Rocky Linux release 9') > -1 or open('/etc/redhat-release', 'r').read().find('AlmaLinux release 9') > -1 or \ 'CloudLinux release 8']):
open('/etc/redhat-release', 'r').read().find('CloudLinux release 9') > -1 or open('/etc/redhat-release', 'r').read().find('CloudLinux release 8') > -1: if any(x in content for x in ['AlmaLinux release 9', 'Rocky Linux release 9']):
## this is check only ProcessUtilities.alma9check = 1
if open(distroPathAlma, 'r').read().find('AlmaLinux release 9') > -1 or open(distroPathAlma, 'r').read().find('Rocky Linux release 9') > -1: return ProcessUtilities.cent8
ProcessUtilities.alma9check = 1
return ProcessUtilities.cent8
return ProcessUtilities.centos
# Default to Ubuntu if no other distribution is detected
return ProcessUtilities.ubuntu
@staticmethod @staticmethod
def containerCheck(): def containerCheck():

View File

@@ -3,150 +3,113 @@ import os
import os.path import os.path
import sys import sys
import django import django
from typing import Union, Optional
from datetime import datetime, timedelta
import time
sys.path.append('/usr/local/CyberCP') sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
django.setup() django.setup()
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
from websiteFunctions.models import Websites, ChildDomains from websiteFunctions.models import Websites, ChildDomains
from os import path
from datetime import datetime
import OpenSSL import OpenSSL
from plogical.virtualHostUtilities import virtualHostUtilities from plogical.virtualHostUtilities import virtualHostUtilities
from plogical.processUtilities import ProcessUtilities
class Renew: class Renew:
def _check_and_renew_ssl(self, domain: str, path: str, admin_email: str, is_child: bool = False) -> None:
"""Helper method to check and renew SSL for a domain."""
try:
logging.writeToFile(f'Checking SSL for {domain}.', 0)
file_path = f'/etc/letsencrypt/live/{domain}/fullchain.pem'
if not os.path.exists(file_path):
logging.writeToFile(f'SSL does not exist for {domain}. Obtaining now..', 0)
virtualHostUtilities.issueSSL(domain, path, admin_email)
return
logging.writeToFile(f'SSL exists for {domain}. Checking if SSL will expire in 15 days..', 0)
with open(file_path, 'r') as cert_file:
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_file.read())
expire_data = x509.get_notAfter().decode('ascii')
final_date = datetime.strptime(expire_data, '%Y%m%d%H%M%SZ')
now = datetime.now()
diff = final_date - now
ssl_provider = x509.get_issuer().get_components()[1][1].decode('utf-8')
logging.writeToFile(f'Provider: {ssl_provider}, Days until expiration: {diff.days}', 0)
if diff.days >= 15 and ssl_provider != 'Denial':
logging.writeToFile(f'SSL exists for {domain} and is not ready to renew, skipping..', 0)
return
if ssl_provider == 'Denial' or ssl_provider == "Let's Encrypt":
logging.writeToFile(f'SSL exists for {domain} and ready to renew..', 0)
logging.writeToFile(f'Renewing SSL for {domain}..', 0)
virtualHostUtilities.issueSSL(domain, path, admin_email)
elif ssl_provider != "Let's Encrypt":
logging.writeToFile(f'Custom SSL exists for {domain} and ready to renew..', 1)
except OpenSSL.crypto.Error as e:
logging.writeToFile(f'OpenSSL error for {domain}: {str(e)}', 1)
except Exception as e:
logging.writeToFile(f'Error processing SSL for {domain}: {str(e)}', 1)
def _restart_services(self) -> None:
"""Helper method to restart required services."""
try:
logging.writeToFile('Restarting mail services for them to see new SSL.', 0)
commands = [
'postmap -F hash:/etc/postfix/vmail_ssl.map',
'systemctl restart postfix',
'systemctl restart dovecot',
'systemctl restart lscpd'
]
for cmd in commands:
ProcessUtilities.normalExecutioner(cmd)
# Add a small delay between restarts
time.sleep(2)
except Exception as e:
logging.writeToFile(f'Error restarting services: {str(e)}', 1)
def SSLObtainer(self): def SSLObtainer(self):
try: try:
logging.writeToFile('Running SSL Renew Utility') logging.writeToFile('Running SSL Renew Utility')
## For Non-suspended websites only # Process main domains
for website in Websites.objects.filter(state=1): for website in Websites.objects.filter(state=1):
logging.writeToFile('Checking SSL for %s.' % (website.domain), 0) self._check_and_renew_ssl(
filePath = '/etc/letsencrypt/live/%s/fullchain.pem' % (website.domain) website.domain,
f'/home/{website.domain}/public_html',
website.adminEmail
)
if path.exists(filePath): # Process child domains
logging.writeToFile('SSL exists for %s. Checking if SSL will expire in 15 days..' % (website.domain), 0) for child in ChildDomains.objects.all():
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, self._check_and_renew_ssl(
open(filePath, 'r').read()) child.domain,
expireData = x509.get_notAfter().decode('ascii') child.path,
finalDate = datetime.strptime(expireData, '%Y%m%d%H%M%SZ') child.master.adminEmail,
now = datetime.now() is_child=True
diff = finalDate - now )
SSLProvider = x509.get_issuer().get_components()[1][1].decode('utf-8') self._restart_services()
print(f"Provider: {x509.get_issuer().get_components()[1][1].decode('utf-8')}, Days : {diff.days}") except Exception as e:
logging.writeToFile(f'Error in SSLObtainer: {str(e)}', 1)
if int(diff.days) >= 15 and SSLProvider!='Denial':
logging.writeToFile(
'SSL exists for %s and is not ready to renew, skipping..' % (website.domain), 0)
print(
f'SSL exists for %s and is not ready to renew, skipping..' % (website.domain))
elif SSLProvider == 'Denial':
logging.writeToFile(
'SSL exists for %s and ready to renew..' % (website.domain), 0)
logging.writeToFile(
'Renewing SSL for %s..' % (website.domain), 0)
print(
f'SSL exists for %s and ready to renew..' % (website.domain))
virtualHostUtilities.issueSSL(website.domain, '/home/%s/public_html' % (website.domain),
website.adminEmail)
elif SSLProvider != "Let's Encrypt":
logging.writeToFile(
'Custom SSL exists for %s and ready to renew..' % (website.domain), 1)
print(
'Custom SSL exists for %s and ready to renew..' % (website.domain))
else:
logging.writeToFile(
'SSL exists for %s and ready to renew..' % (website.domain), 0)
logging.writeToFile(
'Renewing SSL for %s..' % (website.domain), 0)
print(
'SSL exists for %s and ready to renew..' % (website.domain))
virtualHostUtilities.issueSSL(website.domain, '/home/%s/public_html' % (website.domain), website.adminEmail)
else:
logging.writeToFile(
'SSL does not exist for %s. Obtaining now..' % (website.domain), 0)
virtualHostUtilities.issueSSL(website.domain, '/home/%s/public_html' % (website.domain),
website.adminEmail)
## For child-domains
for website in ChildDomains.objects.all():
logging.writeToFile('Checking SSL for %s.' % (website.domain), 0)
filePath = '/etc/letsencrypt/live/%s/fullchain.pem' % (website.domain)
if path.exists(filePath):
logging.writeToFile(
'SSL exists for %s. Checking if SSL will expire in 15 days..' % (website.domain), 0)
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
open(filePath, 'r').read())
expireData = x509.get_notAfter().decode('ascii')
finalDate = datetime.strptime(expireData, '%Y%m%d%H%M%SZ')
now = datetime.now()
diff = finalDate - now
SSLProvider = x509.get_issuer().get_components()[1][1]
print(f"Provider: {x509.get_issuer().get_components()[1][1].decode('utf-8')}, Days : {diff.days}")
if int(diff.days) >= 15 and SSLProvider != 'Denial':
logging.writeToFile(
'SSL exists for %s and is not ready to renew, skipping..' % (website.domain), 0)
elif SSLProvider == 'Denial':
logging.writeToFile(
'SSL exists for %s and ready to renew..' % (website.domain), 0)
logging.writeToFile(
'Renewing SSL for %s..' % (website.domain), 0)
virtualHostUtilities.issueSSL(website.domain, website.path,
website.master.adminEmail)
elif SSLProvider != "Let's Encrypt":
logging.writeToFile(
'Custom SSL exists for %s and ready to renew..' % (website.domain), 1)
else:
logging.writeToFile(
'SSL exists for %s and ready to renew..' % (website.domain), 0)
logging.writeToFile(
'Renewing SSL for %s..' % (website.domain), 0)
virtualHostUtilities.issueSSL(website.domain, website.path,
website.master.adminEmail)
else:
logging.writeToFile(
'SSL does not exist for %s. Obtaining now..' % (website.domain), 0)
virtualHostUtilities.issueSSL(website.domain, website.path,
website.master.adminEmail)
self.file = logging.writeToFile('Restarting mail services for them to see new SSL.', 0)
from plogical.processUtilities import ProcessUtilities
command = 'postmap -F hash:/etc/postfix/vmail_ssl.map'
ProcessUtilities.normalExecutioner(command)
command = 'systemctl restart postfix'
ProcessUtilities.normalExecutioner(command)
command = 'systemctl restart dovecot'
ProcessUtilities.normalExecutioner(command)
command = 'systemctl restart lscpd'
ProcessUtilities.normalExecutioner(command)
except BaseException as msg:
logging.writeToFile(str(msg) + '. Renew.SSLObtainer')
@staticmethod @staticmethod
def FixMailSSL(): def FixMailSSL():
for website in Websites.objects.all(): try:
virtualHostUtilities.setupAutoDiscover(1, '/home/cyberpanel/templogs', website.domain, website.admin) for website in Websites.objects.all():
virtualHostUtilities.setupAutoDiscover(1, '/home/cyberpanel/templogs', website.domain, website.admin)
except Exception as e:
logging.writeToFile(f'Error in FixMailSSL: {str(e)}', 1)
if __name__ == "__main__": if __name__ == "__main__":
sslOB = Renew() sslOB = Renew()

View File

@@ -173,14 +173,14 @@ class sslUtilities:
@staticmethod @staticmethod
def PatchVhostConf(virtualHostName): def PatchVhostConf(virtualHostName):
"""Patch the virtual host configuration to add ACME challenge support """Patch the virtual host configuration to add ACME challenge support
This function adds the necessary configuration to handle ACME challenges This function adds the necessary configuration to handle ACME challenges
for both OpenLiteSpeed (OLS) and Apache configurations. It also checks for both OpenLiteSpeed (OLS) and Apache configurations. It also checks
for potential configuration conflicts before making changes. for potential configuration conflicts before making changes.
Args: Args:
virtualHostName (str): The domain name to configure virtualHostName (str): The domain name to configure
Returns: Returns:
tuple: (status, message) where status is 1 for success, 0 for failure tuple: (status, message) where status is 1 for success, 0 for failure
""" """
@@ -188,12 +188,12 @@ class sslUtilities:
# Construct paths # Construct paths
confPath = os.path.join(sslUtilities.Server_root, "conf", "vhosts", virtualHostName) confPath = os.path.join(sslUtilities.Server_root, "conf", "vhosts", virtualHostName)
completePathToConfigFile = os.path.join(confPath, "vhost.conf") completePathToConfigFile = os.path.join(confPath, "vhost.conf")
# Check if file exists # Check if file exists
if not os.path.exists(completePathToConfigFile): if not os.path.exists(completePathToConfigFile):
logging.CyberCPLogFileWriter.writeToFile(f'Configuration file not found: {completePathToConfigFile}') logging.CyberCPLogFileWriter.writeToFile(f'Configuration file not found: {completePathToConfigFile}')
return 0, f'Configuration file not found: {completePathToConfigFile}' return 0, f'Configuration file not found: {completePathToConfigFile}'
# Read current configuration # Read current configuration
try: try:
with open(completePathToConfigFile, 'r') as f: with open(completePathToConfigFile, 'r') as f:
@@ -201,42 +201,41 @@ class sslUtilities:
except IOError as e: except IOError as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error reading configuration file: {str(e)}') logging.CyberCPLogFileWriter.writeToFile(f'Error reading configuration file: {str(e)}')
return 0, f'Error reading configuration file: {str(e)}' return 0, f'Error reading configuration file: {str(e)}'
# Check for potential conflicts # Check for potential conflicts
conflicts = [] conflicts = []
# Check if ACME challenge is already configured # Check if ACME challenge is already configured
if DataVhost.find('/.well-known/acme-challenge') != -1: if DataVhost.find('/.well-known/acme-challenge') != -1:
logging.CyberCPLogFileWriter.writeToFile(f'ACME challenge already configured for {virtualHostName}') logging.CyberCPLogFileWriter.writeToFile(f'ACME challenge already configured for {virtualHostName}')
return 1, 'ACME challenge already configured' return 1, 'ACME challenge already configured'
# Check for conflicting rewrite rules # Check for conflicting rewrite rules
if DataVhost.find('rewrite') != -1 and DataVhost.find('enable 1') != -1: if DataVhost.find('rewrite') != -1 and DataVhost.find('enable 1') != -1:
conflicts.append('Active rewrite rules found that might interfere with ACME challenges') conflicts.append('Active rewrite rules found that might interfere with ACME challenges')
# Check for conflicting location blocks # Check for conflicting location blocks
if DataVhost.find('location /.well-known') != -1: if DataVhost.find('location /.well-known') != -1:
conflicts.append('Existing location block for /.well-known found') conflicts.append('Existing location block for /.well-known found')
# Check for conflicting aliases # Check for conflicting aliases
if DataVhost.find('Alias /.well-known') != -1: if DataVhost.find('Alias /.well-known') != -1:
conflicts.append('Existing alias for /.well-known found') conflicts.append('Existing alias for /.well-known found')
# Check for conflicting context blocks # Check for conflicting context blocks
if DataVhost.find('context /.well-known') != -1: if DataVhost.find('context /.well-known') != -1:
conflicts.append('Existing context block for /.well-known found') conflicts.append('Existing context block for /.well-known found')
# Check for conflicting access controls # Check for conflicting access controls
if DataVhost.find('deny from all') != -1 and DataVhost.find('location') != -1: if DataVhost.find('deny from all') != -1 and DataVhost.find('location') != -1:
conflicts.append('Global deny rules found that might block ACME challenges') conflicts.append('Global deny rules found that might block ACME challenges')
# If conflicts found, log them and return # If conflicts found, log them and return
if conflicts: if conflicts:
conflict_message = 'Configuration conflicts found: ' + '; '.join(conflicts) conflict_message = 'Configuration conflicts found: ' + '; '.join(conflicts)
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(f'Configuration conflicts for {virtualHostName}: {conflict_message}')
f'Configuration conflicts for {virtualHostName}: {conflict_message}')
return 0, conflict_message return 0, conflict_message
# Create challenge directory if it doesn't exist # Create challenge directory if it doesn't exist
challenge_dir = '/usr/local/lsws/Example/html/.well-known/acme-challenge' challenge_dir = '/usr/local/lsws/Example/html/.well-known/acme-challenge'
try: try:
@@ -246,7 +245,7 @@ class sslUtilities:
except OSError as e: except OSError as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error creating challenge directory: {str(e)}') logging.CyberCPLogFileWriter.writeToFile(f'Error creating challenge directory: {str(e)}')
return 0, f'Error creating challenge directory: {str(e)}' return 0, f'Error creating challenge directory: {str(e)}'
# Handle configuration based on server type # Handle configuration based on server type
if ProcessUtilities.decideServer() == ProcessUtilities.OLS: if ProcessUtilities.decideServer() == ProcessUtilities.OLS:
# OpenLiteSpeed configuration # OpenLiteSpeed configuration
@@ -274,31 +273,29 @@ context /.well-known/acme-challenge {
# Read current configuration # Read current configuration
with open(completePathToConfigFile, 'r') as f: with open(completePathToConfigFile, 'r') as f:
lines = f.readlines() lines = f.readlines()
# Write new configuration # Write new configuration
with open(completePathToConfigFile, 'w') as f: with open(completePathToConfigFile, 'w') as f:
check = 0 check = 0
for line in lines: for line in lines:
f.write(line) f.write(line)
if line.find('DocumentRoot /home/') > -1 and check == 0: if line.find('DocumentRoot /home/') > -1 and check == 0:
f.write( f.write(' Alias /.well-known/acme-challenge /usr/local/lsws/Example/html/.well-known/acme-challenge\n')
' Alias /.well-known/acme-challenge /usr/local/lsws/Example/html/.well-known/acme-challenge\n')
check = 1 check = 1
except IOError as e: except IOError as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error writing Apache configuration: {str(e)}') logging.CyberCPLogFileWriter.writeToFile(f'Error writing Apache configuration: {str(e)}')
return 0, f'Error writing Apache configuration: {str(e)}' return 0, f'Error writing Apache configuration: {str(e)}'
# Restart LiteSpeed # Restart LiteSpeed
try: try:
from plogical import installUtilities from plogical import installUtilities
installUtilities.installUtilities.reStartLiteSpeed() installUtilities.installUtilities.reStartLiteSpeed()
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(f'Successfully configured ACME challenge for {virtualHostName}')
f'Successfully configured ACME challenge for {virtualHostName}')
return 1, 'Successfully configured ACME challenge' return 1, 'Successfully configured ACME challenge'
except Exception as e: except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Error restarting LiteSpeed: {str(e)}') logging.CyberCPLogFileWriter.writeToFile(f'Error restarting LiteSpeed: {str(e)}')
return 0, f'Error restarting LiteSpeed: {str(e)}' return 0, f'Error restarting LiteSpeed: {str(e)}'
except Exception as e: except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(f'Unexpected error in PatchVhostConf: {str(e)}') logging.CyberCPLogFileWriter.writeToFile(f'Unexpected error in PatchVhostConf: {str(e)}')
return 0, f'Unexpected error: {str(e)}' return 0, f'Unexpected error: {str(e)}'
@@ -578,12 +575,12 @@ context /.well-known/acme-challenge {
command = f'chmod -R 755 /usr/local/lsws/Example/html' command = f'chmod -R 755 /usr/local/lsws/Example/html'
ProcessUtilities.executioner(command) ProcessUtilities.executioner(command)
# Try custom ACME implementation first # Try Let's Encrypt first
try: try:
domains = [virtualHostName, f'www.{virtualHostName}'] domains = [virtualHostName, f'www.{virtualHostName}']
if aliasDomain: if aliasDomain:
domains.extend([aliasDomain, f'www.{aliasDomain}']) domains.extend([aliasDomain, f'www.{aliasDomain}'])
# Check if Cloudflare is used # Check if Cloudflare is used
use_dns = False use_dns = False
try: try:
@@ -592,17 +589,32 @@ context /.well-known/acme-challenge {
use_dns = True use_dns = True
except: except:
pass pass
acme = CustomACME(virtualHostName, adminEmail, staging=False) # Force production environment acme = CustomACME(virtualHostName, adminEmail, staging=False, provider='letsencrypt')
if acme.issue_certificate(domains, use_dns=use_dns): if acme.issue_certificate(domains, use_dns=use_dns):
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(
f"Successfully obtained SSL using custom ACME implementation for: {virtualHostName}") f"Successfully obtained SSL using Let's Encrypt for: {virtualHostName}")
return 1 return 1
except Exception as e: except Exception as e:
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(
f"Custom ACME implementation failed: {str(e)}. Falling back to acme.sh") f"Let's Encrypt failed: {str(e)}. Trying ZeroSSL...")
# Fallback to acme.sh if custom implementation fails # Try ZeroSSL if Let's Encrypt fails
try:
domains = [virtualHostName, f'www.{virtualHostName}']
if aliasDomain:
domains.extend([aliasDomain, f'www.{aliasDomain}'])
acme = CustomACME(virtualHostName, adminEmail, staging=False, provider='zerossl')
if acme.issue_certificate(domains, use_dns=use_dns):
logging.CyberCPLogFileWriter.writeToFile(
f"Successfully obtained SSL using ZeroSSL for: {virtualHostName}")
return 1
except Exception as e:
logging.CyberCPLogFileWriter.writeToFile(
f"ZeroSSL failed: {str(e)}. Falling back to acme.sh")
# Fallback to acme.sh if both ACME providers fail
try: try:
acmePath = '/root/.acme.sh/acme.sh' acmePath = '/root/.acme.sh/acme.sh'
command = '%s --register-account -m %s' % (acmePath, adminEmail) command = '%s --register-account -m %s' % (acmePath, adminEmail)
@@ -621,20 +633,19 @@ context /.well-known/acme-challenge {
command = acmePath + " --issue -d " + virtualHostName + " -d www." + virtualHostName \ command = acmePath + " --issue -d " + virtualHostName + " -d www." + virtualHostName \
+ ' --cert-file ' + existingCertPath + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \ + ' --cert-file ' + existingCertPath + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \
+ ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + ' -w /usr/local/lsws/Example/html -k ec-256 --force --staging' + ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + ' -w /usr/local/lsws/Example/html -k ec-256 --force --staging'
if ProcessUtilities.decideServer() == ProcessUtilities.OLS: if ProcessUtilities.decideServer() == ProcessUtilities.OLS:
result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True)
else: else:
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True)
universal_newlines=True, shell=True)
if result.returncode == 0: if result.returncode == 0:
command = acmePath + " --issue -d " + virtualHostName + " -d www." + virtualHostName \ command = acmePath + " --issue -d " + virtualHostName + " -d www." + virtualHostName \
+ ' --cert-file ' + existingCertPath + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \ + ' --cert-file ' + existingCertPath + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \
+ ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + ' -w /usr/local/lsws/Example/html -k ec-256 --force --server letsencrypt' + ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + ' -w /usr/local/lsws/Example/html -k ec-256 --force --server letsencrypt'
result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True)
if result.returncode == 0: if result.returncode == 0:
logging.CyberCPLogFileWriter.writeToFile( logging.CyberCPLogFileWriter.writeToFile(
"Successfully obtained SSL for: " + virtualHostName + " and: www." + virtualHostName, 0) "Successfully obtained SSL for: " + virtualHostName + " and: www." + virtualHostName, 0)
@@ -658,7 +669,7 @@ context /.well-known/acme-challenge {
+ ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + ' -w /usr/local/lsws/Example/html -k ec-256 --force --server letsencrypt' + ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + ' -w /usr/local/lsws/Example/html -k ec-256 --force --server letsencrypt'
result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True)
if result.returncode == 0: if result.returncode == 0:
return 1 return 1
return 0 return 0

View File

@@ -2207,6 +2207,10 @@ CREATE TABLE `websiteFunctions_backupsv2` (`id` integer AUTO_INCREMENT NOT NULL
if not Upgrade.executioner(command, command, 1): if not Upgrade.executioner(command, command, 1):
return 0, 'Failed to execute %s' % (command) return 0, 'Failed to execute %s' % (command)
command = 'git clean -f'
if not Upgrade.executioner(command, command, 1):
return 0, 'Failed to execute %s' % (command)
command = 'git pull' command = 'git pull'
if not Upgrade.executioner(command, command, 1): if not Upgrade.executioner(command, command, 1):
return 0, 'Failed to execute %s' % (command) return 0, 'Failed to execute %s' % (command)
@@ -3401,6 +3405,25 @@ pm.max_spare_servers = 3
WriteToFile.write(content) WriteToFile.write(content)
WriteToFile.close() WriteToFile.close()
@staticmethod
def setupPHPSymlink():
try:
# Remove existing PHP symlink if it exists
if os.path.exists('/usr/bin/php'):
os.remove('/usr/bin/php')
# Create symlink to PHP 8.0
command = 'ln -s /usr/local/lsws/lsphp80/bin/php /usr/bin/php'
Upgrade.executioner(command, 'Setup PHP Symlink', 0)
Upgrade.stdOut("PHP symlink created successfully.")
except BaseException as msg:
Upgrade.stdOut('[ERROR] ' + str(msg) + " [setupPHPSymlink]")
return 0
return 1
@staticmethod @staticmethod
def upgrade(branch): def upgrade(branch):
@@ -3464,6 +3487,7 @@ pm.max_spare_servers = 3
Upgrade.executioner(command, 'tmp adjustment', 0) Upgrade.executioner(command, 'tmp adjustment', 0)
Upgrade.dockerUsers() Upgrade.dockerUsers()
Upgrade.setupPHPSymlink()
Upgrade.setupComposer() Upgrade.setupComposer()
## ##

View File

@@ -0,0 +1,143 @@
// Resource Monitoring
let cpuChart, memoryChart, diskChart;
let cpuData = [], memoryData = [], diskData = [];
const maxDataPoints = 30;
function initializeCharts() {
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
},
animation: {
duration: 750
}
};
// CPU Chart
const cpuCtx = document.getElementById('cpuChart').getContext('2d');
cpuChart = new Chart(cpuCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'CPU Usage (%)',
data: [],
borderColor: '#2563eb',
backgroundColor: 'rgba(37, 99, 235, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: chartOptions
});
// Memory Chart
const memoryCtx = document.getElementById('memoryChart').getContext('2d');
memoryChart = new Chart(memoryCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Memory Usage (%)',
data: [],
borderColor: '#00b894',
backgroundColor: 'rgba(0, 184, 148, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: chartOptions
});
// Disk Chart
const diskCtx = document.getElementById('diskChart').getContext('2d');
diskChart = new Chart(diskCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Disk Usage (%)',
data: [],
borderColor: '#ff9800',
backgroundColor: 'rgba(255, 152, 0, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: chartOptions
});
}
function updateCharts(data) {
const now = new Date();
const timeLabel = now.toLocaleTimeString();
// Update CPU Chart
cpuData.push(data.cpu_usage);
if (cpuData.length > maxDataPoints) cpuData.shift();
cpuChart.data.labels.push(timeLabel);
if (cpuChart.data.labels.length > maxDataPoints) cpuChart.data.labels.shift();
cpuChart.data.datasets[0].data = cpuData;
cpuChart.update('none'); // Use 'none' mode for better performance
// Update Memory Chart
memoryData.push(data.memory_usage);
if (memoryData.length > maxDataPoints) memoryData.shift();
memoryChart.data.labels.push(timeLabel);
if (memoryChart.data.labels.length > maxDataPoints) memoryChart.data.labels.shift();
memoryChart.data.datasets[0].data = memoryData;
memoryChart.update('none');
// Update Disk Chart
diskData.push(data.disk_percent);
if (diskData.length > maxDataPoints) diskData.shift();
diskChart.data.labels.push(timeLabel);
if (diskChart.data.labels.length > maxDataPoints) diskChart.data.labels.shift();
diskChart.data.datasets[0].data = diskData;
diskChart.update('none');
}
function fetchResourceUsage() {
$.ajax({
url: '/websites/get_website_resources/',
type: 'POST',
data: JSON.stringify({
'domain': $('#domainNamePage').text().trim()
}),
contentType: 'application/json',
success: function(data) {
if (data.status === 1) {
updateCharts(data);
} else {
console.error('Error fetching resource data:', data.error_message);
}
},
error: function(xhr, status, error) {
console.error('Failed to fetch resource usage:', error);
}
});
}
// Initialize charts when the page loads
$(document).ready(function() {
if (document.getElementById('cpuChart')) {
initializeCharts();
// Fetch resource usage every 5 seconds
setInterval(fetchResourceUsage, 5000);
// Initial fetch
fetchResourceUsage();
}
});

View File

@@ -0,0 +1,52 @@
import psutil
import os
from plogical.processUtilities import ProcessUtilities
from plogical.acl import ACLManager
import plogical.CyberCPLogFileWriter as logging
def get_website_resource_usage(externalApp):
try:
user = externalApp
if not user:
return {'status': 0, 'error_message': 'User not found'}
# Get CPU and Memory usage using ps command
command = f"ps -u {user} -o pcpu,pmem | grep -v CPU | awk '{{cpu += $1; mem += $2}} END {{print cpu, mem}}'"
result = ProcessUtilities.outputExecutioner(command)
try:
cpu_percent, memory_percent = map(float, result.split())
except:
cpu_percent = 0
memory_percent = 0
# Get disk usage using du command
website_path = f"/home/{user}/public_html"
if os.path.exists(website_path):
# Get disk usage in MB
command = f"du -sm {website_path} | cut -f1"
disk_used = float(ProcessUtilities.outputExecutioner(command))
# Get total disk space
command = f"df -m {website_path} | tail -1 | awk '{{print $2}}'"
disk_total = float(ProcessUtilities.outputExecutioner(command))
# Calculate percentage
disk_percent = (disk_used / disk_total) * 100 if disk_total > 0 else 0
else:
disk_used = 0
disk_total = 0
disk_percent = 0
return {
'status': 1,
'cpu_usage': round(cpu_percent, 2),
'memory_usage': round(memory_percent, 2),
'disk_used': round(disk_used, 2),
'disk_total': round(disk_total, 2),
'disk_percent': round(disk_percent, 2)
}
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(f'Error in get_website_resource_usage: {str(msg)}')
return {'status': 0, 'error_message': str(msg)}

View File

@@ -0,0 +1,143 @@
// Resource Monitoring
let cpuChart, memoryChart, diskChart;
let cpuData = [], memoryData = [], diskData = [];
const maxDataPoints = 30;
function initializeCharts() {
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
},
animation: {
duration: 750
}
};
// CPU Chart
const cpuCtx = document.getElementById('cpuChart').getContext('2d');
cpuChart = new Chart(cpuCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'CPU Usage (%)',
data: [],
borderColor: '#2563eb',
backgroundColor: 'rgba(37, 99, 235, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: chartOptions
});
// Memory Chart
const memoryCtx = document.getElementById('memoryChart').getContext('2d');
memoryChart = new Chart(memoryCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Memory Usage (%)',
data: [],
borderColor: '#00b894',
backgroundColor: 'rgba(0, 184, 148, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: chartOptions
});
// Disk Chart
const diskCtx = document.getElementById('diskChart').getContext('2d');
diskChart = new Chart(diskCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Disk Usage (%)',
data: [],
borderColor: '#ff9800',
backgroundColor: 'rgba(255, 152, 0, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: chartOptions
});
}
function updateCharts(data) {
const now = new Date();
const timeLabel = now.toLocaleTimeString();
// Update CPU Chart
cpuData.push(data.cpu_usage);
if (cpuData.length > maxDataPoints) cpuData.shift();
cpuChart.data.labels.push(timeLabel);
if (cpuChart.data.labels.length > maxDataPoints) cpuChart.data.labels.shift();
cpuChart.data.datasets[0].data = cpuData;
cpuChart.update('none'); // Use 'none' mode for better performance
// Update Memory Chart
memoryData.push(data.memory_usage);
if (memoryData.length > maxDataPoints) memoryData.shift();
memoryChart.data.labels.push(timeLabel);
if (memoryChart.data.labels.length > maxDataPoints) memoryChart.data.labels.shift();
memoryChart.data.datasets[0].data = memoryData;
memoryChart.update('none');
// Update Disk Chart
diskData.push(data.disk_percent);
if (diskData.length > maxDataPoints) diskData.shift();
diskChart.data.labels.push(timeLabel);
if (diskChart.data.labels.length > maxDataPoints) diskChart.data.labels.shift();
diskChart.data.datasets[0].data = diskData;
diskChart.update('none');
}
function fetchResourceUsage() {
$.ajax({
url: '/websites/get_website_resources/',
type: 'POST',
data: JSON.stringify({
'domain': $('#domainNamePage').text().trim()
}),
contentType: 'application/json',
success: function(data) {
if (data.status === 1) {
updateCharts(data);
} else {
console.error('Error fetching resource data:', data.error_message);
}
},
error: function(xhr, status, error) {
console.error('Failed to fetch resource usage:', error);
}
});
}
// Initialize charts when the page loads
$(document).ready(function() {
if (document.getElementById('cpuChart')) {
initializeCharts();
// Fetch resource usage every 5 seconds
setInterval(fetchResourceUsage, 5000);
// Initial fetch
fetchResourceUsage();
}
});

View File

@@ -0,0 +1,162 @@
// Resource Monitoring
let cpuChart, memoryChart, diskChart;
let cpuData = [], memoryData = [], diskData = [];
const maxDataPoints = 30;
function initializeCharts() {
// CPU Chart
const cpuCtx = document.getElementById('cpuChart').getContext('2d');
cpuChart = new Chart(cpuCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'CPU Usage (%)',
data: [],
borderColor: '#2563eb',
backgroundColor: 'rgba(37, 99, 235, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
// Memory Chart
const memoryCtx = document.getElementById('memoryChart').getContext('2d');
memoryChart = new Chart(memoryCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Memory Usage (%)',
data: [],
borderColor: '#00b894',
backgroundColor: 'rgba(0, 184, 148, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
// Disk Chart
const diskCtx = document.getElementById('diskChart').getContext('2d');
diskChart = new Chart(diskCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Disk Usage (%)',
data: [],
borderColor: '#ff9800',
backgroundColor: 'rgba(255, 152, 0, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
}
function updateCharts(data) {
const now = new Date();
const timeLabel = now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds();
// Update CPU Chart
cpuData.push(data.cpu_usage);
if (cpuData.length > maxDataPoints) cpuData.shift();
cpuChart.data.labels.push(timeLabel);
if (cpuChart.data.labels.length > maxDataPoints) cpuChart.data.labels.shift();
cpuChart.data.datasets[0].data = cpuData;
cpuChart.update();
// Update Memory Chart
memoryData.push(data.memory_usage);
if (memoryData.length > maxDataPoints) memoryData.shift();
memoryChart.data.labels.push(timeLabel);
if (memoryChart.data.labels.length > maxDataPoints) memoryChart.data.labels.shift();
memoryChart.data.datasets[0].data = memoryData;
memoryChart.update();
// Update Disk Chart
diskData.push(data.disk_percent);
if (diskData.length > maxDataPoints) diskData.shift();
diskChart.data.labels.push(timeLabel);
if (diskChart.data.labels.length > maxDataPoints) diskChart.data.labels.shift();
diskChart.data.datasets[0].data = diskData;
diskChart.update();
}
function fetchResourceUsage() {
$.ajax({
url: '/website/get_website_resources/',
type: 'POST',
data: JSON.stringify({
'domain': $('#domainNamePage').text()
}),
contentType: 'application/json',
success: function(data) {
if (data.status === 1) {
updateCharts(data);
}
},
error: function() {
console.error('Error fetching resource usage data');
}
});
}
// Initialize charts when the page loads
$(document).ready(function() {
initializeCharts();
// Fetch resource usage every 5 seconds
setInterval(fetchResourceUsage, 5000);
// Initial fetch
fetchResourceUsage();
});

View File

@@ -0,0 +1,143 @@
// Resource Monitoring
let cpuChart, memoryChart, diskChart;
let cpuData = [], memoryData = [], diskData = [];
const maxDataPoints = 30;
function initializeCharts() {
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
},
animation: {
duration: 750
}
};
// CPU Chart
const cpuCtx = document.getElementById('cpuChart').getContext('2d');
cpuChart = new Chart(cpuCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'CPU Usage (%)',
data: [],
borderColor: '#2563eb',
backgroundColor: 'rgba(37, 99, 235, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: chartOptions
});
// Memory Chart
const memoryCtx = document.getElementById('memoryChart').getContext('2d');
memoryChart = new Chart(memoryCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Memory Usage (%)',
data: [],
borderColor: '#00b894',
backgroundColor: 'rgba(0, 184, 148, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: chartOptions
});
// Disk Chart
const diskCtx = document.getElementById('diskChart').getContext('2d');
diskChart = new Chart(diskCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Disk Usage (%)',
data: [],
borderColor: '#ff9800',
backgroundColor: 'rgba(255, 152, 0, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: chartOptions
});
}
function updateCharts(data) {
const now = new Date();
const timeLabel = now.toLocaleTimeString();
// Update CPU Chart
cpuData.push(data.cpu_usage);
if (cpuData.length > maxDataPoints) cpuData.shift();
cpuChart.data.labels.push(timeLabel);
if (cpuChart.data.labels.length > maxDataPoints) cpuChart.data.labels.shift();
cpuChart.data.datasets[0].data = cpuData;
cpuChart.update('none'); // Use 'none' mode for better performance
// Update Memory Chart
memoryData.push(data.memory_usage);
if (memoryData.length > maxDataPoints) memoryData.shift();
memoryChart.data.labels.push(timeLabel);
if (memoryChart.data.labels.length > maxDataPoints) memoryChart.data.labels.shift();
memoryChart.data.datasets[0].data = memoryData;
memoryChart.update('none');
// Update Disk Chart
diskData.push(data.disk_percent);
if (diskData.length > maxDataPoints) diskData.shift();
diskChart.data.labels.push(timeLabel);
if (diskChart.data.labels.length > maxDataPoints) diskChart.data.labels.shift();
diskChart.data.datasets[0].data = diskData;
diskChart.update('none');
}
function fetchResourceUsage() {
$.ajax({
url: '/websites/get_website_resources/',
type: 'POST',
data: JSON.stringify({
'domain': $('#domainNamePage').text().trim()
}),
contentType: 'application/json',
success: function(data) {
if (data.status === 1) {
updateCharts(data);
} else {
console.error('Error fetching resource data:', data.error_message);
}
},
error: function(xhr, status, error) {
console.error('Failed to fetch resource usage:', error);
}
});
}
// Initialize charts when the page loads
$(document).ready(function() {
if (document.getElementById('cpuChart')) {
initializeCharts();
// Fetch resource usage every 5 seconds
setInterval(fetchResourceUsage, 5000);
// Initial fetch
fetchResourceUsage();
}
});

View File

@@ -0,0 +1,162 @@
// Resource Monitoring
let cpuChart, memoryChart, diskChart;
let cpuData = [], memoryData = [], diskData = [];
const maxDataPoints = 30;
function initializeCharts() {
// CPU Chart
const cpuCtx = document.getElementById('cpuChart').getContext('2d');
cpuChart = new Chart(cpuCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'CPU Usage (%)',
data: [],
borderColor: '#2563eb',
backgroundColor: 'rgba(37, 99, 235, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
// Memory Chart
const memoryCtx = document.getElementById('memoryChart').getContext('2d');
memoryChart = new Chart(memoryCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Memory Usage (%)',
data: [],
borderColor: '#00b894',
backgroundColor: 'rgba(0, 184, 148, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
// Disk Chart
const diskCtx = document.getElementById('diskChart').getContext('2d');
diskChart = new Chart(diskCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Disk Usage (%)',
data: [],
borderColor: '#ff9800',
backgroundColor: 'rgba(255, 152, 0, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
}
function updateCharts(data) {
const now = new Date();
const timeLabel = now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds();
// Update CPU Chart
cpuData.push(data.cpu_usage);
if (cpuData.length > maxDataPoints) cpuData.shift();
cpuChart.data.labels.push(timeLabel);
if (cpuChart.data.labels.length > maxDataPoints) cpuChart.data.labels.shift();
cpuChart.data.datasets[0].data = cpuData;
cpuChart.update();
// Update Memory Chart
memoryData.push(data.memory_usage);
if (memoryData.length > maxDataPoints) memoryData.shift();
memoryChart.data.labels.push(timeLabel);
if (memoryChart.data.labels.length > maxDataPoints) memoryChart.data.labels.shift();
memoryChart.data.datasets[0].data = memoryData;
memoryChart.update();
// Update Disk Chart
diskData.push(data.disk_percent);
if (diskData.length > maxDataPoints) diskData.shift();
diskChart.data.labels.push(timeLabel);
if (diskChart.data.labels.length > maxDataPoints) diskChart.data.labels.shift();
diskChart.data.datasets[0].data = diskData;
diskChart.update();
}
function fetchResourceUsage() {
$.ajax({
url: '/website/get_website_resources/',
type: 'POST',
data: JSON.stringify({
'domain': $('#domainNamePage').text()
}),
contentType: 'application/json',
success: function(data) {
if (data.status === 1) {
updateCharts(data);
}
},
error: function() {
console.error('Error fetching resource usage data');
}
});
}
// Initialize charts when the page loads
$(document).ready(function() {
initializeCharts();
// Fetch resource usage every 5 seconds
setInterval(fetchResourceUsage, 5000);
// Initial fetch
fetchResourceUsage();
});

View File

@@ -16904,4 +16904,937 @@ app.controller('BuyAddons', function ($scope, $http) {
} }
}) })
app.controller('launchChild', function ($scope, $http) {
$scope.logFileLoading = true;
$scope.logsFeteched = true;
$scope.couldNotFetchLogs = true;
$scope.couldNotConnect = true;
$scope.fetchedData = true;
$scope.hideLogs = true;
$scope.hideErrorLogs = true;
$scope.hidelogsbtn = function () {
$scope.hideLogs = true;
};
$scope.hideErrorLogsbtn = function () {
$scope.hideLogs = true;
};
$scope.fileManagerURL = "/filemanager/" + $("#domainNamePage").text();
$scope.previewUrl = "/preview/" + $("#childDomain").text() + "/";
$scope.wordPressInstallURL = "/websites/" + $("#childDomain").text() + "/wordpressInstall";
$scope.joomlaInstallURL = "/websites/" + $("#childDomain").text() + "/joomlaInstall";
$scope.setupGit = "/websites/" + $("#childDomain").text() + "/setupGit";
$scope.installPrestaURL = "/websites/" + $("#childDomain").text() + "/installPrestaShop";
$scope.installMagentoURL = "/websites/" + $("#childDomain").text() + "/installMagento";
var logType = 0;
$scope.pageNumber = 1;
$scope.fetchLogs = function (type) {
var pageNumber = $scope.pageNumber;
if (type == 3) {
pageNumber = $scope.pageNumber + 1;
$scope.pageNumber = pageNumber;
} else if (type == 4) {
pageNumber = $scope.pageNumber - 1;
$scope.pageNumber = pageNumber;
} else {
logType = type;
}
$scope.logFileLoading = false;
$scope.logsFeteched = true;
$scope.couldNotFetchLogs = true;
$scope.couldNotConnect = true;
$scope.fetchedData = false;
$scope.hideErrorLogs = true;
url = "/websites/getDataFromLogFile";
var domainNamePage = $("#domainNamePage").text();
var data = {
logType: logType,
virtualHost: domainNamePage,
page: pageNumber,
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
if (response.data.logstatus === 1) {
$scope.logFileLoading = true;
$scope.logsFeteched = false;
$scope.couldNotFetchLogs = true;
$scope.couldNotConnect = true;
$scope.fetchedData = false;
$scope.hideLogs = false;
$scope.records = JSON.parse(response.data.data);
} else {
$scope.logFileLoading = true;
$scope.logsFeteched = true;
$scope.couldNotFetchLogs = false;
$scope.couldNotConnect = true;
$scope.fetchedData = true;
$scope.hideLogs = false;
$scope.errorMessage = response.data.error_message;
console.log(domainNamePage)
}
}
function cantLoadInitialDatas(response) {
$scope.logFileLoading = true;
$scope.logsFeteched = true;
$scope.couldNotFetchLogs = true;
$scope.couldNotConnect = false;
$scope.fetchedData = true;
$scope.hideLogs = false;
}
};
$scope.errorPageNumber = 1;
$scope.fetchErrorLogs = function (type) {
var errorPageNumber = $scope.errorPageNumber;
if (type === 3) {
errorPageNumber = $scope.errorPageNumber + 1;
$scope.errorPageNumber = errorPageNumber;
} else if (type === 4) {
errorPageNumber = $scope.errorPageNumber - 1;
$scope.errorPageNumber = errorPageNumber;
} else {
logType = type;
}
// notifications
$scope.logFileLoading = false;
$scope.logsFeteched = true;
$scope.couldNotFetchLogs = true;
$scope.couldNotConnect = true;
$scope.fetchedData = true;
$scope.hideErrorLogs = true;
$scope.hideLogs = false;
url = "/websites/fetchErrorLogs";
var domainNamePage = $("#domainNamePage").text();
var data = {
virtualHost: domainNamePage,
page: errorPageNumber,
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
if (response.data.logstatus === 1) {
// notifications
$scope.logFileLoading = true;
$scope.logsFeteched = false;
$scope.couldNotFetchLogs = true;
$scope.couldNotConnect = true;
$scope.fetchedData = true;
$scope.hideLogs = false;
$scope.hideErrorLogs = false;
$scope.errorLogsData = response.data.data;
} else {
// notifications
$scope.logFileLoading = true;
$scope.logsFeteched = true;
$scope.couldNotFetchLogs = false;
$scope.couldNotConnect = true;
$scope.fetchedData = true;
$scope.hideLogs = true;
$scope.hideErrorLogs = true;
$scope.errorMessage = response.data.error_message;
}
}
function cantLoadInitialDatas(response) {
// notifications
$scope.logFileLoading = true;
$scope.logsFeteched = true;
$scope.couldNotFetchLogs = true;
$scope.couldNotConnect = false;
$scope.fetchedData = true;
$scope.hideLogs = true;
$scope.hideErrorLogs = true;
}
};
///////// Configurations Part
$scope.configurationsBox = true;
$scope.configsFetched = true;
$scope.couldNotFetchConfigs = true;
$scope.couldNotConnect = true;
$scope.fetchedConfigsData = true;
$scope.configFileLoading = true;
$scope.configSaved = true;
$scope.couldNotSaveConfigurations = true;
$scope.hideconfigbtn = function () {
$scope.configurationsBox = true;
};
$scope.fetchConfigurations = function () {
$scope.hidsslconfigs = true;
$scope.configurationsBoxRewrite = true;
$scope.changePHPView = true;
//Rewrite rules
$scope.configurationsBoxRewrite = true;
$scope.rewriteRulesFetched = true;
$scope.couldNotFetchRewriteRules = true;
$scope.rewriteRulesSaved = true;
$scope.couldNotSaveRewriteRules = true;
$scope.fetchedRewriteRules = true;
$scope.saveRewriteRulesBTN = true;
///
$scope.configFileLoading = false;
url = "/websites/getDataFromConfigFile";
var virtualHost = $("#childDomain").text();
var data = {
virtualHost: virtualHost,
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
if (response.data.configstatus === 1) {
//Rewrite rules
$scope.configurationsBoxRewrite = true;
$scope.rewriteRulesFetched = true;
$scope.couldNotFetchRewriteRules = true;
$scope.rewriteRulesSaved = true;
$scope.couldNotSaveRewriteRules = true;
$scope.fetchedRewriteRules = true;
$scope.saveRewriteRulesBTN = true;
///
$scope.configurationsBox = false;
$scope.configsFetched = false;
$scope.couldNotFetchConfigs = true;
$scope.couldNotConnect = true;
$scope.fetchedConfigsData = false;
$scope.configFileLoading = true;
$scope.configSaved = true;
$scope.couldNotSaveConfigurations = true;
$scope.saveConfigBtn = false;
$scope.configData = response.data.configData;
} else {
//Rewrite rules
$scope.configurationsBoxRewrite = true;
$scope.rewriteRulesFetched = true;
$scope.couldNotFetchRewriteRules = true;
$scope.rewriteRulesSaved = true;
$scope.couldNotSaveRewriteRules = true;
$scope.fetchedRewriteRules = true;
$scope.saveRewriteRulesBTN = true;
///
$scope.configurationsBox = false;
$scope.configsFetched = true;
$scope.couldNotFetchConfigs = false;
$scope.couldNotConnect = true;
$scope.fetchedConfigsData = true;
$scope.configFileLoading = true;
$scope.configSaved = true;
$scope.couldNotSaveConfigurations = true;
$scope.errorMessage = response.data.error_message;
}
}
function cantLoadInitialDatas(response) {
//Rewrite rules
$scope.configurationsBoxRewrite = true;
$scope.rewriteRulesFetched = true;
$scope.couldNotFetchRewriteRules = true;
$scope.rewriteRulesSaved = true;
$scope.couldNotSaveRewriteRules = true;
$scope.fetchedRewriteRules = true;
$scope.saveRewriteRulesBTN = true;
///
$scope.configurationsBox = false;
$scope.configsFetched = true;
$scope.couldNotFetchConfigs = true;
$scope.couldNotConnect = false;
$scope.fetchedConfigsData = true;
$scope.configFileLoading = true;
$scope.configSaved = true;
$scope.couldNotSaveConfigurations = true;
}
};
$scope.saveCongiruations = function () {
$scope.configFileLoading = false;
url = "/websites/saveConfigsToFile";
var virtualHost = $("#childDomain").text();
var configData = $scope.configData;
var data = {
virtualHost: virtualHost,
configData: configData,
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
if (response.data.configstatus == 1) {
$scope.configurationsBox = false;
$scope.configsFetched = true;
$scope.couldNotFetchConfigs = true;
$scope.couldNotConnect = true;
$scope.fetchedConfigsData = true;
$scope.configFileLoading = true;
$scope.configSaved = false;
$scope.couldNotSaveConfigurations = true;
$scope.saveConfigBtn = true;
} else {
$scope.configurationsBox = false;
$scope.configsFetched = true;
$scope.couldNotFetchConfigs = true;
$scope.couldNotConnect = true;
$scope.fetchedConfigsData = false;
$scope.configFileLoading = true;
$scope.configSaved = true;
$scope.couldNotSaveConfigurations = false;
$scope.errorMessage = response.data.error_message;
}
}
function cantLoadInitialDatas(response) {
$scope.configurationsBox = false;
$scope.configsFetched = true;
$scope.couldNotFetchConfigs = true;
$scope.couldNotConnect = false;
$scope.fetchedConfigsData = true;
$scope.configFileLoading = true;
$scope.configSaved = true;
$scope.couldNotSaveConfigurations = true;
}
};
///////// Rewrite Rules
$scope.configurationsBoxRewrite = true;
$scope.rewriteRulesFetched = true;
$scope.couldNotFetchRewriteRules = true;
$scope.rewriteRulesSaved = true;
$scope.couldNotSaveRewriteRules = true;
$scope.fetchedRewriteRules = true;
$scope.saveRewriteRulesBTN = true;
$scope.hideRewriteRulesbtn = function () {
$scope.configurationsBoxRewrite = true;
};
$scope.fetchRewriteFules = function () {
$scope.hidsslconfigs = true;
$scope.configurationsBox = true;
$scope.changePHPView = true;
$scope.configurationsBox = true;
$scope.configsFetched = true;
$scope.couldNotFetchConfigs = true;
$scope.couldNotConnect = true;
$scope.fetchedConfigsData = true;
$scope.configFileLoading = true;
$scope.configSaved = true;
$scope.couldNotSaveConfigurations = true;
$scope.saveConfigBtn = true;
$scope.configFileLoading = false;
url = "/websites/getRewriteRules";
var virtualHost = $("#childDomain").text();
var data = {
virtualHost: virtualHost,
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
if (response.data.rewriteStatus == 1) {
// from main
$scope.configurationsBox = true;
$scope.configsFetched = true;
$scope.couldNotFetchConfigs = true;
$scope.fetchedConfigsData = true;
$scope.configSaved = true;
$scope.couldNotSaveConfigurations = true;
$scope.saveConfigBtn = true;
// main ends
$scope.configFileLoading = true;
//
$scope.configurationsBoxRewrite = false;
$scope.rewriteRulesFetched = false;
$scope.couldNotFetchRewriteRules = true;
$scope.rewriteRulesSaved = true;
$scope.couldNotSaveRewriteRules = true;
$scope.fetchedRewriteRules = false;
$scope.saveRewriteRulesBTN = false;
$scope.couldNotConnect = true;
$scope.rewriteRules = response.data.rewriteRules;
} else {
// from main
$scope.configurationsBox = true;
$scope.configsFetched = true;
$scope.couldNotFetchConfigs = true;
$scope.fetchedConfigsData = true;
$scope.configFileLoading = true;
$scope.configSaved = true;
$scope.couldNotSaveConfigurations = true;
$scope.saveConfigBtn = true;
// from main
$scope.configFileLoading = true;
///
$scope.configurationsBoxRewrite = true;
$scope.rewriteRulesFetched = true;
$scope.couldNotFetchRewriteRules = false;
$scope.rewriteRulesSaved = true;
$scope.couldNotSaveRewriteRules = true;
$scope.fetchedRewriteRules = true;
$scope.saveRewriteRulesBTN = true;
$scope.couldNotConnect = true;
$scope.errorMessage = response.data.error_message;
}
}
function cantLoadInitialDatas(response) {
// from main
$scope.configurationsBox = true;
$scope.configsFetched = true;
$scope.couldNotFetchConfigs = true;
$scope.fetchedConfigsData = true;
$scope.configFileLoading = true;
$scope.configSaved = true;
$scope.couldNotSaveConfigurations = true;
$scope.saveConfigBtn = true;
// from main
$scope.configFileLoading = true;
///
$scope.configurationsBoxRewrite = true;
$scope.rewriteRulesFetched = true;
$scope.couldNotFetchRewriteRules = true;
$scope.rewriteRulesSaved = true;
$scope.couldNotSaveRewriteRules = true;
$scope.fetchedRewriteRules = true;
$scope.saveRewriteRulesBTN = true;
$scope.couldNotConnect = false;
}
};
$scope.saveRewriteRules = function () {
$scope.configFileLoading = false;
url = "/websites/saveRewriteRules";
var virtualHost = $("#childDomain").text();
var rewriteRules = $scope.rewriteRules;
var data = {
virtualHost: virtualHost,
rewriteRules: rewriteRules,
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
if (response.data.rewriteStatus == 1) {
$scope.configurationsBoxRewrite = false;
$scope.rewriteRulesFetched = true;
$scope.couldNotFetchRewriteRules = true;
$scope.rewriteRulesSaved = false;
$scope.couldNotSaveRewriteRules = true;
$scope.fetchedRewriteRules = true;
$scope.saveRewriteRulesBTN = true;
$scope.configFileLoading = true;
} else {
$scope.configurationsBoxRewrite = false;
$scope.rewriteRulesFetched = false;
$scope.couldNotFetchRewriteRules = true;
$scope.rewriteRulesSaved = true;
$scope.couldNotSaveRewriteRules = false;
$scope.fetchedRewriteRules = true;
$scope.saveRewriteRulesBTN = false;
$scope.configFileLoading = true;
$scope.errorMessage = response.data.error_message;
}
}
function cantLoadInitialDatas(response) {
$scope.configurationsBoxRewrite = false;
$scope.rewriteRulesFetched = false;
$scope.couldNotFetchRewriteRules = true;
$scope.rewriteRulesSaved = true;
$scope.couldNotSaveRewriteRules = true;
$scope.fetchedRewriteRules = true;
$scope.saveRewriteRulesBTN = false;
$scope.configFileLoading = true;
$scope.couldNotConnect = false;
}
};
//////// SSL Part
$scope.sslSaved = true;
$scope.couldNotSaveSSL = true;
$scope.hidsslconfigs = true;
$scope.couldNotConnect = true;
$scope.hidesslbtn = function () {
$scope.hidsslconfigs = true;
};
$scope.addSSL = function () {
$scope.hidsslconfigs = false;
$scope.configurationsBox = true;
$scope.configurationsBoxRewrite = true;
$scope.changePHPView = true;
};
$scope.saveSSL = function () {
$scope.configFileLoading = false;
url = "/websites/saveSSL";
var virtualHost = $("#childDomain").text();
var cert = $scope.cert;
var key = $scope.key;
var data = {
virtualHost: virtualHost,
cert: cert,
key: key,
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
if (response.data.sslStatus === 1) {
$scope.sslSaved = false;
$scope.couldNotSaveSSL = true;
$scope.couldNotConnect = true;
$scope.configFileLoading = true;
} else {
$scope.sslSaved = true;
$scope.couldNotSaveSSL = false;
$scope.couldNotConnect = true;
$scope.configFileLoading = true;
$scope.errorMessage = response.data.error_message;
}
}
function cantLoadInitialDatas(response) {
$scope.sslSaved = true;
$scope.couldNotSaveSSL = true;
$scope.couldNotConnect = false;
$scope.configFileLoading = true;
}
};
//// Change PHP Master
$scope.failedToChangePHPMaster = true;
$scope.phpChangedMaster = true;
$scope.couldNotConnect = true;
$scope.changePHPView = true;
$scope.hideChangePHPMaster = function () {
$scope.changePHPView = true;
};
$scope.changePHPMaster = function () {
$scope.hidsslconfigs = true;
$scope.configurationsBox = true;
$scope.configurationsBoxRewrite = true;
$scope.changePHPView = false;
};
$scope.changePHPVersionMaster = function (childDomain, phpSelection) {
// notifcations
$scope.configFileLoading = false;
var url = "/websites/changePHP";
var data = {
childDomain: $("#childDomain").text(),
phpSelection: $scope.phpSelectionMaster,
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
if (response.data.changePHP === 1) {
$scope.configFileLoading = true;
$scope.websiteDomain = $("#childDomain").text();
// notifcations
$scope.failedToChangePHPMaster = true;
$scope.phpChangedMaster = false;
$scope.couldNotConnect = true;
} else {
$scope.configFileLoading = true;
$scope.errorMessage = response.data.error_message;
// notifcations
$scope.failedToChangePHPMaster = false;
$scope.phpChangedMaster = true;
$scope.couldNotConnect = true;
}
}
function cantLoadInitialDatas(response) {
$scope.configFileLoading = true;
// notifcations
$scope.failedToChangePHPMaster = true;
$scope.phpChangedMaster = true;
$scope.couldNotConnect = false;
}
};
/// Open_basedir protection
$scope.baseDirLoading = true;
$scope.operationFailed = true;
$scope.operationSuccessfull = true;
$scope.couldNotConnect = true;
$scope.openBaseDirBox = true;
$scope.openBaseDirView = function () {
$scope.openBaseDirBox = false;
};
$scope.hideOpenBasedir = function () {
$scope.openBaseDirBox = true;
};
$scope.applyOpenBasedirChanges = function (childDomain, phpSelection) {
// notifcations
$scope.baseDirLoading = false;
$scope.operationFailed = true;
$scope.operationSuccessfull = true;
$scope.couldNotConnect = true;
$scope.openBaseDirBox = false;
var url = "/websites/changeOpenBasedir";
var data = {
domainName: $("#childDomain").text(),
openBasedirValue: $scope.openBasedirValue
};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas);
function ListInitialDatas(response) {
if (response.data.changeOpenBasedir === 1) {
$scope.baseDirLoading = true;
$scope.operationFailed = true;
$scope.operationSuccessfull = false;
$scope.couldNotConnect = true;
$scope.openBaseDirBox = false;
} else {
$scope.baseDirLoading = true;
$scope.operationFailed = false;
$scope.operationSuccessfull = true;
$scope.couldNotConnect = true;
$scope.openBaseDirBox = false;
$scope.errorMessage = response.data.error_message;
}
}
function cantLoadInitialDatas(response) {
$scope.baseDirLoading = true;
$scope.operationFailed = true;
$scope.operationSuccessfull = true;
$scope.couldNotConnect = false;
$scope.openBaseDirBox = false;
}
}
});

View File

@@ -87,6 +87,42 @@
} }
}; };
$scope.ScanWordpressSite = function () {
$('#cyberPanelLoading').show();
var url = "{% url 'ScanWordpressSite' %}";
var data = {};
var config = {
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
};
$http.post(url, data, config).then(function(response) {
$('#cyberPanelLoading').hide();
if (response.data.status === 1) {
new PNotify({
title: 'Success!',
text: 'WordPress sites scanned successfully!',
type: 'success'
});
location.reload();
} else {
new PNotify({
title: 'Operation Failed!',
text: response.data.error_message,
type: 'error'
});
}
}, function(response) {
$('#cyberPanelLoading').hide();
new PNotify({
title: 'Operation Failed!',
text: response.data.error_message,
type: 'error'
});
});
};
$scope.updateSetting = function(site, setting) { $scope.updateSetting = function(site, setting) {
var settingMap = { var settingMap = {
'search-indexing': 'searchIndex', 'search-indexing': 'searchIndex',
@@ -355,6 +391,7 @@
<h3 class="panel-title">{% trans "WordPress Sites" %}</h3> <h3 class="panel-title">{% trans "WordPress Sites" %}</h3>
</div> </div>
<div class="col-sm-6 text-right"> <div class="col-sm-6 text-right">
<button ng-click="ScanWordpressSite()" class="btn btn-info btn-sm" style="margin-right: 10px;">Scan WordPress Sites</button>
<a href="{% url 'createWordpress' %}" class="btn btn-success btn-sm">Install WordPress</a> <a href="{% url 'createWordpress' %}" class="btn btn-success btn-sm">Install WordPress</a>
</div> </div>
</div> </div>
@@ -602,6 +639,4 @@
margin-left: 4px; margin-left: 4px;
} }
</style> </style>
{% endblock content %} {% endblock content %}

View File

@@ -66,7 +66,7 @@
</div> </div>
<div class="col-sm-10" style="padding: 0px; box-shadow: 0px 0px 1px 0px #888888; margin-bottom: 2%"> <div class="col-sm-10" style="padding: 0px; box-shadow: 0px 0px 1px 0px #888888; margin-bottom: 2%">
<input ng-change="searchWebsites()" placeholder="Search..." ng-model="patternAdded" name="dom" type="text" <input ng-keypress="$event.keyCode === 13 && searchWebsites()" placeholder="Search... (Press Enter to search)" ng-model="patternAdded" name="dom" type="text"
class="form-control" required> class="form-control" required>
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@@ -200,4 +200,6 @@ urlpatterns = [
# Catch all for domains # Catch all for domains
path('<domain>/<childDomain>', views.launchChild, name='launchChild'), path('<domain>/<childDomain>', views.launchChild, name='launchChild'),
path('<domain>', views.domain, name='domain'), path('<domain>', views.domain, name='domain'),
path('get_website_resources/', views.get_website_resources, name='get_website_resources'),
] ]

View File

@@ -2,11 +2,12 @@
from django.shortcuts import redirect from django.shortcuts import redirect
from django.http import HttpResponse from django.http import HttpResponse, JsonResponse
from loginSystem.models import Administrator from loginSystem.models import Administrator
from loginSystem.views import loadLoginPage from loginSystem.views import loadLoginPage
import json import json
import plogical.CyberCPLogFileWriter as logging import plogical.CyberCPLogFileWriter as logging
from plogical.acl import ACLManager
from plogical.httpProc import httpProc from plogical.httpProc import httpProc
@@ -17,6 +18,7 @@ from django.views.decorators.csrf import csrf_exempt
from .dockerviews import startContainer as docker_startContainer from .dockerviews import startContainer as docker_startContainer
from .dockerviews import stopContainer as docker_stopContainer from .dockerviews import stopContainer as docker_stopContainer
from .dockerviews import restartContainer as docker_restartContainer from .dockerviews import restartContainer as docker_restartContainer
from .resource_monitoring import get_website_resource_usage
def loadWebsitesHome(request): def loadWebsitesHome(request):
val = request.session['userID'] val = request.session['userID']
@@ -1882,4 +1884,42 @@ def restartContainer(request):
return docker_restartContainer(request) return docker_restartContainer(request)
return HttpResponse('Not allowed') return HttpResponse('Not allowed')
except KeyError: except KeyError:
return redirect(loadLoginPage) return redirect(loadLoginPage)
@csrf_exempt
def get_website_resources(request):
try:
data = json.loads(request.body)
domain = data['domain']
# Get userID from session
try:
userID = request.session['userID']
admin = Administrator.objects.get(pk=userID)
except:
return JsonResponse({'status': 0, 'error_message': 'Unauthorized access'})
# Verify domain ownership
currentACL = ACLManager.loadedACL(userID)
from websiteFunctions.models import Websites
try:
website = Websites.objects.get(domain=domain)
except Websites.DoesNotExist:
return JsonResponse({'status': 0, 'error_message': 'Website not found'})
if ACLManager.checkOwnership(domain, admin, currentACL) == 1:
pass
else:
return ACLManager.loadError()
# Get resource usage data using externalApp
resource_data = get_website_resource_usage(website.externalApp)
if resource_data['status'] == 0:
return JsonResponse(resource_data)
return JsonResponse(resource_data)
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile(f'Error in get_website_resources: {str(msg)}')
return JsonResponse({'status': 0, 'error_message': str(msg)})

View File

@@ -1955,19 +1955,43 @@ class WebsiteManager:
return HttpResponse(json_data) return HttpResponse(json_data)
def UpdateWPSettings(self, userID=None, data=None): def UpdateWPSettings(self, userID=None, data=None):
# Map old setting names to new ones
setting_map = {
'PasswordProtection': 'password-protection',
'searchIndex': 'search-indexing',
'debugging': 'debugging',
'maintenanceMode': 'maintenance-mode',
'lscache': 'lscache',
'Wpcron': 'wpcron',
# Add more mappings as needed
}
siteId = data.get('siteId') or data.get('WPid')
if not siteId:
resp = {'status': 0, 'error_message': 'Missing siteId or WPid'}
return JsonResponse(resp)
# Accept both new and old setting names
setting = data.get('setting')
if not setting:
for old_key in setting_map:
if old_key in data:
setting = old_key
data['settingValue'] = data[old_key]
break
# Map to new setting name if needed
setting = setting_map.get(setting, setting)
value = data.get('value') or data.get('settingValue')
try: try:
currentACL = ACLManager.loadedACL(userID) currentACL = ACLManager.loadedACL(userID)
admin = Administrator.objects.get(pk=userID) admin = Administrator.objects.get(pk=userID)
siteId = data['siteId']
setting = data['setting']
value = data['value']
wpsite = WPSites.objects.get(pk=siteId) wpsite = WPSites.objects.get(pk=siteId)
if ACLManager.checkOwnership(wpsite.owner.domain, admin, currentACL) != 1: if ACLManager.checkOwnership(wpsite.owner.domain, admin, currentACL) != 1:
return ACLManager.loadError() return ACLManager.loadError()
# Get PHP version and path # Get PHP version and path
Webobj = Websites.objects.get(pk=wpsite.owner_id) Webobj = Websites.objects.get(pk=wpsite.owner_id)
Vhuser = Webobj.externalApp Vhuser = Webobj.externalApp
@@ -1977,10 +2001,8 @@ class WebsiteManager:
# Update the appropriate setting based on the setting type # Update the appropriate setting based on the setting type
if setting == 'search-indexing': if setting == 'search-indexing':
# Update search engine indexing
command = f'sudo -u {Vhuser} {FinalPHPPath} -d error_reporting=0 /usr/bin/wp option update blog_public {value} --skip-plugins --skip-themes --path={wpsite.path}' command = f'sudo -u {Vhuser} {FinalPHPPath} -d error_reporting=0 /usr/bin/wp option update blog_public {value} --skip-plugins --skip-themes --path={wpsite.path}'
elif setting == 'debugging': elif setting == 'debugging':
# Update debugging in wp-config.php
if value: if value:
command = f'sudo -u {Vhuser} {FinalPHPPath} -d error_reporting=0 /usr/bin/wp config set WP_DEBUG true --raw --skip-plugins --skip-themes --path={wpsite.path}' command = f'sudo -u {Vhuser} {FinalPHPPath} -d error_reporting=0 /usr/bin/wp config set WP_DEBUG true --raw --skip-plugins --skip-themes --path={wpsite.path}'
else: else:
@@ -1990,76 +2012,84 @@ class WebsiteManager:
vhostPassDir = f'/home/{vhostName}' vhostPassDir = f'/home/{vhostName}'
path = f'{vhostPassDir}/{siteId}' path = f'{vhostPassDir}/{siteId}'
if value: if value:
# Enable password protection
tempPath = f'/home/cyberpanel/{str(randint(1000, 9999))}' tempPath = f'/home/cyberpanel/{str(randint(1000, 9999))}'
os.makedirs(tempPath) os.makedirs(tempPath)
# Create temporary .htpasswd file
htpasswd = f'{tempPath}/.htpasswd' htpasswd = f'{tempPath}/.htpasswd'
htaccess = f'{tempPath}/.htaccess' htaccess = f'{tempPath}/.htaccess'
password = randomPassword.generate_pass(12) password = randomPassword.generate_pass(12)
# Create .htpasswd file
command = f"htpasswd -cb {htpasswd} admin {password}" command = f"htpasswd -cb {htpasswd} admin {password}"
ProcessUtilities.executioner(command) ProcessUtilities.executioner(command)
# Create .htaccess file content
htaccess_content = f""" htaccess_content = f"""
AuthType Basic AuthType Basic
AuthName "Restricted Access" AuthName "Restricted Access"
AuthUserFile {path}/.htpasswd AuthUserFile {path}/.htpasswd
Require valid-user Require valid-user
""" """
with open(htaccess, 'w') as f: with open(htaccess, 'w') as f:
f.write(htaccess_content) f.write(htaccess_content)
# Create final directory and move files
command = f"mkdir -p {path}" command = f"mkdir -p {path}"
ProcessUtilities.executioner(command, wpsite.owner.externalApp) ProcessUtilities.executioner(command, wpsite.owner.externalApp)
# Move files to final location
command = f"mv {htpasswd} {path}/.htpasswd" command = f"mv {htpasswd} {path}/.htpasswd"
ProcessUtilities.executioner(command, wpsite.owner.externalApp) ProcessUtilities.executioner(command, wpsite.owner.externalApp)
command = f"mv {htaccess} {wpsite.path}/.htaccess"
command = f"mv {htaccess} {wpsite.path}/.htaccess"
ProcessUtilities.executioner(command, wpsite.owner.externalApp) ProcessUtilities.executioner(command, wpsite.owner.externalApp)
# Cleanup temp directory
command = f"rm -rf {tempPath}" command = f"rm -rf {tempPath}"
ProcessUtilities.executioner(command) ProcessUtilities.executioner(command)
else: else:
# Disable password protection
if os.path.exists(path): if os.path.exists(path):
command = f"rm -rf {path}" command = f"rm -rf {path}"
ProcessUtilities.executioner(command, wpsite.owner.externalApp) ProcessUtilities.executioner(command, wpsite.owner.externalApp)
htaccess = f'{wpsite.path}/.htaccess' htaccess = f'{wpsite.path}/.htaccess'
if os.path.exists(htaccess): if os.path.exists(htaccess):
command = f"rm -f {htaccess}" command = f"rm -f {htaccess}"
ProcessUtilities.executioner(command, wpsite.owner.externalApp) ProcessUtilities.executioner(command, wpsite.owner.externalApp)
resp = {'status': 1, 'error_message': 'None'}
return JsonResponse({'status': 1, 'error_message': 'None'}) if data.get('legacy_response'):
import json
return HttpResponse(json.dumps(resp))
else:
return JsonResponse(resp)
elif setting == 'maintenance-mode': elif setting == 'maintenance-mode':
if value: if value:
command = f'sudo -u {Vhuser} {FinalPHPPath} -d error_reporting=0 /usr/bin/wp maintenance-mode activate --skip-plugins --skip-themes --path={wpsite.path}' command = f'sudo -u {Vhuser} {FinalPHPPath} -d error_reporting=0 /usr/bin/wp maintenance-mode activate --skip-plugins --skip-themes --path={wpsite.path}'
else: else:
command = f'sudo -u {Vhuser} {FinalPHPPath} -d error_reporting=0 /usr/bin/wp maintenance-mode deactivate --skip-plugins --skip-themes --path={wpsite.path}' command = f'sudo -u {Vhuser} {FinalPHPPath} -d error_reporting=0 /usr/bin/wp maintenance-mode deactivate --skip-plugins --skip-themes --path={wpsite.path}'
elif setting == 'lscache':
if value:
command = f'sudo -u {Vhuser} {FinalPHPPath} -d error_reporting=0 /usr/bin/wp plugin activate litespeed-cache --skip-plugins --skip-themes --path={wpsite.path}'
else:
command = f'sudo -u {Vhuser} {FinalPHPPath} -d error_reporting=0 /usr/bin/wp plugin deactivate litespeed-cache --skip-plugins --skip-themes --path={wpsite.path}'
else: else:
return JsonResponse({'status': 0, 'error_message': 'Invalid setting type'}) resp = {'status': 0, 'error_message': 'Invalid setting type'}
if data.get('legacy_response'):
import json
return HttpResponse(json.dumps(resp))
else:
return JsonResponse(resp)
result = ProcessUtilities.outputExecutioner(command) result = ProcessUtilities.outputExecutioner(command)
if result.find('Error:') > -1: if result.find('Error:') > -1:
return JsonResponse({'status': 0, 'error_message': result}) resp = {'status': 0, 'error_message': result}
if data.get('legacy_response'):
return JsonResponse({'status': 1, 'error_message': 'None'}) import json
return HttpResponse(json.dumps(resp))
else:
return JsonResponse(resp)
resp = {'status': 1, 'error_message': 'None'}
if data.get('legacy_response'):
import json
return HttpResponse(json.dumps(resp))
else:
return JsonResponse(resp)
except BaseException as msg: except BaseException as msg:
return JsonResponse({'status': 0, 'error_message': str(msg)}) resp = {'status': 0, 'error_message': str(msg)}
if data and data.get('legacy_response'):
import json
return HttpResponse(json.dumps(resp))
else:
return JsonResponse(resp)
def submitWorpressCreation(self, userID=None, data=None): def submitWorpressCreation(self, userID=None, data=None):
try: try:
@@ -4572,8 +4602,7 @@ StrictHostKeyChecking no
websites = ACLManager.searchWebsiteObjects(currentlACL, userID, searchTerm) websites = ACLManager.searchWebsiteObjects(currentlACL, userID, searchTerm)
json_data = "[" json_data = []
checker = 0
try: try:
ipFile = "/etc/cyberpanel/machineIP" ipFile = "/etc/cyberpanel/machineIP"
@@ -4604,19 +4633,34 @@ StrictHostKeyChecking no
PHPVersionActual = 'PHP 8.1' PHPVersionActual = 'PHP 8.1'
diskUsed = "%sMB" % str(DiskUsage) diskUsed = "%sMB" % str(DiskUsage)
dic = {'domain': items.domain, 'adminEmail': items.adminEmail, 'ipAddress': ipAddress,
'admin': items.admin.userName, 'package': items.package.packageName, 'state': state,
'diskUsed': diskUsed, 'phpVersion': PHPVersionActual}
if checker == 0: # Get WordPress sites for this website
json_data = json_data + json.dumps(dic) wp_sites = []
checker = 1 try:
else: wp_sites = WPSites.objects.filter(owner=items)
json_data = json_data + ',' + json.dumps(dic) wp_sites = [{
'id': wp.id,
'title': wp.title,
'url': wp.FinalURL,
'version': wp.version if hasattr(wp, 'version') else 'Unknown',
'phpVersion': wp.phpVersion if hasattr(wp, 'phpVersion') else 'Unknown'
} for wp in wp_sites]
except:
pass
json_data = json_data + ']' json_data.append({
'domain': items.domain,
'adminEmail': items.adminEmail,
'ipAddress': ipAddress,
'admin': items.admin.userName,
'package': items.package.packageName,
'state': state,
'diskUsed': diskUsed,
'phpVersion': PHPVersionActual,
'wp_sites': wp_sites
})
return json_data return json.dumps(json_data)
def findWebsitesJson(self, currentACL, userID, pageNumber): def findWebsitesJson(self, currentACL, userID, pageNumber):
finalPageNumber = ((pageNumber * 10)) - 10 finalPageNumber = ((pageNumber * 10)) - 10
@@ -4779,7 +4823,6 @@ StrictHostKeyChecking no
return ACLManager.loadErrorJson() return ACLManager.loadErrorJson()
tempStatusPath = "/home/cyberpanel/" + str(randint(1000, 9999)) tempStatusPath = "/home/cyberpanel/" + str(randint(1000, 9999))
execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/virtualHostUtilities.py" execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/virtualHostUtilities.py"
execPath = execPath + " switchServer --phpVersion '" + phpVersion + "' --server " + str( execPath = execPath + " switchServer --phpVersion '" + phpVersion + "' --server " + str(
server) + " --virtualHostName " + domainName + " --tempStatusPath " + tempStatusPath server) + " --virtualHostName " + domainName + " --tempStatusPath " + tempStatusPath
@@ -7221,5 +7264,4 @@ StrictHostKeyChecking no
except BaseException as msg: except BaseException as msg:
data_ret = {'status': 0, 'fetchStatus': 0, 'error_message': str(msg)} data_ret = {'status': 0, 'fetchStatus': 0, 'error_message': str(msg)}
json_data = json.dumps(data_ret) json_data = json.dumps(data_ret)
return HttpResponse(json_data) return HttpResponse(json_data)