design overall

This commit is contained in:
usmannasir
2025-06-15 01:10:08 +05:00
parent 03e8bbbf54
commit f23b57053c
2075 changed files with 102714 additions and 25096 deletions

0
ApachController/ApacheController.py Executable file → Normal file
View File

0
ApachController/ApacheVhosts.py Executable file → Normal file
View File

0
ApachController/__init__.py Executable file → Normal file
View File

0
ApachController/phpApache.xml Executable file → Normal file
View File

0
CLManager/CLPackages.py Executable file → Normal file
View File

0
CLManager/templates/CLManager/cloudLinux.html Executable file → Normal file
View File

0
CLManager/templates/CLManager/createPackage.html Executable file → Normal file
View File

0
CLManager/templates/CLManager/listPackages.html Executable file → Normal file
View File

0
CLManager/templates/CLManager/listWebsites.html Executable file → Normal file
View File

0
CLManager/templates/CLManager/monitorUsage.html Executable file → Normal file
View File

0
CLManager/templates/CLManager/notAvailable.html Executable file → Normal file
View File

View File

0
CLScript/CloudLinuxAdmins.py Executable file → Normal file
View File

0
CLScript/CloudLinuxDB.py Executable file → Normal file
View File

0
CLScript/CloudLinuxDomains.py Executable file → Normal file
View File

0
CLScript/CloudLinuxPackages.py Executable file → Normal file
View File

0
CLScript/CloudLinuxResellers.py Executable file → Normal file
View File

0
CLScript/CloudLinuxUsers.py Executable file → Normal file
View File

0
CLScript/UserInfo.py Executable file → Normal file
View File

0
CLScript/panel_info.py Executable file → Normal file
View File

0
CyberCP/__init__.py Executable file → Normal file
View File

0
CyberCP/secMiddleware.py Executable file → Normal file
View File

8
CyberCP/settings.py Executable file → Normal file
View 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
View File

0
CyberCP/wsgi.py Executable file → Normal file
View File

View 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
View File

654
IncBackups/templates/IncBackups/createBackup.html Executable file → Normal file
View 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">
&times;
</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">&times;</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 %}

View 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 %}

View File

0
WebTerminal/static/WebTerminal/main.js Executable file → Normal file
View File

0
WebTerminal/static/WebTerminal/term.js Executable file → Normal file
View File

0
WebTerminal/static/WebTerminal/ws.js Executable file → Normal file
View File

0
WebTerminal/static/images/loading.gif Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

0
WebTerminal/templates/WebTerminal/WebTerminal.html Executable file → Normal file
View File

0
WebTerminal/urls.py Executable file → Normal file
View File

0
api/__init__.py Executable file → Normal file
View File

0
api/admin.py Executable file → Normal file
View File

0
api/apps.py Executable file → Normal file
View File

0
api/migrations/__init__.py Executable file → Normal file
View File

0
api/models.py Executable file → Normal file
View File

0
api/tests.py Executable file → Normal file
View File

0
api/urls.py Executable file → Normal file
View File

0
api/views.py Executable file → Normal file
View File

0
backup/__init__.py Executable file → Normal file
View File

0
backup/admin.py Executable file → Normal file
View File

0
backup/apps.py Executable file → Normal file
View File

0
backup/backupManager.py Executable file → Normal file
View File

0
backup/backupRouter.py Executable file → Normal file
View File

0
backup/migrations/__init__.py Executable file → Normal file
View File

0
backup/models.py Executable file → Normal file
View File

0
backup/pluginManager.py Executable file → Normal file
View File

0
backup/signals.py Executable file → Normal file
View File

0
backup/static/backup/backup.js Executable file → Normal file
View File

0
backup/templates/backup/OneClickBackupSchedule.html Executable file → Normal file
View File

661
backup/templates/backup/backup.html Executable file → Normal file
View 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
View 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
View File

795
backup/templates/backup/backupSchedule.html Executable file → Normal file
View 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">&times;
<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;">&times;</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
View 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">&times;
</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">&times;
<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;">&times;</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;">&times;</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
View 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
View 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
View 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
View File

0
backup/tests.py Executable file → Normal file
View File

0
backup/urls.py Executable file → Normal file
View File

0
backup/views.py Executable file → Normal file
View File

0
baseTemplate/__init__.py Executable file → Normal file
View File

0
baseTemplate/admin.py Executable file → Normal file
View File

0
baseTemplate/apps.py Executable file → Normal file
View File

0
baseTemplate/migrations/__init__.py Executable file → Normal file
View File

0
baseTemplate/models.py Executable file → Normal file
View File

View File

View File

View File

0
baseTemplate/static/baseTemplate/assets/bootstrap/css/bootstrap.css vendored Executable file → Normal file
View File

View File

View File

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

0
baseTemplate/static/baseTemplate/assets/bootstrap/js/bootstrap.js vendored Executable file → Normal file
View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

Some files were not shown because too many files have changed in this diff Show More