mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-11-06 13:25:51 +01:00
design overall
This commit is contained in:
0
ApachController/ApacheController.py
Executable file → Normal file
0
ApachController/ApacheController.py
Executable file → Normal file
0
ApachController/ApacheVhosts.py
Executable file → Normal file
0
ApachController/ApacheVhosts.py
Executable file → Normal file
0
ApachController/__init__.py
Executable file → Normal file
0
ApachController/__init__.py
Executable file → Normal file
0
ApachController/phpApache.xml
Executable file → Normal file
0
ApachController/phpApache.xml
Executable file → Normal file
0
CLManager/CLPackages.py
Executable file → Normal file
0
CLManager/CLPackages.py
Executable file → Normal file
0
CLManager/templates/CLManager/cloudLinux.html
Executable file → Normal file
0
CLManager/templates/CLManager/cloudLinux.html
Executable file → Normal file
0
CLManager/templates/CLManager/createPackage.html
Executable file → Normal file
0
CLManager/templates/CLManager/createPackage.html
Executable file → Normal file
0
CLManager/templates/CLManager/listPackages.html
Executable file → Normal file
0
CLManager/templates/CLManager/listPackages.html
Executable file → Normal file
0
CLManager/templates/CLManager/listWebsites.html
Executable file → Normal file
0
CLManager/templates/CLManager/listWebsites.html
Executable file → Normal file
0
CLManager/templates/CLManager/monitorUsage.html
Executable file → Normal file
0
CLManager/templates/CLManager/monitorUsage.html
Executable file → Normal file
0
CLManager/templates/CLManager/notAvailable.html
Executable file → Normal file
0
CLManager/templates/CLManager/notAvailable.html
Executable file → Normal file
0
CLManager/templates/CLManager/websiteContainerLimit.html
Executable file → Normal file
0
CLManager/templates/CLManager/websiteContainerLimit.html
Executable file → Normal file
0
CLScript/CloudLinuxAdmins.py
Executable file → Normal file
0
CLScript/CloudLinuxAdmins.py
Executable file → Normal file
0
CLScript/CloudLinuxDB.py
Executable file → Normal file
0
CLScript/CloudLinuxDB.py
Executable file → Normal file
0
CLScript/CloudLinuxDomains.py
Executable file → Normal file
0
CLScript/CloudLinuxDomains.py
Executable file → Normal file
0
CLScript/CloudLinuxPackages.py
Executable file → Normal file
0
CLScript/CloudLinuxPackages.py
Executable file → Normal file
0
CLScript/CloudLinuxResellers.py
Executable file → Normal file
0
CLScript/CloudLinuxResellers.py
Executable file → Normal file
0
CLScript/CloudLinuxUsers.py
Executable file → Normal file
0
CLScript/CloudLinuxUsers.py
Executable file → Normal file
0
CLScript/UserInfo.py
Executable file → Normal file
0
CLScript/UserInfo.py
Executable file → Normal file
0
CLScript/panel_info.py
Executable file → Normal file
0
CLScript/panel_info.py
Executable file → Normal file
0
CyberCP/__init__.py
Executable file → Normal file
0
CyberCP/__init__.py
Executable file → Normal file
0
CyberCP/secMiddleware.py
Executable file → Normal file
0
CyberCP/secMiddleware.py
Executable file → Normal file
8
CyberCP/settings.py
Executable file → Normal file
8
CyberCP/settings.py
Executable file → Normal file
@@ -104,25 +104,25 @@ WSGI_APPLICATION = 'CyberCP.wsgi.application'
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
|
||||
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'cyberpanel',
|
||||
'USER': 'cyberpanel',
|
||||
'PASSWORD': 'JjWbFBFDxMI8D8',
|
||||
'PASSWORD': 'SLTUIUxqhulwsh',
|
||||
'HOST': 'localhost',
|
||||
'PORT': ''
|
||||
'PORT':''
|
||||
},
|
||||
'rootdb': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'mysql',
|
||||
'USER': 'root',
|
||||
'PASSWORD': 'JjWbFBFDxMI8D8',
|
||||
'PASSWORD': 'SLTUIUxqhulwsh',
|
||||
'HOST': 'localhost',
|
||||
'PORT': '',
|
||||
},
|
||||
}
|
||||
|
||||
DATABASE_ROUTERS = ['backup.backupRouter.backupRouter']
|
||||
|
||||
# Password validation
|
||||
|
||||
0
CyberCP/urls.py
Executable file → Normal file
0
CyberCP/urls.py
Executable file → Normal file
0
CyberCP/wsgi.py
Executable file → Normal file
0
CyberCP/wsgi.py
Executable file → Normal file
@@ -4,7 +4,7 @@ app.controller('createIncrementalBackups', function ($scope, $http, $timeout) {
|
||||
|
||||
$scope.destination = true;
|
||||
$scope.backupButton = true;
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
$scope.runningBackup = true;
|
||||
$scope.restoreSt = true;
|
||||
|
||||
@@ -48,7 +48,7 @@ app.controller('createIncrementalBackups', function ($scope, $http, $timeout) {
|
||||
$scope.destination = false;
|
||||
$scope.runningBackup = false;
|
||||
$scope.backupButton = false;
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
$scope.fileName = response.data.fileName;
|
||||
$scope.status = response.data.status;
|
||||
$scope.populateCurrentRecords();
|
||||
@@ -66,7 +66,7 @@ app.controller('createIncrementalBackups', function ($scope, $http, $timeout) {
|
||||
} else {
|
||||
$timeout.cancel();
|
||||
$scope.cyberpanelLoadingBottom = true;
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
$scope.backupButton = false;
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ app.controller('createIncrementalBackups', function ($scope, $http, $timeout) {
|
||||
$scope.tempPath = response.data.tempPath;
|
||||
getBackupStatus();
|
||||
} else {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: response.data.error_message,
|
||||
@@ -295,7 +295,7 @@ app.controller('createIncrementalBackups', function ($scope, $http, $timeout) {
|
||||
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
if (response.data.status === 1) {
|
||||
$scope.jobs = response.data.data;
|
||||
} else {
|
||||
@@ -367,7 +367,7 @@ app.controller('createIncrementalBackups', function ($scope, $http, $timeout) {
|
||||
|
||||
|
||||
app.controller('incrementalDestinations', function ($scope, $http) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
$scope.sftpHide = true;
|
||||
$scope.awsHide = true;
|
||||
|
||||
@@ -386,7 +386,7 @@ app.controller('incrementalDestinations', function ($scope, $http) {
|
||||
|
||||
$scope.populateCurrentRecords = function () {
|
||||
|
||||
$scope.cyberpanelLoading = false;
|
||||
$scope.cyberpanelLoading = true;
|
||||
|
||||
|
||||
url = "/IncrementalBackups/populateCurrentRecords";
|
||||
@@ -413,7 +413,7 @@ app.controller('incrementalDestinations', function ($scope, $http) {
|
||||
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
if (response.data.status === 1) {
|
||||
$scope.records = response.data.data;
|
||||
} else {
|
||||
@@ -427,7 +427,7 @@ app.controller('incrementalDestinations', function ($scope, $http) {
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Could not connect to server, please refresh this page',
|
||||
@@ -438,7 +438,7 @@ app.controller('incrementalDestinations', function ($scope, $http) {
|
||||
};
|
||||
|
||||
$scope.addDestination = function (type) {
|
||||
$scope.cyberpanelLoading = false;
|
||||
$scope.cyberpanelLoading = true;
|
||||
|
||||
|
||||
url = "/IncrementalBackups/addDestination";
|
||||
@@ -469,7 +469,7 @@ app.controller('incrementalDestinations', function ($scope, $http) {
|
||||
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
$scope.populateCurrentRecords();
|
||||
if (response.data.status === 1) {
|
||||
new PNotify({
|
||||
@@ -488,7 +488,7 @@ app.controller('incrementalDestinations', function ($scope, $http) {
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Could not connect to server, please refresh this page',
|
||||
@@ -499,7 +499,7 @@ app.controller('incrementalDestinations', function ($scope, $http) {
|
||||
};
|
||||
|
||||
$scope.removeDestination = function (type, ipAddress) {
|
||||
$scope.cyberpanelLoading = false;
|
||||
$scope.cyberpanelLoading = true;
|
||||
|
||||
|
||||
url = "/IncrementalBackups/removeDestination";
|
||||
@@ -520,7 +520,7 @@ app.controller('incrementalDestinations', function ($scope, $http) {
|
||||
|
||||
|
||||
function ListInitialDatas(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
$scope.populateCurrentRecords();
|
||||
if (response.data.status === 1) {
|
||||
new PNotify({
|
||||
@@ -539,7 +539,7 @@ app.controller('incrementalDestinations', function ($scope, $http) {
|
||||
}
|
||||
|
||||
function cantLoadInitialDatas(response) {
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
new PNotify({
|
||||
title: 'Operation Failed!',
|
||||
text: 'Could not connect to server, please refresh this page',
|
||||
@@ -1073,7 +1073,7 @@ app.controller('restoreRemoteBackupsInc', function ($scope, $http, $timeout) {
|
||||
$scope.destination = false;
|
||||
$scope.runningBackup = false;
|
||||
$scope.backupButton = false;
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
$scope.fileName = response.data.fileName;
|
||||
$scope.status = response.data.status;
|
||||
$scope.populateCurrentRecords();
|
||||
@@ -1096,7 +1096,7 @@ app.controller('restoreRemoteBackupsInc', function ($scope, $http, $timeout) {
|
||||
} else {
|
||||
$timeout.cancel();
|
||||
$scope.cyberpanelLoadingBottom = true;
|
||||
$scope.cyberpanelLoading = true;
|
||||
$scope.cyberpanelLoading = false;
|
||||
$scope.backupButton = false;
|
||||
}
|
||||
|
||||
|
||||
0
IncBackups/templates/IncBackups/backupSchedule.html
Executable file → Normal file
0
IncBackups/templates/IncBackups/backupSchedule.html
Executable file → Normal file
654
IncBackups/templates/IncBackups/createBackup.html
Executable file → Normal file
654
IncBackups/templates/IncBackups/createBackup.html
Executable file → Normal file
@@ -1,168 +1,560 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Create Incremental Backup" %}{% endblock %}
|
||||
|
||||
{% block header_scripts %}
|
||||
<style>
|
||||
/* Page Specific Styles */
|
||||
.backup-wrapper {
|
||||
background: transparent;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.backup-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Page Header */
|
||||
.page-header {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 25px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #2f3640;
|
||||
margin: 0 0 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.page-header .icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: #5b5fcf;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
box-shadow: 0 4px 12px rgba(91,95,207,0.3);
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
font-size: 14px;
|
||||
color: #64748b;
|
||||
margin: 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.docs-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: #5b5fcf;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.docs-link:hover {
|
||||
color: #4b4fbf;
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
/* Content Section */
|
||||
.content-section {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #2f3640;
|
||||
margin-bottom: 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.section-title::before {
|
||||
content: '';
|
||||
width: 4px;
|
||||
height: 24px;
|
||||
background: #5b5fcf;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* Form Styles */
|
||||
.form-group {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #2f3640;
|
||||
margin-bottom: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
background: #f8f9ff;
|
||||
color: #2f3640;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(91,95,207,0.1);
|
||||
}
|
||||
|
||||
.form-select {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
background: #f8f9ff;
|
||||
color: #2f3640;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-select:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(91,95,207,0.1);
|
||||
}
|
||||
|
||||
/* Checkbox Styles */
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.checkbox-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
background: #f8f9ff;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.checkbox-wrapper:hover {
|
||||
background: #f0f0ff;
|
||||
border-color: #5b5fcf;
|
||||
}
|
||||
|
||||
.checkbox-wrapper input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
font-size: 14px;
|
||||
color: #2f3640;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Button Styles */
|
||||
.btn-primary {
|
||||
background: #5b5fcf;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 30px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #4b4fbf;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(91,95,207,0.3);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
background: #94a3b8;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Status Textarea */
|
||||
.status-textarea {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
font-size: 13px;
|
||||
font-family: 'Monaco', 'Consolas', monospace;
|
||||
line-height: 1.5;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
background: #f8f9ff;
|
||||
color: #2f3640;
|
||||
resize: vertical;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
.backups-table {
|
||||
width: 100%;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.backups-table th {
|
||||
background: #f8f9ff;
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #64748b;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.backups-table td {
|
||||
padding: 15px;
|
||||
font-size: 14px;
|
||||
color: #2f3640;
|
||||
border-bottom: 1px solid #f0f0ff;
|
||||
}
|
||||
|
||||
.backups-table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.backups-table tr:hover {
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
background: #5b5fcf;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background: #4b4fbf;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(91,95,207,0.3);
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background: #dc2626;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(239,68,68,0.3);
|
||||
}
|
||||
|
||||
/* Modal Styles */
|
||||
.modal-content {
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
background: #f8f9ff;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
border-radius: 12px 12px 0 0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #2f3640;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
background: #f8f9ff;
|
||||
border-top: 1px solid #e8e9ff;
|
||||
border-radius: 0 0 12px 12px;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
/* Loading Spinner */
|
||||
.loading-spinner {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid #5b5fcf;
|
||||
border-radius: 50%;
|
||||
border-top-color: transparent;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* Angular cloak */
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Empty State */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #8893a7;
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
font-size: 48px;
|
||||
color: #e8e9ff;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.backup-wrapper {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.backups-table {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.backups-table th,
|
||||
.backups-table td {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% load static %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
<div class="container">
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Backup Website" %} - <a target="_blank" href="https://cyberpanel.net/docs/2-create-restore-incremental-backups/"
|
||||
style="height: 23px;line-height: 21px;"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red" title=""><span>{% trans "Backup Docs" %}</span></a>
|
||||
</h2>
|
||||
<p>{% trans "This page can be used to create incremental backups for your websites." %}</p>
|
||||
<div class="backup-wrapper">
|
||||
<div class="backup-container" ng-controller="createIncrementalBackups" ng-init="cyberpanelLoading=false" ng-cloak>
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
<div class="icon">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
</div>
|
||||
{% trans "Create Incremental Backup" %}
|
||||
</h1>
|
||||
<p>{% trans "Create incremental backups for your websites with efficient storage usage and quick restore capabilities." %}</p>
|
||||
<a href="https://cyberpanel.net/docs/2-create-restore-incremental-backups/" target="_blank" class="docs-link">
|
||||
<i class="fas fa-book"></i>
|
||||
{% trans "View Documentation" %}
|
||||
<i class="fas fa-external-link-alt" style="font-size: 11px;"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div ng-controller="createIncrementalBackups" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Backup Website" %} <img ng-hide="cyberpanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
|
||||
<form action="/" class="form-horizontal bordered-row">
|
||||
<!-- Configuration Section -->
|
||||
<div class="content-section">
|
||||
<h2 class="section-title">{% trans "Backup Configuration" %}</h2>
|
||||
|
||||
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-change="fetchDetails()" ng-model="websiteToBeBacked" class="form-control">
|
||||
<label class="form-label">{% trans "Select Website" %}</label>
|
||||
<select ng-change="fetchDetails()" ng-model="websiteToBeBacked" class="form-select">
|
||||
<option value="">{% trans "Choose a website to backup" %}</option>
|
||||
{% for items in websiteList %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="destination" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Destination" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-change="destinationSelection()" ng-model="backupDestinations"
|
||||
class="form-control">
|
||||
<label class="form-label">{% trans "Backup Destination" %}</label>
|
||||
<select ng-change="destinationSelection()" ng-model="backupDestinations" class="form-select">
|
||||
<option value="">{% trans "Select backup destination" %}</option>
|
||||
{% for items in destinations %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="destination" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Backup Content" %}</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteData" type="checkbox" value="">
|
||||
Data
|
||||
</label>
|
||||
<label class="form-label">{% trans "Backup Content" %}</label>
|
||||
<div class="checkbox-group">
|
||||
<div class="checkbox-wrapper">
|
||||
<input ng-model="websiteData" type="checkbox" id="backup-data" checked>
|
||||
<label for="backup-data" class="checkbox-label">{% trans "Website Data" %}</label>
|
||||
</div>
|
||||
<div class="checkbox-wrapper">
|
||||
<input ng-model="websiteDatabases" type="checkbox" id="backup-databases">
|
||||
<label for="backup-databases" class="checkbox-label">{% trans "Databases" %}</label>
|
||||
</div>
|
||||
<div class="checkbox-wrapper">
|
||||
<input ng-model="websiteEmails" type="checkbox" id="backup-emails">
|
||||
<label for="backup-emails" class="checkbox-label">{% trans "Email Accounts" %}</label>
|
||||
</div>
|
||||
</div>
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteDatabases" type="checkbox" value="">
|
||||
Databases
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteEmails" type="checkbox" value="">
|
||||
Emails
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!---
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="websiteSSLs" type="checkbox" value="">
|
||||
SSL Certificates
|
||||
</label>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
|
||||
<!---- if Backup is running ----->
|
||||
|
||||
<!-- Backup Status -->
|
||||
<div ng-hide="runningBackup" class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<div class="col-sm-12">
|
||||
<textarea ng-model="status" class="form-control" rows="10"></textarea>
|
||||
<label class="form-label">{% trans "Backup Progress" %}</label>
|
||||
<textarea ng-model="status" class="status-textarea" readonly></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Create Backup Button -->
|
||||
<div ng-hide="backupButton" class="form-group" style="text-align: center; margin-top: 30px;">
|
||||
<button type="button" ng-click="createBackup()" class="btn-primary" ng-disabled="cyberpanelLoading">
|
||||
<i class="fas fa-shield-alt" ng-if="!cyberpanelLoading"></i>
|
||||
<i class="fas fa-spinner fa-spin" ng-if="cyberpanelLoading"></i>
|
||||
<span ng-if="!cyberpanelLoading">{% trans "Create Backup" %}</span>
|
||||
<span ng-if="cyberpanelLoading">{% trans "Creating Backup..." %}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<!---- if Backup is running------>
|
||||
<!-- Existing Backups -->
|
||||
<div class="content-section">
|
||||
<h2 class="section-title">{% trans "Existing Backups" %}</h2>
|
||||
|
||||
<div ng-hide="backupButton" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-click="createBackup()"
|
||||
class="btn btn-primary btn-lg btn-block">{% trans "Create Backup" %}</button>
|
||||
</div>
|
||||
<div ng-if="!records || records.length === 0" class="empty-state">
|
||||
<i class="fas fa-folder-open"></i>
|
||||
<p>{% trans "No backups created yet" %}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<!------ List of records --------------->
|
||||
|
||||
<div class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table class="table">
|
||||
<table class="backups-table" ng-if="records && records.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "ID" %}</th>
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "Restore" %}</th>
|
||||
<th>{% trans "Delete" %}</th>
|
||||
<th>{% trans "Backup ID" %}</th>
|
||||
<th>{% trans "Date Created" %}</th>
|
||||
<th style="text-align: center;">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records track by $index">
|
||||
<td ng-bind="record.id"></td>
|
||||
<td ng-bind="record.date"></td>
|
||||
<td>
|
||||
<i class="fas fa-archive" style="color: #5b5fcf; margin-right: 8px;"></i>
|
||||
<span ng-bind="record.id"></span>
|
||||
</td>
|
||||
<td ng-bind="record.date"></td>
|
||||
<td style="text-align: center;">
|
||||
<a ng-click="restore(record.id)" data-toggle="modal" data-target="#settings"
|
||||
ng-click='deleteCLPackage()'
|
||||
class="btn btn-border btn-alt border-green btn-link font-green"
|
||||
title=""><span>Restore Points</span></a>
|
||||
class="action-btn" title="Restore">
|
||||
<i class="fas fa-undo"></i>
|
||||
{% trans "Restore Points" %}
|
||||
</a>
|
||||
<button type="button" class="delete-btn" ng-click="deleteBackup(record.id)" style="margin-left: 10px;">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
{% trans "Delete" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Restore Points Modal -->
|
||||
<div id="settings" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
×
|
||||
</button>
|
||||
<h4 class="modal-title">Restore Points
|
||||
<img ng-hide="cyberpanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
<h4 class="modal-title">
|
||||
{% trans "Restore Points" %}
|
||||
<span class="loading-spinner" ng-show="cyberpanelLoading"></span>
|
||||
</h4>
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<table class="table">
|
||||
<table class="backups-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Job ID" %}</th>
|
||||
<th>{% trans "Snapshot ID" %}</th>
|
||||
<th>{% trans "Type" %}</th>
|
||||
<th>{% trans "Destination" %}</th>
|
||||
<th>{% trans "Action" %}</th>
|
||||
<th style="text-align: center;">{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -171,57 +563,31 @@
|
||||
<td ng-bind="job.snapshotid"></td>
|
||||
<td ng-bind="job.type"></td>
|
||||
<td ng-bind="job.destination"></td>
|
||||
<td>
|
||||
<a ng-click="restorePoint(job.id, 0)" class="btn btn-border btn-alt border-green btn-link font-green"
|
||||
title=""><span>Restore</span></a>
|
||||
<td style="text-align: center;">
|
||||
<button ng-click="restorePoint(job.id, 0)" class="action-btn">
|
||||
<i class="fas fa-undo"></i>
|
||||
{% trans "Restore" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div ng-hide="restoreSt" class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<div class="col-sm-12">
|
||||
<textarea ng-model="status"
|
||||
class="form-control"
|
||||
rows="7"></textarea>
|
||||
<div ng-hide="restoreSt" style="margin-top: 20px;">
|
||||
<label class="form-label">{% trans "Restore Progress" %}</label>
|
||||
<textarea ng-model="status" class="status-textarea" rows="7" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" ng-disabled="savingSettings"
|
||||
class="btn btn-default" data-dismiss="modal">
|
||||
Close
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">
|
||||
{% trans "Close" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<a href="">
|
||||
<td ng-click="deleteBackup(record.id)"><img
|
||||
src="{% static 'images/delete.png' %}"></td>
|
||||
</a>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!------ List of records --------------->
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script src="{% static 'IncBackups/IncBackups.js' %}"></script>
|
||||
{% endblock %}
|
||||
637
IncBackups/templates/IncBackups/incrementalDestinations.html
Executable file → Normal file
637
IncBackups/templates/IncBackups/incrementalDestinations.html
Executable file → Normal file
@@ -1,184 +1,503 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Set up Backup Destinations" %}{% endblock %}
|
||||
|
||||
{% block header_scripts %}
|
||||
<style>
|
||||
/* Page Specific Styles */
|
||||
.backup-wrapper {
|
||||
background: transparent;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.backup-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Page Header */
|
||||
.page-header {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 25px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #2f3640;
|
||||
margin: 0 0 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.page-header .icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: #5b5fcf;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
box-shadow: 0 4px 12px rgba(91,95,207,0.3);
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
font-size: 14px;
|
||||
color: #64748b;
|
||||
margin: 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.docs-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: #5b5fcf;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.docs-link:hover {
|
||||
color: #4b4fbf;
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
/* Content Section */
|
||||
.content-section {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #2f3640;
|
||||
margin-bottom: 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.section-title::before {
|
||||
content: '';
|
||||
width: 4px;
|
||||
height: 24px;
|
||||
background: #5b5fcf;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* Form Styles */
|
||||
.form-group {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #2f3640;
|
||||
margin-bottom: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
background: #f8f9ff;
|
||||
color: #2f3640;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(91,95,207,0.1);
|
||||
}
|
||||
|
||||
.form-select {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
background: #f8f9ff;
|
||||
color: #2f3640;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-select:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(91,95,207,0.1);
|
||||
}
|
||||
|
||||
.form-help {
|
||||
font-size: 12px;
|
||||
color: #8893a7;
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.form-help i {
|
||||
font-size: 13px;
|
||||
color: #5b5fcf;
|
||||
}
|
||||
|
||||
/* Button Styles */
|
||||
.btn-primary {
|
||||
background: #5b5fcf;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 30px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #4b4fbf;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(91,95,207,0.3);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
background: #94a3b8;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Angular cloak */
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Loading Spinner */
|
||||
.loading-spinner {
|
||||
display: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid #ffffff;
|
||||
border-radius: 50%;
|
||||
border-top-color: transparent;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.loading-spinner.ng-hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Show spinner only when Angular explicitly shows it */
|
||||
.loading-spinner[style*="display: none;"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.loading-spinner.ng-hide-add,
|
||||
.loading-spinner.ng-hide-remove {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.btn-primary .loading-spinner:not([style*="display: none;"]):not(.ng-hide) {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
.destinations-table {
|
||||
width: 100%;
|
||||
margin-top: 30px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.destinations-table th {
|
||||
background: #f8f9ff;
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #64748b;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.destinations-table td {
|
||||
padding: 15px;
|
||||
font-size: 14px;
|
||||
color: #2f3640;
|
||||
border-bottom: 1px solid #f0f0ff;
|
||||
}
|
||||
|
||||
.destinations-table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.destinations-table tr:hover {
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background: #dc2626;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(239,68,68,0.3);
|
||||
}
|
||||
|
||||
/* Empty State */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #8893a7;
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
font-size: 48px;
|
||||
color: #e8e9ff;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Alert Box */
|
||||
.alert-box {
|
||||
background: #f0f0ff;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.alert-box i {
|
||||
color: #5b5fcf;
|
||||
font-size: 18px;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.alert-box-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.alert-box-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #2f3640;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.alert-box-text {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.backup-wrapper {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.destinations-table {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.destinations-table th,
|
||||
.destinations-table td {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% load static %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
<div class="container">
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Set up Incremental Backup Destinations" %} - <a target="_blank"
|
||||
href="https://cyberpanel.net/docs/1-add-remove-destinations-for-incremental-backups/"
|
||||
style="height: 23px;line-height: 21px;"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>{% trans "Remote Backups" %}</span></a>
|
||||
</h2>
|
||||
<p>{% trans "On this page you can set up your Backup destinations. (SFTP and AWS)" %}</p>
|
||||
<div class="backup-wrapper">
|
||||
<div class="backup-container" ng-controller="incrementalDestinations">
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
<div class="icon">
|
||||
<i class="fas fa-cloud-upload-alt"></i>
|
||||
</div>
|
||||
{% trans "Set up Incremental Backup Destinations" %}
|
||||
</h1>
|
||||
<p>{% trans "Configure your backup destinations for incremental backups. Currently supporting AWS S3 storage." %}</p>
|
||||
<a href="https://cyberpanel.net/docs/1-add-remove-destinations-for-incremental-backups/" target="_blank" class="docs-link">
|
||||
<i class="fas fa-book"></i>
|
||||
{% trans "View Documentation" %}
|
||||
<i class="fas fa-external-link-alt" style="font-size: 11px;"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div ng-controller="incrementalDestinations" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Set up Backup Destinations." %} <img ng-hide="cyberpanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
<!-- Configuration Section -->
|
||||
<div class="content-section">
|
||||
<h2 class="section-title">{% trans "Add Backup Destination" %}</h2>
|
||||
|
||||
<div class="alert-box">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<div class="alert-box-content">
|
||||
<div class="alert-box-title">{% trans "Important Information" %}</div>
|
||||
<div class="alert-box-text">{% trans "Incremental backups allow you to save storage space and bandwidth by only backing up changes since the last backup." %}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<form action="/" class="form-horizontal bordered-row">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "Destination Type" %}</label>
|
||||
<select ng-change="fetchDetails()" ng-model="destinationType" class="form-select">
|
||||
<option value="">{% trans "Select destination type" %}</option>
|
||||
<option value="AWS">AWS S3</option>
|
||||
</select>
|
||||
<div class="form-help">
|
||||
<i class="fas fa-lightbulb"></i>
|
||||
{% trans "Choose your backup storage provider" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AWS Configuration -->
|
||||
<div ng-hide="awsHide">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "AWS Access Key ID" %}</label>
|
||||
<input type="text" class="form-control" ng-model="AWS_ACCESS_KEY_ID" placeholder="{% trans 'Enter your AWS access key' %}" required>
|
||||
<div class="form-help">
|
||||
<i class="fas fa-key"></i>
|
||||
{% trans "You can find this in your AWS IAM console" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Type" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-change="fetchDetails()" ng-model="destinationType" class="form-control">
|
||||
{# <option>SFTP</option>#}
|
||||
<option>AWS</option>
|
||||
</select>
|
||||
<label class="form-label">{% trans "AWS Secret Access Key" %}</label>
|
||||
<input type="password" class="form-control" ng-model="AWS_SECRET_ACCESS_KEY" placeholder="{% trans 'Enter your AWS secret key' %}" required>
|
||||
<div class="form-help">
|
||||
<i class="fas fa-lock"></i>
|
||||
{% trans "Keep this key secure and never share it" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--- SFTP --->
|
||||
|
||||
<div ng-hide="sftpHide" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "IP Address" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="IPAddress" required>
|
||||
<button type="button" ng-click="addDestination('AWS')" class="btn-primary" ng-disabled="cyberpanelLoading">
|
||||
<i class="fas fa-plus-circle" ng-hide="cyberpanelLoading"></i>
|
||||
<i class="fas fa-spinner fa-spin" ng-show="cyberpanelLoading" ng-cloak></i>
|
||||
<span ng-hide="cyberpanelLoading">{% trans "Add Destination" %}</span>
|
||||
<span ng-show="cyberpanelLoading" ng-cloak>{% trans "Adding..." %}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="sftpHide" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Password" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input placeholder="" type="password" class="form-control" ng-model="password" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="sftpHide" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Port" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input placeholder="{% trans "Backup server SSH Port, leave empty for 22." %}"
|
||||
type="text" class="form-control" ng-model="backupSSHPort" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="sftpHide" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-click="addDestination('SFTP')"
|
||||
class="btn btn-primary btn-lg btn-block">{% trans "Add Destination" %}</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--- SFTP --->
|
||||
|
||||
|
||||
<!------ List of Destinations --------------->
|
||||
|
||||
<div ng-hide="sftpHide" class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "IP" %}</th>
|
||||
<th>{% trans "Port" %}</th>
|
||||
<th>{% trans "Delete" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records track by $index">
|
||||
<td ng-bind="record.ip"></td>
|
||||
<td ng-bind="record.port"></td>
|
||||
<td ng-click="removeDestination('SFTP',record.ip)"><img src="{% static 'images/delete.png' %}">
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!------ List of records --------------->
|
||||
|
||||
|
||||
<!--- SFTP End --->
|
||||
|
||||
<!--- AWS Start --->
|
||||
|
||||
<div ng-hide="awsHide" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "AWS_ACCESS_KEY_ID" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="AWS_ACCESS_KEY_ID" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="awsHide" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "AWS_SECRET_ACCESS_KEY" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input placeholder="" type="password" class="form-control" ng-model="AWS_SECRET_ACCESS_KEY" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="awsHide" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-click="addDestination('AWS')"
|
||||
class="btn btn-primary btn-lg btn-block">{% trans "Add Destination" %}</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--- SFTP --->
|
||||
|
||||
|
||||
|
||||
|
||||
<!------ List of Destinations --------------->
|
||||
|
||||
<div ng-hide="awsHide" class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "AWS_ACCESS_KEY_ID" %}</th>
|
||||
<th>{% trans "Delete" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records track by $index">
|
||||
<td ng-bind="record.AWS_ACCESS_KEY_ID"></td>
|
||||
<td ng-click="removeDestination('AWS', record.AWS_ACCESS_KEY_ID)"><img src="{% static 'images/delete.png' %}">
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!------ List of records --------------->
|
||||
|
||||
|
||||
<!--- AWS End --->
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Existing Destinations -->
|
||||
<div class="content-section" ng-hide="awsHide">
|
||||
<h2 class="section-title">{% trans "Configured Destinations" %}</h2>
|
||||
|
||||
<div ng-if="!records || records.length === 0" class="empty-state">
|
||||
<i class="fas fa-cloud-slash"></i>
|
||||
<p>{% trans "No backup destinations configured yet" %}</p>
|
||||
</div>
|
||||
|
||||
<table class="destinations-table" ng-if="records && records.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "AWS Access Key ID" %}</th>
|
||||
<th style="text-align: right;">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records track by $index">
|
||||
<td>
|
||||
<i class="fas fa-key" style="color: #5b5fcf; margin-right: 8px;"></i>
|
||||
<span ng-bind="record.AWS_ACCESS_KEY_ID"></span>
|
||||
</td>
|
||||
<td style="text-align: right;">
|
||||
<button type="button" class="delete-btn" ng-click="removeDestination('AWS', record.AWS_ACCESS_KEY_ID)">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
{% trans "Delete" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="{% static 'IncBackups/IncBackups.js' %}"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Initialize the page with AWS selected by default
|
||||
setTimeout(function() {
|
||||
var scope = angular.element(document.querySelector('[ng-controller="incrementalDestinations"]')).scope();
|
||||
if (scope && scope.fetchDetails) {
|
||||
scope.$apply(function() {
|
||||
scope.destinationType = 'AWS';
|
||||
scope.fetchDetails();
|
||||
});
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
0
IncBackups/templates/IncBackups/restoreRemoteBackups.html
Executable file → Normal file
0
IncBackups/templates/IncBackups/restoreRemoteBackups.html
Executable file → Normal file
0
WebTerminal/static/WebTerminal/main.js
Executable file → Normal file
0
WebTerminal/static/WebTerminal/main.js
Executable file → Normal file
0
WebTerminal/static/WebTerminal/term.js
Executable file → Normal file
0
WebTerminal/static/WebTerminal/term.js
Executable file → Normal file
0
WebTerminal/static/WebTerminal/ws.js
Executable file → Normal file
0
WebTerminal/static/WebTerminal/ws.js
Executable file → Normal file
0
WebTerminal/static/images/loading.gif
Executable file → Normal file
0
WebTerminal/static/images/loading.gif
Executable file → Normal file
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
0
WebTerminal/templates/WebTerminal/WebTerminal.html
Executable file → Normal file
0
WebTerminal/templates/WebTerminal/WebTerminal.html
Executable file → Normal file
0
WebTerminal/urls.py
Executable file → Normal file
0
WebTerminal/urls.py
Executable file → Normal file
0
api/__init__.py
Executable file → Normal file
0
api/__init__.py
Executable file → Normal file
0
api/admin.py
Executable file → Normal file
0
api/admin.py
Executable file → Normal file
0
api/apps.py
Executable file → Normal file
0
api/apps.py
Executable file → Normal file
0
api/migrations/__init__.py
Executable file → Normal file
0
api/migrations/__init__.py
Executable file → Normal file
0
api/models.py
Executable file → Normal file
0
api/models.py
Executable file → Normal file
0
api/tests.py
Executable file → Normal file
0
api/tests.py
Executable file → Normal file
0
api/urls.py
Executable file → Normal file
0
api/urls.py
Executable file → Normal file
0
api/views.py
Executable file → Normal file
0
api/views.py
Executable file → Normal file
0
backup/__init__.py
Executable file → Normal file
0
backup/__init__.py
Executable file → Normal file
0
backup/admin.py
Executable file → Normal file
0
backup/admin.py
Executable file → Normal file
0
backup/apps.py
Executable file → Normal file
0
backup/apps.py
Executable file → Normal file
0
backup/backupManager.py
Executable file → Normal file
0
backup/backupManager.py
Executable file → Normal file
0
backup/backupRouter.py
Executable file → Normal file
0
backup/backupRouter.py
Executable file → Normal file
0
backup/migrations/__init__.py
Executable file → Normal file
0
backup/migrations/__init__.py
Executable file → Normal file
0
backup/models.py
Executable file → Normal file
0
backup/models.py
Executable file → Normal file
0
backup/pluginManager.py
Executable file → Normal file
0
backup/pluginManager.py
Executable file → Normal file
0
backup/signals.py
Executable file → Normal file
0
backup/signals.py
Executable file → Normal file
0
backup/static/backup/backup.js
Executable file → Normal file
0
backup/static/backup/backup.js
Executable file → Normal file
0
backup/templates/backup/OneClickBackupSchedule.html
Executable file → Normal file
0
backup/templates/backup/OneClickBackupSchedule.html
Executable file → Normal file
661
backup/templates/backup/backup.html
Executable file → Normal file
661
backup/templates/backup/backup.html
Executable file → Normal file
@@ -1,111 +1,566 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Backup Website" %}{% endblock %}
|
||||
{% block title %}{% trans "Backup Website - CyberPanel" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
<div class="container">
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Backup Website" %} - <a target="_blank" href="http://go.cyberpanel.net/backup"
|
||||
style="height: 23px;line-height: 21px;"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>{% trans "Backup Docs" %}</span></a></h2>
|
||||
<p>{% trans "This page can be used to Backup your websites" %}</p>
|
||||
<style>
|
||||
.modern-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
padding: 3rem 0;
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
border-radius: 20px;
|
||||
animation: fadeInDown 0.5s ease-out;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 1.25rem;
|
||||
color: #64748b;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #5b5fcf;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #4547a9;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(91, 95, 207, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #fff;
|
||||
color: #5b5fcf;
|
||||
border: 1px solid #e8e9ff;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #f8f9ff;
|
||||
border-color: #5b5fcf;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(91, 95, 207, 0.2);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.825rem;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #dc2626;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
|
||||
}
|
||||
|
||||
.main-card {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 10px 40px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
overflow: hidden;
|
||||
margin-bottom: 2rem;
|
||||
animation: fadeInUp 0.5s ease-out;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
padding: 1.5rem 2rem;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.promo-banner {
|
||||
background: linear-gradient(135deg, #5b5fcf 0%, #8187ff 100%);
|
||||
color: white;
|
||||
padding: 1.5rem 2rem;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 4px 20px rgba(91, 95, 207, 0.3);
|
||||
}
|
||||
|
||||
.promo-text {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.promo-link {
|
||||
background: white;
|
||||
color: #5b5fcf;
|
||||
padding: 0.5rem 1.5rem;
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.promo-link:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.875rem 1rem;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 10px;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s ease;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
box-shadow: 0 0 0 3px rgba(91, 95, 207, 0.1);
|
||||
}
|
||||
|
||||
.running-backup-card {
|
||||
background: #fef3c7;
|
||||
border: 1px solid #fde68a;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.running-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.status-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: #fbbf24;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
.status-details h4 {
|
||||
margin: 0;
|
||||
color: #92400e;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.status-details p {
|
||||
margin: 0;
|
||||
color: #92400e;
|
||||
font-size: 0.875rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.backup-table {
|
||||
width: 100%;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.backup-table thead {
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
}
|
||||
|
||||
.backup-table th {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.backup-table td {
|
||||
padding: 1rem;
|
||||
color: #64748b;
|
||||
font-size: 0.875rem;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
}
|
||||
|
||||
.backup-table tbody tr:hover {
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
.backup-table tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.file-badge {
|
||||
background: #e0e7ff;
|
||||
color: #5b5fcf;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.size-badge {
|
||||
background: #f3f4f6;
|
||||
color: #1e293b;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-complete {
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
animation: slideInRight 0.3s ease-out;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
border: 1px solid #a7f3d0;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid #e8e9ff;
|
||||
border-top-color: #5b5fcf;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
display: inline-block;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.promo-banner {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.button-group button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Ensure elements are shown when website is selected
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var selectElement = document.getElementById('create-backup-select');
|
||||
if (selectElement) {
|
||||
selectElement.addEventListener('change', function() {
|
||||
if (this.value && this.value !== '') {
|
||||
// Show all elements with destinationHide class
|
||||
var elements = document.querySelectorAll('.destinationHide');
|
||||
elements.forEach(function(el) {
|
||||
el.style.display = 'block';
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="modern-container" ng-controller="backupWebsiteControl">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">
|
||||
<i class="fas fa-download"></i>
|
||||
{% trans "Backup Website" %}
|
||||
</h1>
|
||||
<p class="page-subtitle">{% trans "Create and manage backups of your websites to ensure data safety" %}</p>
|
||||
<div class="header-actions">
|
||||
<a href="http://go.cyberpanel.net/backup" target="_blank" class="btn-secondary">
|
||||
<i class="fas fa-book"></i>
|
||||
{% trans "Backup Documentation" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-controller="backupWebsiteControl" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Backup Website" %} <img ng-hide="backupLoading" src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="alert alert-warning">
|
||||
<p>Configure automatic backups to our secure servers in 60 seconds. <a
|
||||
href="/backup/OneClickBackups">Set-up now.</a></p>
|
||||
<!-- Promo Banner -->
|
||||
<div class="promo-banner">
|
||||
<span class="promo-text">
|
||||
<i class="fas fa-shield-alt" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Configure automatic backups to our secure servers in 60 seconds" %}
|
||||
</span>
|
||||
<a href="/backup/OneClickBackups" class="promo-link">
|
||||
<i class="fas fa-rocket"></i>
|
||||
{% trans "Set-up now" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
<form action="/" class="form-horizontal bordered-row">
|
||||
|
||||
<div class="main-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
<i class="fas fa-archive"></i>
|
||||
{% trans "Create New Backup" %}
|
||||
<span ng-hide="backupLoading" class="loading-spinner"></span>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<form action="/" method="post">
|
||||
<div class="form-section">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select id="create-backup-select" ng-model="websiteToBeBacked" class="form-control">
|
||||
<label class="form-label">{% trans "Select Website" %}</label>
|
||||
<select id="create-backup-select" ng-model="websiteToBeBacked" ng-change="fetchDetails()" class="form-control">
|
||||
<option value="">{% trans "Choose a website..." %}</option>
|
||||
{% for items in websiteList %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group destinationHide">
|
||||
<label class="col-sm-3 control-label">{% trans "Destination" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-change="destinationSelection()" ng-model="backupDestinations"
|
||||
class="form-control">
|
||||
<label class="form-label">{% trans "Destination" %}</label>
|
||||
<select ng-change="destinationSelection()" ng-model="backupDestinations" class="form-control">
|
||||
<option>{% trans "Home" %}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!---- if Backup is running ----->
|
||||
|
||||
<div ng-hide="runningBackup" class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>{% trans "File Name" %}</th>
|
||||
<th>{% trans "Status" %} <img ng-hide="backupLoadingBottom"
|
||||
src="{% static 'images/loading.gif' %}"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{% trans "Running" %}</td>
|
||||
<td>{$ fileName $}</td>
|
||||
<td style="color: red"><strong>{$ status $}</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!---- if Backup is running------>
|
||||
|
||||
<div class="form-group destinationHide">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-click="createBackup()" id="createBackup"
|
||||
class="btn btn-primary btn-lg btn-block">{% trans "Create Backup" %}</button>
|
||||
<!-- Running Backup Status -->
|
||||
<div ng-hide="runningBackup" class="running-backup-card">
|
||||
<div class="running-status">
|
||||
<div class="status-info">
|
||||
<div class="status-icon">
|
||||
<i class="fas fa-sync-alt fa-spin" style="color: white;"></i>
|
||||
</div>
|
||||
<div class="status-details">
|
||||
<h4>{% trans "Backup in Progress" %}</h4>
|
||||
<p>
|
||||
<strong>{% trans "File:" %}</strong> {$ fileName $} |
|
||||
<strong>{% trans "Status:" %}</strong> {$ status $}
|
||||
<span ng-hide="backupLoadingBottom" class="loading-spinner" style="margin-left: 0.5rem;"></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="cancelButton" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-click="cancelBackup()"
|
||||
class="btn btn-primary btn-lg btn-block">{% trans "Cancel Backup" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!------ List of records --------------->
|
||||
<div class="button-group">
|
||||
<button type="button" ng-click="createBackup()" id="createBackup" class="btn-primary destinationHide">
|
||||
<i class="fas fa-play"></i>
|
||||
{% trans "Create Backup" %}
|
||||
</button>
|
||||
<button type="button" ng-click="cancelBackup()" ng-hide="cancelButton" class="btn-danger">
|
||||
<i class="fas fa-stop"></i>
|
||||
{% trans "Cancel Backup" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table class="table">
|
||||
<!-- Existing Backups -->
|
||||
<div class="main-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
<i class="fas fa-history"></i>
|
||||
{% trans "Existing Backups" %}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body" style="padding: 0;">
|
||||
<table class="backup-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "ID" %}</th>
|
||||
@@ -113,55 +568,47 @@
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "Size" %}</th>
|
||||
<th>{% trans "Status" %}</th>
|
||||
<th>{% trans "Delete" %}</th>
|
||||
<th style="text-align: center;">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records track by $index">
|
||||
<td ng-bind="record.id"></td>
|
||||
<td ng-bind="record.file"></td>
|
||||
<td ng-bind="record.date"></td>
|
||||
<td ng-bind="record.size"></td>
|
||||
<td ng-bind="record.status"></td>
|
||||
<a href="">
|
||||
<td ng-click="deleteBackup(record.id)"><img
|
||||
src="{% static 'images/delete.png' %}"></td>
|
||||
</a>
|
||||
<td><strong ng-bind="record.id"></strong></td>
|
||||
<td>
|
||||
<span class="file-badge" ng-bind="record.file"></span>
|
||||
</td>
|
||||
<td>
|
||||
<i class="fas fa-calendar-alt" style="color: #64748b; margin-right: 0.5rem;"></i>
|
||||
<span ng-bind="record.date"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="size-badge" ng-bind="record.size"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="status-complete" ng-bind="record.status"></span>
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<button type="button" ng-click="deleteBackup(record.id)" class="btn-danger">
|
||||
<i class="fas fa-trash"></i>
|
||||
{% trans "Delete" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!------ List of records --------------->
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<div id="websiteDeleteFailure" class="alert alert-danger">
|
||||
<p>{% trans "Cannot delete website, Error message: " %}{$ errorMessage $}</p>
|
||||
<!-- Alert Messages -->
|
||||
<div id="websiteDeleteFailure" class="alert alert-danger" style="display: none;">
|
||||
<i class="fas fa-exclamation-circle"></i>
|
||||
{% trans "Cannot delete backup. Error message:" %} {$ errorMessage $}
|
||||
</div>
|
||||
|
||||
<div id="websiteDeleteSuccess" class="alert alert-success">
|
||||
<p>Website <strong>{$ deletedWebsite $}</strong> {% trans "Successfully Deleted" %}
|
||||
</p>
|
||||
<div id="websiteDeleteSuccess" class="alert alert-success" style="display: none;">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
{% trans "Backup" %} <strong>{$ deletedWebsite $}</strong> {% trans "successfully deleted" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
726
backup/templates/backup/backupDestinations.html
Executable file → Normal file
726
backup/templates/backup/backupDestinations.html
Executable file → Normal file
@@ -1,218 +1,652 @@
|
||||
{% extends "baseTemplate/index.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Set up Backup Destinations" %}{% endblock %}
|
||||
{% block title %}{% trans "Backup Destinations - CyberPanel" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
<div class="container">
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Set up Backup Destinations" %} - <a target="_blank"
|
||||
href="https://cyberpanel.net/KnowledgeBase/home/add-destination-scheduled-local-sftp-remote-backups/"
|
||||
style="height: 23px;line-height: 21px;"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>{% trans "Remote Backups" %}</span></a>
|
||||
<style>
|
||||
.modern-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
padding: 3rem 0;
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
border-radius: 20px;
|
||||
animation: fadeInDown 0.5s ease-out;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 1.25rem;
|
||||
color: #64748b;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #5b5fcf;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #4547a9;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(91, 95, 207, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #fff;
|
||||
color: #5b5fcf;
|
||||
border: 1px solid #e8e9ff;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #f8f9ff;
|
||||
border-color: #5b5fcf;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(91, 95, 207, 0.2);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.825rem;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #dc2626;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
|
||||
}
|
||||
|
||||
.promo-banner {
|
||||
background: linear-gradient(135deg, #5b5fcf 0%, #8187ff 100%);
|
||||
color: white;
|
||||
padding: 1.5rem 2rem;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 4px 20px rgba(91, 95, 207, 0.3);
|
||||
}
|
||||
|
||||
.promo-text {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.promo-link {
|
||||
background: white;
|
||||
color: #5b5fcf;
|
||||
padding: 0.5rem 1.5rem;
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.promo-link:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.main-card {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 10px 40px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
overflow: hidden;
|
||||
margin-bottom: 2rem;
|
||||
animation: fadeInUp 0.5s ease-out;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
padding: 1.5rem 2rem;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.destination-type-selector {
|
||||
background: #f8f9ff;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.destination-tabs {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
padding: 0.75rem 2rem;
|
||||
background: #fff;
|
||||
border: 2px solid #e8e9ff;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.tab-button.active {
|
||||
background: #5b5fcf;
|
||||
color: white;
|
||||
border-color: #5b5fcf;
|
||||
}
|
||||
|
||||
.tab-button:hover:not(.active) {
|
||||
border-color: #5b5fcf;
|
||||
color: #5b5fcf;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.875rem 1rem;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 10px;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s ease;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
box-shadow: 0 0 0 3px rgba(91, 95, 207, 0.1);
|
||||
}
|
||||
|
||||
.destination-table {
|
||||
width: 100%;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e9ff;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.destination-table thead {
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
}
|
||||
|
||||
.destination-table th {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.destination-table td {
|
||||
padding: 1rem;
|
||||
color: #64748b;
|
||||
font-size: 0.875rem;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
}
|
||||
|
||||
.destination-table tbody tr:hover {
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
.destination-table tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid #e8e9ff;
|
||||
border-top-color: #5b5fcf;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
display: inline-block;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.info-badge {
|
||||
background: #e0e7ff;
|
||||
color: #5b5fcf;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.port-badge {
|
||||
background: #f3f4f6;
|
||||
color: #1e293b;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.destination-icon {
|
||||
font-size: 2.5rem;
|
||||
color: #5b5fcf;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes fadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.promo-banner {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.destination-tabs {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="modern-container" ng-controller="backupDestinations" ng-init="destinationType='local'">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">
|
||||
<i class="fas fa-cloud-upload-alt"></i>
|
||||
{% trans "Backup Destinations" %}
|
||||
</h1>
|
||||
<p class="page-subtitle">{% trans "Configure local and remote destinations for your backups" %}</p>
|
||||
<div class="header-actions">
|
||||
<a href="https://cyberpanel.net/KnowledgeBase/home/add-destination-scheduled-local-sftp-remote-backups/"
|
||||
target="_blank"
|
||||
class="btn-secondary">
|
||||
<i class="fas fa-book"></i>
|
||||
{% trans "Remote Backup Guide" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Promo Banner -->
|
||||
<div class="promo-banner">
|
||||
<span class="promo-text">
|
||||
<i class="fas fa-shield-alt" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Configure automatic backups to our secure servers in 60 seconds" %}
|
||||
</span>
|
||||
<a href="/backup/OneClickBackups" class="promo-link">
|
||||
<i class="fas fa-rocket"></i>
|
||||
{% trans "Set-up now" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Main Configuration Card -->
|
||||
<div class="main-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
<i class="fas fa-cog"></i>
|
||||
{% trans "Configure Backup Destination" %}
|
||||
<span ng-hide="cyberpanelLoading" class="loading-spinner"></span>
|
||||
</h2>
|
||||
<p>{% trans "On this page you can set up your Backup destinations. (SFTP)" %}</p>
|
||||
</div>
|
||||
|
||||
<div ng-controller="backupDestinations" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Set up Backup Destinations." %} <img ng-hide="cyberpanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="alert alert-warning">
|
||||
<p>Configure automatic backups to our secure servers in 60 seconds. <a
|
||||
href="/backup/OneClickBackups">Set-up now.</a></p>
|
||||
<div class="card-body">
|
||||
<form action="/" method="post">
|
||||
<!-- Destination Type Selector -->
|
||||
<div class="destination-type-selector">
|
||||
<div class="destination-icon">
|
||||
<i class="fas fa-server"></i>
|
||||
</div>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
|
||||
<form action="/" class="form-horizontal bordered-row">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Type" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-change="fetchDetails()" ng-model="destinationType" class="form-control">
|
||||
<option>local</option>
|
||||
<option>SFTP</option>
|
||||
</select>
|
||||
<label class="form-label">
|
||||
<i class="fas fa-check-square" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Select Destination Type" %}
|
||||
</label>
|
||||
<div class="destination-tabs">
|
||||
<button type="button"
|
||||
class="tab-button"
|
||||
ng-class="{'active': destinationType === 'local'}"
|
||||
ng-click="destinationType = 'local'; fetchDetails()">
|
||||
<i class="fas fa-hdd" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Local Storage" %}
|
||||
</button>
|
||||
<button type="button"
|
||||
class="tab-button"
|
||||
ng-class="{'active': destinationType === 'SFTP'}"
|
||||
ng-click="destinationType = 'SFTP'; fetchDetails()">
|
||||
<i class="fas fa-network-wired" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "SFTP Server" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--- SFTP --->
|
||||
<!-- SFTP Configuration -->
|
||||
<div ng-hide="sftpHide" class="form-section">
|
||||
<h3 style="font-size: 1.1rem; font-weight: 600; color: #1e293b; margin-bottom: 1.5rem;">
|
||||
<i class="fas fa-network-wired" style="color: #5b5fcf; margin-right: 0.5rem;"></i>
|
||||
{% trans "SFTP Server Configuration" %}
|
||||
</h3>
|
||||
|
||||
<div ng-hide="sftpHide" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Name" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="name" required>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "Name" %}</label>
|
||||
<input type="text" class="form-control" ng-model="name" placeholder="My SFTP Backup" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "IP Address" %}</label>
|
||||
<input type="text" class="form-control" ng-model="IPAddress" placeholder="192.168.1.100" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="sftpHide" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "IP Address" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="IPAddress" required>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "Username" %}</label>
|
||||
<input type="text" class="form-control" ng-model="userName" placeholder="backup_user" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "Password" %}</label>
|
||||
<input type="password" class="form-control" ng-model="password" placeholder="••••••••" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="sftpHide" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Username" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="userName" required>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "SSH Port" %}</label>
|
||||
<input type="text" class="form-control" ng-model="backupSSHPort"
|
||||
placeholder="{% trans "Default: 22" %}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "Remote Path" %}</label>
|
||||
<input type="text" class="form-control" ng-model="path"
|
||||
placeholder="/home/backups" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="sftpHide" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Password" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input placeholder="" type="password" class="form-control" ng-model="password" required>
|
||||
<div style="text-align: center; margin-top: 2rem;">
|
||||
<button type="button" ng-click="addDestination('SFTP')" class="btn-primary">
|
||||
<i class="fas fa-plus-circle"></i>
|
||||
{% trans "Add SFTP Destination" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="sftpHide" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Port" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input placeholder="{% trans "Backup server SSH Port, leave empty for 22." %}"
|
||||
type="text" class="form-control" ng-model="backupSSHPort" required>
|
||||
<!-- Local Configuration -->
|
||||
<div ng-hide="localHide" class="form-section">
|
||||
<h3 style="font-size: 1.1rem; font-weight: 600; color: #1e293b; margin-bottom: 1.5rem;">
|
||||
<i class="fas fa-hdd" style="color: #5b5fcf; margin-right: 0.5rem;"></i>
|
||||
{% trans "Local Storage Configuration" %}
|
||||
</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "Name" %}</label>
|
||||
<input type="text" class="form-control" ng-model="name"
|
||||
placeholder="Local Backup Storage" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{% trans "Local Path" %}</label>
|
||||
<input type="text" class="form-control" ng-model="localPath"
|
||||
placeholder="/backup/local" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="sftpHide" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Path" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input placeholder="Path on remote server to store backups." type="text"
|
||||
class="form-control" ng-model="path" required>
|
||||
<div style="text-align: center; margin-top: 2rem;">
|
||||
<button type="button" ng-click="addDestination('local')" class="btn-primary">
|
||||
<i class="fas fa-plus-circle"></i>
|
||||
{% trans "Add Local Destination" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="sftpHide" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-click="addDestination('SFTP')"
|
||||
class="btn btn-primary btn-lg btn-block">{% trans "Add Destination" %}</button>
|
||||
|
||||
<!-- SFTP Destinations Table -->
|
||||
<div ng-hide="sftpHide" class="main-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
<i class="fas fa-list"></i>
|
||||
{% trans "Configured SFTP Destinations" %}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--- SFTP --->
|
||||
|
||||
|
||||
<!------ List of Destinations --------------->
|
||||
|
||||
<div ng-hide="sftpHide" class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table class="table">
|
||||
<div class="card-body" style="padding: 0;">
|
||||
<div class="table-responsive">
|
||||
<table class="destination-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "IP" %}</th>
|
||||
<th>{% trans "Server IP" %}</th>
|
||||
<th>{% trans "Username" %}</th>
|
||||
<th>{% trans "Path" %}</th>
|
||||
<th>{% trans "Port" %}</th>
|
||||
<th>{% trans "Delete" %}</th>
|
||||
<th style="text-align: center;">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records track by $index">
|
||||
<td ng-bind="record.name"></td>
|
||||
<td ng-bind="record.ip"></td>
|
||||
<td ng-bind="record.username"></td>
|
||||
<td ng-bind="record.path"></td>
|
||||
<td ng-bind="record.port"></td>
|
||||
<td ng-click="removeDestination('SFTP', record.name)"><img
|
||||
src="{% static 'images/delete.png' %}">
|
||||
<td>
|
||||
<strong ng-bind="record.name"></strong>
|
||||
</td>
|
||||
<td>
|
||||
<span class="info-badge" ng-bind="record.ip"></span>
|
||||
</td>
|
||||
<td>
|
||||
<i class="fas fa-user" style="color: #64748b; margin-right: 0.5rem;"></i>
|
||||
<span ng-bind="record.username"></span>
|
||||
</td>
|
||||
<td>
|
||||
<i class="fas fa-folder" style="color: #64748b; margin-right: 0.5rem;"></i>
|
||||
<span ng-bind="record.path"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="port-badge" ng-bind="record.port"></span>
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<button type="button"
|
||||
ng-click="removeDestination('SFTP', record.name)"
|
||||
class="btn-danger">
|
||||
<i class="fas fa-trash"></i>
|
||||
{% trans "Delete" %}
|
||||
</button>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!------ List of records --------------->
|
||||
|
||||
|
||||
<!--- SFTP End --->
|
||||
|
||||
<!--- AWS Start --->
|
||||
|
||||
<div ng-hide="localHide" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Name" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="name" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="localHide" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Local Path" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="localPath" required>
|
||||
<!-- Local Destinations Table -->
|
||||
<div ng-hide="localHide" class="main-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
<i class="fas fa-list"></i>
|
||||
{% trans "Configured Local Destinations" %}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="localHide" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-click="addDestination('local')"
|
||||
class="btn btn-primary btn-lg btn-block">{% trans "Add Destination" %}</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--- SFTP --->
|
||||
|
||||
|
||||
<!------ List of Destinations --------------->
|
||||
|
||||
<div ng-hide="localHide" class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table class="table">
|
||||
<div class="card-body" style="padding: 0;">
|
||||
<div class="table-responsive">
|
||||
<table class="destination-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Path" %}</th>
|
||||
<th>{% trans "Delete" %}</th>
|
||||
<th>{% trans "Storage Path" %}</th>
|
||||
<th style="text-align: center;">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records track by $index">
|
||||
<td ng-bind="record.name"></td>
|
||||
<td ng-bind="record.path"></td>
|
||||
<td ng-click="removeDestination('local', record.name)"><img
|
||||
src="{% static 'images/delete.png' %}">
|
||||
<td>
|
||||
<strong ng-bind="record.name"></strong>
|
||||
</td>
|
||||
<td>
|
||||
<i class="fas fa-folder-open" style="color: #5b5fcf; margin-right: 0.5rem;"></i>
|
||||
<span style="font-family: monospace; background: #f3f4f6; padding: 0.25rem 0.5rem; border-radius: 4px;"
|
||||
ng-bind="record.path"></span>
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<button type="button"
|
||||
ng-click="removeDestination('local', record.name)"
|
||||
class="btn-danger">
|
||||
<i class="fas fa-trash"></i>
|
||||
{% trans "Delete" %}
|
||||
</button>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!------ List of records --------------->
|
||||
|
||||
|
||||
<!--- AWS End --->
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Initialize destination type to ensure proper display
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
setTimeout(function() {
|
||||
var scope = angular.element(document.querySelector('[ng-controller="backupDestinations"]')).scope();
|
||||
if (scope && !scope.destinationType) {
|
||||
scope.$apply(function() {
|
||||
scope.destinationType = 'local';
|
||||
scope.fetchDetails();
|
||||
});
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
0
backup/templates/backup/backupLogs.html
Executable file → Normal file
0
backup/templates/backup/backupLogs.html
Executable file → Normal file
795
backup/templates/backup/backupSchedule.html
Executable file → Normal file
795
backup/templates/backup/backupSchedule.html
Executable file → Normal file
@@ -3,56 +3,457 @@
|
||||
{% block title %}{% trans "Schedule Backup - CyberPanel" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
{% load static %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
<style>
|
||||
.modern-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
.page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
padding: 3rem 0;
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
border-radius: 20px;
|
||||
animation: fadeInDown 0.5s ease-out;
|
||||
}
|
||||
|
||||
<div ng-controller="scheduleBackup" class="container">
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Schedule Backup" %} - <a target="_blank"
|
||||
href="https://cyberpanel.net/KnowledgeBase/home/schedule-backups-local-or-sftp/"
|
||||
style="height: 23px;line-height: 21px; text-decoration: underline"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>{% trans "Remote Backups" %}</span></a>
|
||||
</h2>
|
||||
<p>{% trans "On this page you can schedule Backups to localhost or remote server (If you have added one)" %}</p>
|
||||
.page-title {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 1.25rem;
|
||||
color: #64748b;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #5b5fcf;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #4547a9;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(91, 95, 207, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #fff;
|
||||
color: #5b5fcf;
|
||||
border: 1px solid #e8e9ff;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #f8f9ff;
|
||||
border-color: #5b5fcf;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(91, 95, 207, 0.2);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.825rem;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #dc2626;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
|
||||
}
|
||||
|
||||
.btn-gray {
|
||||
background: #6b7280;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1.5rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-gray:hover {
|
||||
background: #4b5563;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(107, 114, 128, 0.4);
|
||||
}
|
||||
|
||||
.main-card {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 10px 40px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
overflow: hidden;
|
||||
margin-bottom: 2rem;
|
||||
animation: fadeInUp 0.5s ease-out;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
padding: 1.5rem 2rem;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.875rem 1rem;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 10px;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s ease;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
box-shadow: 0 0 0 3px rgba(91, 95, 207, 0.1);
|
||||
}
|
||||
|
||||
.schedule-info-card {
|
||||
background: #f8f9ff;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.schedule-table {
|
||||
width: 100%;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.schedule-table thead {
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
}
|
||||
|
||||
.schedule-table th {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.schedule-table td {
|
||||
padding: 1rem;
|
||||
color: #64748b;
|
||||
font-size: 0.875rem;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
}
|
||||
|
||||
.schedule-table tbody tr:hover {
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
.schedule-table tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.frequency-badge {
|
||||
background: #e0e7ff;
|
||||
color: #5b5fcf;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid #e8e9ff;
|
||||
border-top-color: #5b5fcf;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
display: inline-block;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.job-selector {
|
||||
background: #f8f9ff;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
border: none;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
padding: 1.5rem 2rem;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.log-table {
|
||||
width: 100%;
|
||||
background: #f8f9ff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.log-table th {
|
||||
background: #e8e9ff;
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.log-table td {
|
||||
padding: 0.75rem;
|
||||
color: #64748b;
|
||||
font-size: 0.875rem;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.log-type {
|
||||
font-weight: 600;
|
||||
color: #5b5fcf;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes fadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.action-buttons button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="modern-container" ng-controller="scheduleBackup">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">
|
||||
<i class="fas fa-clock"></i>
|
||||
{% trans "Schedule Backup" %}
|
||||
</h1>
|
||||
<p class="page-subtitle">{% trans "Schedule automatic backups to localhost or remote servers" %}</p>
|
||||
<div class="header-actions">
|
||||
<a href="https://cyberpanel.net/KnowledgeBase/home/schedule-backups-local-or-sftp/"
|
||||
target="_blank"
|
||||
class="btn-secondary">
|
||||
<i class="fas fa-book"></i>
|
||||
{% trans "Remote Backups Guide" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Create New Backup Schedule" %} <img ng-hide="cyberPanelLoading"
|
||||
src="{% static 'images/loading.gif' %}" alt="cyberPanelLoading">
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
|
||||
<form action="/" class="form-horizontal bordered-row">
|
||||
|
||||
<!-- Create New Schedule Card -->
|
||||
<div class="main-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
<i class="fas fa-plus-circle"></i>
|
||||
{% trans "Create New Backup Schedule" %}
|
||||
<span ng-hide="cyberPanelLoading" class="loading-spinner"></span>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="/" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Destination" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-server" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Select Destination" %}
|
||||
</label>
|
||||
<select ng-model="selectedAccountAdd" class="form-control">
|
||||
<option value="">{% trans "Choose a destination..." %}</option>
|
||||
{% for items in destinations %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Name" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="name" required>
|
||||
<label class="form-label">
|
||||
<i class="fas fa-tag" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Schedule Name" %}
|
||||
</label>
|
||||
<input type="text" class="form-control" ng-model="name"
|
||||
placeholder="{% trans 'e.g., Daily Website Backup' %}" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Backup Frequency" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-sync-alt" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Backup Frequency" %}
|
||||
</label>
|
||||
<select ng-model="backupFrequency" class="form-control">
|
||||
<option>Never</option>
|
||||
<option>Daily</option>
|
||||
@@ -60,253 +461,239 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Backup Retention. Leave 0 for no limit" %}</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="number">
|
||||
<label>
|
||||
<input ng-model="backupRetention" type="number" value="0">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-history" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Backup Retention (0 = unlimited)" %}
|
||||
</label>
|
||||
<input ng-model="backupRetention" type="number" value="0"
|
||||
class="form-control" min="0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-click="addSchedule()"
|
||||
class="btn btn-primary btn-lg btn-block">{% trans "Add Schedule" %}</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; margin-top: 2rem;">
|
||||
<button type="button" ng-click="addSchedule()" class="btn-primary">
|
||||
<i class="fas fa-calendar-plus"></i>
|
||||
{% trans "Add Schedule" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Manage Existing Schedules Card -->
|
||||
<div class="main-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
<i class="fas fa-tasks"></i>
|
||||
{% trans "Manage Existing Backup Schedules" %}
|
||||
<span ng-hide="cyberPanelLoading" class="loading-spinner"></span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Manage Existing Backup Schedules" %} <img ng-hide="cyberPanelLoading"
|
||||
src="{% static 'images/loading.gif' %}" alt="cyberPanelLoading">
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
|
||||
<form action="/" class="form-horizontal bordered-row">
|
||||
|
||||
<div class="card-body">
|
||||
<!-- Destination and Job Selector -->
|
||||
<div class="job-selector">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Destination" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-server" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Select Destination" %}
|
||||
</label>
|
||||
<select ng-change="fetchJobs()" ng-model="selectedAccount" class="form-control">
|
||||
<option value="">{% trans "Choose a destination..." %}</option>
|
||||
{% for items in destinations %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="jobsHidden" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Job" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<div class="col-md-6" ng-hide="jobsHidden">
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-briefcase" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Select Job" %}
|
||||
</label>
|
||||
<div style="display: flex; gap: 1rem;">
|
||||
<select ng-change="fetchWebsites()" ng-model="selectedJob" class="form-control">
|
||||
<option value="">{% trans "Choose a job..." %}</option>
|
||||
<option ng-repeat="job in jobs track by $index">{$ job $}</option>
|
||||
</select>
|
||||
<button ng-hide="driveHidden" type="button" ng-click="deleteAccount()" class="btn-danger">
|
||||
<i class="fas fa-trash"></i>
|
||||
{% trans "Delete" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button ng-hide="driveHidden" type="button" ng-click="deleteAccount()"
|
||||
class="btn btn-danger">{% trans "Delete" %}</button>
|
||||
</div>
|
||||
|
||||
<div ng-hide="driveHidden" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Add Sites for Backup" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-model="selectedWebsite" class="form-control">
|
||||
{% for items in websites %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<button type="button" ng-click="addSite('one')"
|
||||
class="btn btn-primary">{% trans "Add Site" %}</button>
|
||||
<button type="button" ng-click="addSite('all')"
|
||||
class="btn btn-primary">{% trans "Add All" %}</button>
|
||||
</div>
|
||||
|
||||
<div ng-hide="driveHidden" class="form-group">
|
||||
<div style="border: 1px solid green;" class="col-sm-12">
|
||||
<table style="margin-top: 2%" class="table">
|
||||
<!-- Schedule Info -->
|
||||
<div ng-hide="driveHidden" class="schedule-info-card">
|
||||
<table class="schedule-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Last Run</th>
|
||||
<th>All Sites</th>
|
||||
<th>Frequency ({$ currently $})</th>
|
||||
<th>Retention ({$ currently $})</th>
|
||||
<th>Current Status</th>
|
||||
<th>{% trans "Last Run" %}</th>
|
||||
<th>{% trans "All Sites" %}</th>
|
||||
<th>{% trans "Frequency" %} <span style="font-weight: normal;">({$ currently $})</span></th>
|
||||
<th>{% trans "Retention" %} <span style="font-weight: normal;">({$ currently $})</span></th>
|
||||
<th>{% trans "Current Status" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{$ lastRun $}</td>
|
||||
<td>{$ allSites $}</td>
|
||||
<td><i class="fas fa-calendar-check" style="color: #5b5fcf; margin-right: 0.5rem;"></i>{$ lastRun $}</td>
|
||||
<td><span class="frequency-badge">{$ allSites $}</span></td>
|
||||
<td>
|
||||
<select ng-change="changeFrequency()" ng-model="backupFrequency"
|
||||
class="form-control">
|
||||
class="form-control" style="width: auto; min-width: 120px;">
|
||||
<option>Never</option>
|
||||
<option>Daily</option>
|
||||
<option>Weekly</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>{$ currentStatus $}</td>
|
||||
|
||||
<td>
|
||||
<input ng-model="backupRetention" ng-change="changeFrequency()"
|
||||
type="number" min="0"
|
||||
class="form-control" style="width: 100px;">
|
||||
</td>
|
||||
<td><span class="status-badge">{$ currentStatus $}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid green;" ng-hide="driveHidden" class="form-group">
|
||||
<div class="row">
|
||||
<div style="margin-left: 2%" class="col-sm-3">
|
||||
<button data-toggle="modal" data-target="#backupLogs" ng-hide="driveHidden"
|
||||
type="button" ng-click="fetchLogs()"
|
||||
class="btn btn-gray">{% trans "View Logs" %}</button>
|
||||
<div id="backupLogs" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×
|
||||
<div style="margin-top: 1.5rem;">
|
||||
<button data-toggle="modal" data-target="#backupLogs"
|
||||
type="button" ng-click="fetchLogs()" class="btn-gray">
|
||||
<i class="fas fa-file-alt"></i>
|
||||
{% trans "View Logs" %}
|
||||
</button>
|
||||
<h4 class="modal-title">{% trans "Backup logs" %} <img
|
||||
ng-hide="cyberPanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-9">
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<!-- Site Management -->
|
||||
<div ng-hide="driveHidden">
|
||||
<div class="form-section">
|
||||
<div class="form-group">
|
||||
<select ng-model="recordsToShowLogs"
|
||||
ng-change="fetchLogs()"
|
||||
class="form-control" id="example-select">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-globe" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Add Sites for Backup" %}
|
||||
</label>
|
||||
<div style="display: flex; gap: 1rem; align-items: center;">
|
||||
<select ng-model="selectedWebsite" class="form-control" style="flex: 1;">
|
||||
<option value="">{% trans "Choose a website..." %}</option>
|
||||
{% for items in websites %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="action-buttons" style="flex: 0;">
|
||||
<button type="button" ng-click="addSite('one')" class="btn-primary">
|
||||
<i class="fas fa-plus"></i>
|
||||
{% trans "Add Site" %}
|
||||
</button>
|
||||
<button type="button" ng-click="addSite('all')" class="btn-primary">
|
||||
<i class="fas fa-plus-circle"></i>
|
||||
{% trans "Add All" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sites Table -->
|
||||
<div style="display: flex; justify-content: flex-end; margin-bottom: 1rem;">
|
||||
<select ng-model="recordsToShow" ng-change="fetchWebsites()"
|
||||
class="form-control" style="width: auto;">
|
||||
<option>10</option>
|
||||
<option>50</option>
|
||||
<option>100</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table style="margin: 0px; padding-bottom: 2%" class="table">
|
||||
<table class="schedule-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="log in logs track by $index">
|
||||
<td ng-bind="log.type"></td>
|
||||
<td ng-bind="log.message"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="margin-top: 2%" class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<select ng-model="currentPageLogs"
|
||||
class="form-control"
|
||||
ng-change="fetchLogs()">
|
||||
<option ng-repeat="page in paginationLogs">
|
||||
{$
|
||||
$index + 1
|
||||
$}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- end row -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<select ng-model="recordsToShow"
|
||||
ng-change="fetchWebsites()"
|
||||
class="form-control" id="example-select">
|
||||
<option>10</option>
|
||||
<option>50</option>
|
||||
<option>100</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table style="margin-top: 2%" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Sites</th>
|
||||
<th>Action</th>
|
||||
<th>{% trans "Website" %}</th>
|
||||
<th style="text-align: center;">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="website in websites track by $index">
|
||||
<td ng-bind="website.name"></td>
|
||||
<td>
|
||||
<button type="button" ng-click="deleteSite(website.name)"
|
||||
class="btn btn-danger">{% trans "Delete" %}</button>
|
||||
<i class="fas fa-globe" style="color: #5b5fcf; margin-right: 0.5rem;"></i>
|
||||
<strong ng-bind="website.name"></strong>
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<button type="button" ng-click="deleteSite(website.name)" class="btn-danger">
|
||||
<i class="fas fa-times"></i>
|
||||
{% trans "Remove" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="margin-top: 2%" class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<!-- Pagination -->
|
||||
<div style="display: flex; justify-content: center; margin-top: 2rem;">
|
||||
<select ng-model="currentPage" class="form-control"
|
||||
ng-change="fetchWebsites()">
|
||||
<option ng-repeat="page in pagination">{$ $index + 1
|
||||
$}
|
||||
</option>
|
||||
ng-change="fetchWebsites()" style="width: auto;">
|
||||
<option ng-repeat="page in pagination">{$ $index + 1 $}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- end row -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
<!-- Logs Modal -->
|
||||
<div id="backupLogs" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">
|
||||
<i class="fas fa-file-alt" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Backup Logs" %}
|
||||
<span ng-hide="cyberPanelLoading" class="loading-spinner" style="width: 16px; height: 16px;"></span>
|
||||
</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" style="font-size: 1.5rem;">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div style="display: flex; justify-content: flex-end; margin-bottom: 1rem;">
|
||||
<select ng-model="recordsToShowLogs" ng-change="fetchLogs()"
|
||||
class="form-control" style="width: auto;">
|
||||
<option>10</option>
|
||||
<option>50</option>
|
||||
<option>100</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<table class="log-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 100px;">{% trans "Type" %}</th>
|
||||
<th>{% trans "Message" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="log in logs track by $index">
|
||||
<td class="log-type" ng-bind="log.type"></td>
|
||||
<td ng-bind="log.message"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="display: flex; justify-content: center; margin-top: 2rem;">
|
||||
<select ng-model="currentPageLogs" class="form-control"
|
||||
ng-change="fetchLogs()" style="width: auto;">
|
||||
<option ng-repeat="page in paginationLogs">{$ $index + 1 $}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
814
backup/templates/backup/googleDrive.html
Executable file → Normal file
814
backup/templates/backup/googleDrive.html
Executable file → Normal file
@@ -3,261 +3,687 @@
|
||||
{% block title %}{% trans "Google Drive Backups - CyberPanel" %}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
{% load static %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
<style>
|
||||
.modern-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
.page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
padding: 3rem 0;
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
border-radius: 20px;
|
||||
animation: fadeInDown 0.5s ease-out;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
<div class="container">
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Set up Google Drive Backups" %} - <a target="_blank"
|
||||
href="https://cyberpanel.net/docs/backup-to-google-drive/"
|
||||
style="height: 23px;line-height: 21px;"
|
||||
class="btn btn-border btn-alt border-red btn-link font-red"
|
||||
title=""><span>{% trans "Remote Backups" %}</span></a>
|
||||
.page-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: url('data:image/svg+xml;utf8,<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h100v100H0z" fill="none"/><path d="M0 50h100M50 0v100" stroke="%23e8e9ff" stroke-width="0.5"/></svg>') repeat;
|
||||
transform: rotate(45deg);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.google-drive-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 1.25rem;
|
||||
color: #64748b;
|
||||
margin-bottom: 1.5rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #5b5fcf;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #4547a9;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(91, 95, 207, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #fff;
|
||||
color: #5b5fcf;
|
||||
border: 1px solid #e8e9ff;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #f8f9ff;
|
||||
border-color: #5b5fcf;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(91, 95, 207, 0.2);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.825rem;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #dc2626;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
|
||||
}
|
||||
|
||||
.btn-gray {
|
||||
background: #6b7280;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1.5rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-gray:hover {
|
||||
background: #4b5563;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(107, 114, 128, 0.4);
|
||||
}
|
||||
|
||||
.main-card {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 10px 40px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
overflow: hidden;
|
||||
margin-bottom: 2rem;
|
||||
animation: fadeInUp 0.5s ease-out;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
padding: 1.5rem 2rem;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.account-selector {
|
||||
background: #f8f9ff;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.875rem 1rem;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 10px;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s ease;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
box-shadow: 0 0 0 3px rgba(91, 95, 207, 0.1);
|
||||
}
|
||||
|
||||
.gdrive-table {
|
||||
width: 100%;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.gdrive-table thead {
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
}
|
||||
|
||||
.gdrive-table th {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.gdrive-table td {
|
||||
padding: 1rem;
|
||||
color: #64748b;
|
||||
font-size: 0.875rem;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
}
|
||||
|
||||
.gdrive-table tbody tr:hover {
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
.gdrive-table tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.frequency-badge {
|
||||
background: #e0e7ff;
|
||||
color: #5b5fcf;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.retention-info {
|
||||
background: #fef3c7;
|
||||
border: 1px solid #fde68a;
|
||||
border-radius: 12px;
|
||||
padding: 1rem 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.retention-info i {
|
||||
color: #f59e0b;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.retention-info p {
|
||||
margin: 0;
|
||||
color: #92400e;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.retention-info a {
|
||||
color: #5b5fcf;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.retention-info a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid #e8e9ff;
|
||||
border-top-color: #5b5fcf;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
display: inline-block;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
border: none;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
padding: 1.5rem 2rem;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
border-top: 1px solid #e8e9ff;
|
||||
padding: 1.5rem 2rem;
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
.log-table {
|
||||
width: 100%;
|
||||
background: #f8f9ff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.log-table th {
|
||||
background: #e8e9ff;
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.log-table td {
|
||||
padding: 0.75rem;
|
||||
color: #64748b;
|
||||
font-size: 0.875rem;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.log-type {
|
||||
font-weight: 600;
|
||||
color: #5b5fcf;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes fadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.google-drive-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="modern-container" ng-controller="googleDrive">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">
|
||||
<div class="google-drive-icon">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/1/12/Google_Drive_icon_%282020%29.svg"
|
||||
alt="Google Drive" style="width: 36px; height: 36px;">
|
||||
</div>
|
||||
{% trans "Google Drive Backups" %}
|
||||
</h1>
|
||||
<p class="page-subtitle">{% trans "Set up and manage automatic backups to Google Drive" %}</p>
|
||||
<div class="header-actions">
|
||||
<a href="https://cyberpanel.net/docs/backup-to-google-drive/"
|
||||
target="_blank"
|
||||
class="btn-secondary">
|
||||
<i class="fas fa-book"></i>
|
||||
{% trans "Documentation" %}
|
||||
</a>
|
||||
<button type="button" class="btn-primary" data-toggle="modal" data-target="#setupGdrive">
|
||||
<i class="fas fa-plus-circle"></i>
|
||||
{% trans "Setup New Account" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Configuration Card -->
|
||||
<div class="main-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
<i class="fas fa-cog"></i>
|
||||
{% trans "Google Drive Backup Configuration" %}
|
||||
<span ng-hide="cyberPanelLoading" class="loading-spinner"></span>
|
||||
</h2>
|
||||
<p>{% trans "On this page you can set up and manage Google Drive Backups." %}</p>
|
||||
</div>
|
||||
|
||||
<div ng-controller="googleDrive" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "On this page you can set up and manage Google Drive Backups." %} <img
|
||||
ng-hide="cyberPanelLoading" src="{% static 'images/loading.gif' %}"> <a
|
||||
class="btn btn-border btn-alt border-blue-alt btn-link font-blue-alt"
|
||||
href="#"
|
||||
title="" data-toggle="modal"
|
||||
data-target="#setupGdrive"><span>{% trans "Setup new Account" %}</span></a>
|
||||
<div id="setupGdrive" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×
|
||||
</button>
|
||||
<h4 class="modal-title">{% trans "Set up account" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<form name="containerSettingsForm" action="/" class="form-horizontal">
|
||||
|
||||
<div ng-hide="installationDetailsForm" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Account Name" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="accountName" type="text" class="form-control"
|
||||
ng-model="accountName">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary"
|
||||
ng-click="setupAccount()">Save <img
|
||||
ng-hide="cyberPanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</button>
|
||||
<button type="button" ng-disabled="savingSettings"
|
||||
class="btn btn-default" data-dismiss="modal">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
|
||||
|
||||
<form action="/" class="form-horizontal bordered-row">
|
||||
|
||||
<div class="card-body">
|
||||
<!-- Account Selector -->
|
||||
<div class="account-selector">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Drive Account" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-user-circle" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Select Google Drive Account" %}
|
||||
</label>
|
||||
<div style="display: flex; gap: 1rem; align-items: center;">
|
||||
<select ng-change="fetchWebsites()" ng-model="selectedAccount" class="form-control">
|
||||
<option value="">{% trans "Choose an account..." %}</option>
|
||||
{% for items in accounts %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button ng-hide="driveHidden" type="button" ng-click="deleteAccount()" class="btn-danger">
|
||||
<i class="fas fa-trash"></i>
|
||||
{% trans "Delete" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button ng-hide="driveHidden" type="button" ng-click="deleteAccount()"
|
||||
class="btn btn-danger">{% trans "Delete" %}</button>
|
||||
</div>
|
||||
|
||||
<div ng-hide="driveHidden" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Backup Frequency" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<!-- Backup Settings -->
|
||||
<div ng-hide="driveHidden">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-sync-alt" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Backup Frequency" %}
|
||||
</label>
|
||||
<select ng-change="changeFrequency()" ng-model="backupFrequency" class="form-control">
|
||||
<option>Never</option>
|
||||
<option>Daily</option>
|
||||
<option>Weekly</option>
|
||||
</select>
|
||||
<small class="form-text text-muted" style="margin-top: 0.5rem;">
|
||||
{% trans "Currently" %}: <span class="frequency-badge">{$ currently $}</span>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-history" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Backup File Retention" %}
|
||||
</label>
|
||||
<select id="fileretention" ng-model="Retentiontime" ng-change="changeRetention()" class="form-control">
|
||||
<option value="1d">1 day</option>
|
||||
<option value="1w">1 week</option>
|
||||
<option value="1m">1 month</option>
|
||||
<option value="6m">6 months</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
Currently: {$ currently $}
|
||||
</div>
|
||||
|
||||
<div ng-hide="driveHidden" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Add Sites for Backup" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<!-- Retention Info -->
|
||||
<div class="retention-info">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<p>
|
||||
{% trans "Backup retention is a" %}
|
||||
<a href="https://cyberpanel.net/cyberpanel-addons" target="_blank">{% trans "paid feature" %}</a>.
|
||||
{% trans "Upgrade to manage how long backups are stored." %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Site Management -->
|
||||
<div class="form-section">
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-globe" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Add Sites for Backup" %}
|
||||
</label>
|
||||
<div style="display: flex; gap: 1rem; align-items: center;">
|
||||
<select ng-model="selectedWebsite" class="form-control">
|
||||
<option value="">{% trans "Choose a website..." %}</option>
|
||||
{% for items in websites %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<button type="button" ng-click="addSite()"
|
||||
class="btn btn-primary">{% trans "Add Site" %}</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-hide="driveHidden" id="checkret" class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Backup File Retention" %} (<a href="https://cyberpanel.net/cyberpanel-addons">Paid Feature</a> )</label>
|
||||
<div class="col-sm-6">
|
||||
<select id="fileretention" ng-model="Retentiontime" ng-change="changeRetention()" class="form-control">
|
||||
<option value="1d">1 days</option>
|
||||
<option value="1w"> 1 week</option>
|
||||
<option value="1m"> 1 month</option>
|
||||
<option value="6m"> 6 months</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="driveHidden" class="form-group">
|
||||
<div class="row">
|
||||
<div style="margin-left: 2%" class="col-sm-3">
|
||||
<button data-toggle="modal" data-target="#backupLogs" ng-hide="driveHidden"
|
||||
type="button" ng-click="fetchLogs()"
|
||||
class="btn btn-gray">{% trans "View Logs" %}</button>
|
||||
<div id="backupLogs" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×
|
||||
<button type="button" ng-click="addSite()" class="btn-primary">
|
||||
<i class="fas fa-plus"></i>
|
||||
{% trans "Add Site" %}
|
||||
</button>
|
||||
<h4 class="modal-title">{% trans "Git Logs" %} <img
|
||||
ng-hide="cyberPanelLoading"
|
||||
src="{% static 'images/loading.gif' %}">
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-9">
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<select ng-model="recordsToShowLogs"
|
||||
ng-change="fetchLogs()"
|
||||
class="form-control" id="example-select">
|
||||
<!-- Action Buttons -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
|
||||
<button data-toggle="modal" data-target="#backupLogs"
|
||||
type="button" ng-click="fetchLogs()" class="btn-gray">
|
||||
<i class="fas fa-file-alt"></i>
|
||||
{% trans "View Logs" %}
|
||||
</button>
|
||||
<select ng-model="recordsToShow" ng-change="fetchWebsites()"
|
||||
class="form-control" style="width: auto;">
|
||||
<option>10</option>
|
||||
<option>50</option>
|
||||
<option>100</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table style="margin: 0px; padding-bottom: 2%" class="table">
|
||||
<!-- Sites Table -->
|
||||
<table class="gdrive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="log in logs track by $index">
|
||||
<td ng-bind="log.type"></td>
|
||||
<td ng-bind="log.message"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="margin-top: 2%" class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<select ng-model="currentPageLogs"
|
||||
class="form-control"
|
||||
ng-change="fetchLogs()">
|
||||
<option ng-repeat="page in paginationLogs">{$ $index + 1 $}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- end row -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<select ng-model="recordsToShow"
|
||||
ng-change="fetchWebsites()"
|
||||
class="form-control" id="example-select">
|
||||
<option>10</option>
|
||||
<option>50</option>
|
||||
<option>100</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table style="margin-top: 2%" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Sites</th>
|
||||
<th>Action</th>
|
||||
<th>{% trans "Website" %}</th>
|
||||
<th style="text-align: center;">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="website in websites track by $index">
|
||||
<td ng-bind="website.name"></td>
|
||||
<td>
|
||||
<button type="button" ng-click="deleteSite(website.name)"
|
||||
class="btn btn-danger">{% trans "Delete" %}</button>
|
||||
<i class="fas fa-globe" style="color: #5b5fcf; margin-right: 0.5rem;"></i>
|
||||
<strong ng-bind="website.name"></strong>
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<button type="button" ng-click="deleteSite(website.name)" class="btn-danger">
|
||||
<i class="fas fa-times"></i>
|
||||
{% trans "Remove" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="margin-top: 2%" class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<!-- Pagination -->
|
||||
<div style="display: flex; justify-content: center; margin-top: 2rem;">
|
||||
<select ng-model="currentPage" class="form-control"
|
||||
ng-change="fetchWebsites()">
|
||||
<option ng-repeat="page in pagination">{$ $index + 1$}</option>
|
||||
ng-change="fetchWebsites()" style="width: auto;">
|
||||
<option ng-repeat="page in pagination">{$ $index + 1 $}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- end row -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Setup Account Modal -->
|
||||
<div id="setupGdrive" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">
|
||||
<i class="fas fa-user-plus" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Set up Google Drive Account" %}
|
||||
</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" style="font-size: 1.5rem;">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form name="containerSettingsForm" action="/" method="post">
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-tag" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Account Name" %}
|
||||
</label>
|
||||
<input name="accountName" type="text" class="form-control"
|
||||
ng-model="accountName"
|
||||
placeholder="{% trans 'e.g., My Google Drive Backup' %}" required>
|
||||
<small class="form-text text-muted">
|
||||
{% trans "Choose a descriptive name for this Google Drive account" %}
|
||||
</small>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn-secondary" data-dismiss="modal">
|
||||
<i class="fas fa-times"></i>
|
||||
{% trans "Cancel" %}
|
||||
</button>
|
||||
<button type="button" class="btn-primary" ng-click="setupAccount()">
|
||||
<i class="fas fa-check"></i>
|
||||
{% trans "Continue Setup" %}
|
||||
<span ng-hide="cyberPanelLoading" class="loading-spinner" style="width: 16px; height: 16px;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Logs Modal -->
|
||||
<div id="backupLogs" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">
|
||||
<i class="fas fa-file-alt" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Backup Logs" %}
|
||||
<span ng-hide="cyberPanelLoading" class="loading-spinner" style="width: 16px; height: 16px;"></span>
|
||||
</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" style="font-size: 1.5rem;">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div style="display: flex; justify-content: flex-end; margin-bottom: 1rem;">
|
||||
<select ng-model="recordsToShowLogs" ng-change="fetchLogs()"
|
||||
class="form-control" style="width: auto;">
|
||||
<option>10</option>
|
||||
<option>50</option>
|
||||
<option>100</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<table class="log-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 100px;">{% trans "Type" %}</th>
|
||||
<th>{% trans "Message" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="log in logs track by $index">
|
||||
<td class="log-type" ng-bind="log.type"></td>
|
||||
<td ng-bind="log.message"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="display: flex; justify-content: center; margin-top: 2rem;">
|
||||
<select ng-model="currentPageLogs" class="form-control"
|
||||
ng-change="fetchLogs()" style="width: auto;">
|
||||
<option ng-repeat="page in paginationLogs">{$ $index + 1 $}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
0
backup/templates/backup/index.html
Executable file → Normal file
0
backup/templates/backup/index.html
Executable file → Normal file
1038
backup/templates/backup/oneClickBackups.html
Executable file → Normal file
1038
backup/templates/backup/oneClickBackups.html
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
717
backup/templates/backup/remoteBackups.html
Executable file → Normal file
717
backup/templates/backup/remoteBackups.html
Executable file → Normal file
@@ -4,149 +4,690 @@
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
<div class="container">
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Remote Backups" %} - <a target="_blank" href="http://go.cyberpanel.net/remote-transfer" style="height: 23px;line-height: 21px;" class="btn btn-border btn-alt border-red btn-link font-red" title=""><span>{% trans "Remote Transfer" %}</span></a></h2>
|
||||
<p>{% trans "This feature can import website(s) from remote server" %}</p>
|
||||
</div>
|
||||
<style>
|
||||
.modern-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
<div ng-controller="remoteBackupControl" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Remote Backups" %} <img ng-hide="backupLoading" src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
.page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
padding: 3rem 0;
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
border-radius: 20px;
|
||||
animation: fadeInDown 0.5s ease-out;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
<p ng-bind="transferStatus"></p>
|
||||
<form action="/" class="form-horizontal bordered-row">
|
||||
.page-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: radial-gradient(circle at 30% 70%, rgba(91, 95, 207, 0.1) 0%, transparent 50%);
|
||||
animation: float 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 1.25rem;
|
||||
color: #64748b;
|
||||
margin-bottom: 1.5rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #5b5fcf;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #4547a9;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(91, 95, 207, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
background: #cbd5e1;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #fff;
|
||||
color: #5b5fcf;
|
||||
border: 1px solid #e8e9ff;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #f8f9ff;
|
||||
border-color: #5b5fcf;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(91, 95, 207, 0.2);
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: #10b981;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
background: #059669;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(16, 185, 129, 0.4);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #dc2626;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(239, 68, 68, 0.4);
|
||||
}
|
||||
|
||||
.main-card {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 10px 40px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
overflow: hidden;
|
||||
margin-bottom: 2rem;
|
||||
animation: fadeInUp 0.5s ease-out;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
padding: 1.5rem 2rem;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.875rem 1rem;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 10px;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s ease;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
box-shadow: 0 0 0 3px rgba(91, 95, 207, 0.1);
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
animation: slideInRight 0.3s ease-out;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
border: 1px solid #a7f3d0;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
|
||||
.alert p {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.remote-table {
|
||||
width: 100%;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e9ff;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.remote-table thead {
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
}
|
||||
|
||||
.remote-table th {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.remote-table td {
|
||||
padding: 1rem;
|
||||
color: #64748b;
|
||||
font-size: 0.875rem;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
}
|
||||
|
||||
.remote-table tbody tr:hover {
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
.remote-table tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.checkbox-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.checkbox-wrapper input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
accent-color: #5b5fcf;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: relative;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.search-box i {
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding: 0.875rem 1rem 0.875rem 3rem;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 10px;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s ease;
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
box-shadow: 0 0 0 3px rgba(91, 95, 207, 0.1);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.terminal-section {
|
||||
background: #1e293b;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
margin-top: 2rem;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.terminal-header {
|
||||
background: #334155;
|
||||
padding: 0.75rem 1rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
color: #e2e8f0;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.terminal-dots {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.terminal-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.terminal-dot.red {
|
||||
background: #ef4444;
|
||||
}
|
||||
|
||||
.terminal-dot.yellow {
|
||||
background: #f59e0b;
|
||||
}
|
||||
|
||||
.terminal-dot.green {
|
||||
background: #10b981;
|
||||
}
|
||||
|
||||
.terminal-body {
|
||||
padding: 1.5rem;
|
||||
height: 350px;
|
||||
overflow-y: auto;
|
||||
font-family: 'Monaco', 'Consolas', monospace;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.6;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.terminal-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid #e8e9ff;
|
||||
border-top-color: #5b5fcf;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
display: inline-block;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.php-badge {
|
||||
background: #e0e7ff;
|
||||
color: #5b5fcf;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.package-badge {
|
||||
background: #f3f4f6;
|
||||
color: #1e293b;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes fadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translate(0, 0) rotate(0deg); }
|
||||
33% { transform: translate(30px, -30px) rotate(120deg); }
|
||||
66% { transform: translate(-20px, 20px) rotate(240deg); }
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.terminal-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.action-buttons button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="modern-container" ng-controller="remoteBackupControl">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">
|
||||
<i class="fas fa-server"></i>
|
||||
{% trans "Remote Backups" %}
|
||||
</h1>
|
||||
<p class="page-subtitle">{% trans "Import websites from remote CyberPanel servers via SSH" %}</p>
|
||||
<div class="header-actions">
|
||||
<a href="https://cyberpanel.net/docs/transfer-website-from-another-server-using-remote-transfer/"
|
||||
target="_blank"
|
||||
class="btn-secondary">
|
||||
<i class="fas fa-book"></i>
|
||||
{% trans "Remote Transfer Guide" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Connection Card -->
|
||||
<div class="main-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
<i class="fas fa-network-wired"></i>
|
||||
{% trans "Remote Server Connection" %}
|
||||
<span ng-hide="backupLoading" class="loading-spinner"></span>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="/" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "IP Address" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" ng-model="IPAddress" required>
|
||||
<label class="form-label">
|
||||
<i class="fas fa-map-marker-alt" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Remote Server IP Address" %}
|
||||
</label>
|
||||
<input type="text" class="form-control" ng-model="IPAddress"
|
||||
placeholder="192.168.1.100" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Password" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input ng-change="passwordEnter()" type="password" class="form-control" ng-model="password" required>
|
||||
<label class="form-label">
|
||||
<i class="fas fa-key" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Root Password" %}
|
||||
</label>
|
||||
<input ng-change="passwordEnter()" type="password" class="form-control"
|
||||
ng-model="password" placeholder="••••••••" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-hide="backupButton" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-disabled="fetchAccountsBtn" ng-click="fetchAccountsFromRemoteServer()" class="btn btn-primary btn-lg btn-block">{% trans "Fetch Accounts" %}</button>
|
||||
<div ng-hide="backupButton" class="action-buttons">
|
||||
<button type="button" ng-disabled="fetchAccountsBtn"
|
||||
ng-click="fetchAccountsFromRemoteServer()" class="btn-primary">
|
||||
<i class="fas fa-cloud-download-alt"></i>
|
||||
{% trans "Fetch Accounts" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-hide="transferBoxBtn" class="form-group">
|
||||
|
||||
<label class="col-sm-1 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-disabled="startTransferbtn" ng-click="startTransfer()" class="btn btn-primary btn-lg btn-block">{% trans "Start Transfer" %}</button>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-disabled="stopTransferbtn" ng-click="cancelRemoteBackup()" class="btn btn-primary btn-lg btn-block">{% trans "Cancel" %}</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-hide="notificationsBox" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<!-- Notifications -->
|
||||
<div ng-hide="notificationsBox">
|
||||
<div ng-hide="errorMessage" class="alert alert-danger">
|
||||
<i class="fas fa-exclamation-circle"></i>
|
||||
<p>{$ error_message $}</p>
|
||||
</div>
|
||||
<div ng-hide="couldNotConnect" class="alert alert-danger">
|
||||
<i class="fas fa-times-circle"></i>
|
||||
<p>{% trans "Could not connect, please refresh this page." %}</p>
|
||||
</div>
|
||||
|
||||
<div ng-hide="accountsFetched" class="alert alert-success">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
<p>{% trans "Accounts Successfully Fetched from remote server." %}</p>
|
||||
</div>
|
||||
|
||||
<div ng-hide="backupProcessStarted" class="alert alert-success">
|
||||
<i class="fas fa-play-circle"></i>
|
||||
<p>{% trans "Backup Process successfully started." %}</p>
|
||||
</div>
|
||||
|
||||
<div ng-hide="backupCancelled" class="alert alert-success">
|
||||
<i class="fas fa-stop-circle"></i>
|
||||
<p>{% trans "Backup successfully cancelled." %}</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!------ List of Accounts in remote server --------------->
|
||||
|
||||
<div ng-hide="accountsInRemoteServerTable" class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
<input type="text" ng-model="accountsSearch" placeholder="{% trans 'Search Accounts..' %}" class="form-control autocomplete-input">
|
||||
<!-- Accounts Table -->
|
||||
<div ng-hide="accountsInRemoteServerTable" class="main-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
<i class="fas fa-list"></i>
|
||||
{% trans "Available Websites on Remote Server" %}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="search-box">
|
||||
<i class="fas fa-search"></i>
|
||||
<input type="text" ng-model="accountsSearch"
|
||||
placeholder="{% trans 'Search websites...' %}"
|
||||
class="search-input">
|
||||
</div>
|
||||
|
||||
<div ng-hide="accountsInRemoteServerTable" class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table class="table">
|
||||
<div class="table-responsive">
|
||||
<table class="remote-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Website" %}</th>
|
||||
<th>{% trans "PHP" %}</th>
|
||||
<th>{% trans "PHP Version" %}</th>
|
||||
<th>{% trans "Package" %}</th>
|
||||
<th>{% trans "Email" %}</th>
|
||||
<th><input ng-model="webSiteStatus" ng-change="allChecked(webSiteStatus)" type="checkbox" value=""></th>
|
||||
<th style="text-align: center;">
|
||||
<div class="checkbox-wrapper">
|
||||
<input ng-model="webSiteStatus"
|
||||
ng-change="allChecked(webSiteStatus)"
|
||||
type="checkbox" value="">
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in records | filter:accountsSearch">
|
||||
<td ng-bind="record.website"></td>
|
||||
<td ng-bind="record.php"></td>
|
||||
<td ng-bind="record.package"></td>
|
||||
<td ng-bind="record.email"></td>
|
||||
<td ng-click=""><input ng-model="webSiteStatus" ng-change="addRemoveWebsite(record.website,webSiteStatus)" type="checkbox" value=""></td>
|
||||
<td>
|
||||
<i class="fas fa-globe" style="color: #5b5fcf; margin-right: 0.5rem;"></i>
|
||||
<strong ng-bind="record.website"></strong>
|
||||
</td>
|
||||
<td>
|
||||
<span class="php-badge" ng-bind="record.php"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="package-badge" ng-bind="record.package"></span>
|
||||
</td>
|
||||
<td>
|
||||
<i class="fas fa-envelope" style="color: #94a3b8; margin-right: 0.5rem;"></i>
|
||||
<span ng-bind="record.email"></span>
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<div class="checkbox-wrapper">
|
||||
<input ng-model="webSiteStatus"
|
||||
ng-change="addRemoveWebsite(record.website,webSiteStatus)"
|
||||
type="checkbox" value="">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div ng-hide="transferBoxBtn" class="action-buttons">
|
||||
<button type="button" ng-disabled="startTransferbtn"
|
||||
ng-click="startTransfer()" class="btn-success">
|
||||
<i class="fas fa-exchange-alt"></i>
|
||||
{% trans "Start Transfer" %}
|
||||
</button>
|
||||
<button type="button" ng-disabled="stopTransferbtn"
|
||||
ng-click="cancelRemoteBackup()" class="btn-danger">
|
||||
<i class="fas fa-times"></i>
|
||||
{% trans "Cancel Transfer" %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!------ List of Accounts in remote server --------------->
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<div ng-hide="backupStatus" class="form-group">
|
||||
<div class="col-sm-6">
|
||||
<textarea ng-model="requestData" rows="15" class="form-control"></textarea>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<textarea ng-model="restoreData" rows="15" class="form-control"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Transfer Progress Terminal -->
|
||||
<div ng-hide="backupStatus" class="terminal-section">
|
||||
<div class="terminal-header">
|
||||
<div class="terminal-title">
|
||||
<i class="fas fa-terminal"></i>
|
||||
{% trans "Transfer Progress" %}
|
||||
</div>
|
||||
<div class="terminal-dots">
|
||||
<div class="terminal-dot red"></div>
|
||||
<div class="terminal-dot yellow"></div>
|
||||
<div class="terminal-dot green"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="terminal-body">
|
||||
<div class="terminal-grid">
|
||||
<div>
|
||||
<h4 style="color: #94a3b8; margin-bottom: 1rem; font-size: 0.875rem;">
|
||||
{% trans "Backup Progress" %}
|
||||
</h4>
|
||||
<div ng-bind="requestData" style="white-space: pre-wrap;"></div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 style="color: #94a3b8; margin-bottom: 1rem; font-size: 0.875rem;">
|
||||
{% trans "Restore Progress" %}
|
||||
</h4>
|
||||
<div ng-bind="restoreData" style="white-space: pre-wrap;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
492
backup/templates/backup/restore.html
Executable file → Normal file
492
backup/templates/backup/restore.html
Executable file → Normal file
@@ -4,113 +4,483 @@
|
||||
{% block content %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||
|
||||
<style>
|
||||
.modern-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
<div class="container">
|
||||
<div id="page-title">
|
||||
<h2>{% trans "Restore Website" %} - <a target="_blank" href="http://go.cyberpanel.net/backup" style="height: 23px;line-height: 21px;" class="btn btn-border btn-alt border-red btn-link font-red" title=""><span>{% trans "Backup Docs" %}</span></a></h2>
|
||||
<p>{% trans "This page can be used to restore your websites, Backup should be generated from CyberPanel Backup generation tool, it will detect all Backups under <strong>/home/backup</strong>." %}</p>
|
||||
</div>
|
||||
.page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
padding: 3rem 0;
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
border-radius: 20px;
|
||||
animation: fadeInDown 0.5s ease-out;
|
||||
}
|
||||
|
||||
<div ng-controller="restoreWebsiteControl" class="panel">
|
||||
<div class="panel-body">
|
||||
<h3 class="title-hero">
|
||||
{% trans "Restore Website" %} <img ng-hide="restoreLoading" src="{% static 'images/loading.gif' %}">
|
||||
</h3>
|
||||
<div class="example-box-wrapper">
|
||||
.page-title {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 1.25rem;
|
||||
color: #64748b;
|
||||
margin-bottom: 1.5rem;
|
||||
max-width: 800px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
<form action="/" class="form-horizontal bordered-row">
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
background: #e0e7ff;
|
||||
border: 1px solid #c7d2fe;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.info-box i {
|
||||
font-size: 1.5rem;
|
||||
color: #5b5fcf;
|
||||
}
|
||||
|
||||
.info-box p {
|
||||
margin: 0;
|
||||
color: #3730a3;
|
||||
font-size: 0.925rem;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #5b5fcf;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #4547a9;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(91, 95, 207, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #fff;
|
||||
color: #5b5fcf;
|
||||
border: 1px solid #e8e9ff;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #f8f9ff;
|
||||
border-color: #5b5fcf;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(91, 95, 207, 0.2);
|
||||
}
|
||||
|
||||
.main-card {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 10px 40px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e9ff;
|
||||
overflow: hidden;
|
||||
margin-bottom: 2rem;
|
||||
animation: fadeInUp 0.5s ease-out;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f1ff 100%);
|
||||
padding: 1.5rem 2rem;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.875rem 1rem;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 10px;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s ease;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: #5b5fcf;
|
||||
box-shadow: 0 0 0 3px rgba(91, 95, 207, 0.1);
|
||||
}
|
||||
|
||||
.restore-progress-card {
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.progress-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.status-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: #f39c12;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
.status-details h4 {
|
||||
margin: 0;
|
||||
color: #856404;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.status-details p {
|
||||
margin: 0;
|
||||
color: #856404;
|
||||
font-size: 0.875rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.backup-select-card {
|
||||
background: #f8f9ff;
|
||||
border: 1px solid #e8e9ff;
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.backup-icon {
|
||||
font-size: 3rem;
|
||||
color: #5b5fcf;
|
||||
text-align: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
animation: slideInRight 0.3s ease-out;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
border: 1px solid #a7f3d0;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid #e8e9ff;
|
||||
border-top-color: #5b5fcf;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
display: inline-block;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.progress-table {
|
||||
width: 100%;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.progress-table th {
|
||||
background: #f8f9ff;
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
font-size: 0.875rem;
|
||||
border-bottom: 1px solid #e8e9ff;
|
||||
}
|
||||
|
||||
.progress-table td {
|
||||
padding: 0.75rem;
|
||||
color: #64748b;
|
||||
font-size: 0.875rem;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
}
|
||||
|
||||
.file-badge {
|
||||
background: #e0e7ff;
|
||||
color: #5b5fcf;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-weight: 600;
|
||||
color: #f39c12;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="modern-container" ng-controller="restoreWebsiteControl">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">
|
||||
<i class="fas fa-undo-alt"></i>
|
||||
{% trans "Restore Website" %}
|
||||
</h1>
|
||||
<p class="page-subtitle">{% trans "Restore your websites from backups created by CyberPanel" %}</p>
|
||||
<div class="header-actions">
|
||||
<a href="http://go.cyberpanel.net/backup" target="_blank" class="btn-secondary">
|
||||
<i class="fas fa-book"></i>
|
||||
{% trans "Backup Documentation" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info Box -->
|
||||
<div class="info-box">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<p>{% trans "This tool detects all backups under" %} <strong>/home/backup</strong>. {% trans "Backups should be generated from CyberPanel's backup generation tool." %}</p>
|
||||
</div>
|
||||
|
||||
<div class="main-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
<i class="fas fa-folder-open"></i>
|
||||
{% trans "Select Backup to Restore" %}
|
||||
<span ng-hide="restoreLoading" class="loading-spinner"></span>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="/" method="post">
|
||||
<div class="backup-select-card">
|
||||
<div class="backup-icon">
|
||||
<i class="fas fa-archive"></i>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Select Backup" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-database" style="margin-right: 0.5rem;"></i>
|
||||
{% trans "Available Backups" %}
|
||||
</label>
|
||||
<select ng-change="fetchDetails()" ng-model="backupFile" class="form-control">
|
||||
<option value="">{% trans "Choose a backup file..." %}</option>
|
||||
{% for items in backups %}
|
||||
<option>{{ items }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div ng-hide="restoreButton" class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" ng-click="restoreBackup()" id="restoreBackup" class="btn btn-primary btn-lg btn-block">{% trans "Restore" %}</button>
|
||||
|
||||
<div ng-hide="restoreButton" style="text-align: center; margin-top: 2rem;">
|
||||
<button type="button" ng-click="restoreBackup()" id="restoreBackup" class="btn-primary">
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
{% trans "Start Restoration" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Restore Progress -->
|
||||
<div ng-hide="runningRestore" class="restore-progress-card">
|
||||
<div class="progress-status">
|
||||
<div class="status-info">
|
||||
<div class="status-icon">
|
||||
<i class="fas fa-sync-alt fa-spin" style="color: white;"></i>
|
||||
</div>
|
||||
<div class="status-details">
|
||||
<h4>{% trans "Restoration in Progress" %}</h4>
|
||||
<p>{% trans "Please wait while your website is being restored..." %}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!---- if restore is running ----->
|
||||
|
||||
<div ng-hide="runningRestore" class="form-group">
|
||||
|
||||
<div class="col-sm-12">
|
||||
|
||||
<table class="table">
|
||||
<table class="progress-table" style="margin-top: 1.5rem;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Condition" %}</th>
|
||||
<th>{% trans "File Name" %}</th>
|
||||
<th>{% trans "Status" %} <img ng-hide="restoreFinished" src="{% static 'images/loading.gif' %}"></th>
|
||||
<th>
|
||||
{% trans "Status" %}
|
||||
<span ng-hide="restoreFinished" class="loading-spinner" style="width: 16px; height: 16px; margin-left: 0.5rem;"></span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{$ running $}</td>
|
||||
<td>{$ fileName $}</td>
|
||||
<td style="color: red"><strong>{$ status $}</strong></td>
|
||||
<td><span class="file-badge">{$ fileName $}</span></td>
|
||||
<td><span class="status-text">{$ status $}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!---- if restore is running------>
|
||||
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-4">
|
||||
<!-- Alert Messages -->
|
||||
<div ng-hide="backupError" class="alert alert-danger">
|
||||
<p>{% trans "Error message:" %} {$ errorMessage $}</p>
|
||||
<i class="fas fa-exclamation-circle"></i>
|
||||
{% trans "Error message:" %} {$ errorMessage $}
|
||||
</div>
|
||||
|
||||
<div ng-hide="siteExists" class="alert alert-danger">
|
||||
<p>{% trans "Site related to this Backup already exists." %}</p>
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
{% trans "Site related to this backup already exists." %}
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-hide="couldNotConnect" class="alert alert-danger">
|
||||
<p>{% trans "Could not connect to server. Please refresh this page." %}</p>
|
||||
<i class="fas fa-times-circle"></i>
|
||||
{% trans "Could not connect to server. Please refresh this page." %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
0
backup/templates/backup/restoreOCBackups.html
Executable file → Normal file
0
backup/templates/backup/restoreOCBackups.html
Executable file → Normal file
0
backup/tests.py
Executable file → Normal file
0
backup/tests.py
Executable file → Normal file
0
backup/urls.py
Executable file → Normal file
0
backup/urls.py
Executable file → Normal file
0
backup/views.py
Executable file → Normal file
0
backup/views.py
Executable file → Normal file
0
baseTemplate/__init__.py
Executable file → Normal file
0
baseTemplate/__init__.py
Executable file → Normal file
0
baseTemplate/admin.py
Executable file → Normal file
0
baseTemplate/admin.py
Executable file → Normal file
0
baseTemplate/apps.py
Executable file → Normal file
0
baseTemplate/apps.py
Executable file → Normal file
0
baseTemplate/migrations/__init__.py
Executable file → Normal file
0
baseTemplate/migrations/__init__.py
Executable file → Normal file
0
baseTemplate/models.py
Executable file → Normal file
0
baseTemplate/models.py
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/applications/mailbox.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/applications/mailbox.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap-theme.css
vendored
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap-theme.css
vendored
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap-theme.css.map
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap-theme.css.map
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap-theme.min.css
vendored
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap-theme.min.css
vendored
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap-theme.min.css.map
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap-theme.min.css.map
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap.css
vendored
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap.css
vendored
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap.css.map
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap.css.map
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap.min.css
vendored
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap.min.css
vendored
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap.min.css.map
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap.min.css.map
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/fonts/glyphicons-halflings-regular.eot
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/fonts/glyphicons-halflings-regular.eot
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/fonts/glyphicons-halflings-regular.svg
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/fonts/glyphicons-halflings-regular.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
0
baseTemplate/static/baseTemplate/assets/bootstrap/fonts/glyphicons-halflings-regular.ttf
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/fonts/glyphicons-halflings-regular.ttf
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/fonts/glyphicons-halflings-regular.woff
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/fonts/glyphicons-halflings-regular.woff
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/fonts/glyphicons-halflings-regular.woff2
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/fonts/glyphicons-halflings-regular.woff2
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/js/bootstrap.js
vendored
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/js/bootstrap.js
vendored
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/js/bootstrap.min.js
vendored
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/js/bootstrap.min.js
vendored
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/js/npm.js
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/bootstrap/js/npm.js
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/badges.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/badges.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/buttons.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/buttons.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/content-box.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/content-box.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/dashboard-box.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/dashboard-box.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/elements-combind.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/elements-combind.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/elementsCombined.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/elementsCombined.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/elementsCombinedAndMinified.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/elementsCombinedAndMinified.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/forms.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/forms.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/images.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/images.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/info-box.css
Executable file → Normal file
0
baseTemplate/static/baseTemplate/assets/elements/info-box.css
Executable file → Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user