mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-10-26 07:46:35 +01:00
Add storage stats, last backup info, and error logs to ManageOCBackups page
- Add comprehensive backup account overview with visual stats cards - Display storage usage (total, used, available, percentage) from platform API - Show last backup run timestamp and status (success/failed) - Display total backups count and failed backups count - Add recent backup error logs table with timestamp, website, and error message - Fetch all stats from platform.cyberpersons.com/Billing/GetBackupStats endpoint - Beautiful gradient cards for visual presentation of stats - Progress bar for storage usage visualization - Conditional display of error logs (only shown if errors exist) - Add account info card showing SFTP user and plan name - Graceful fallback to N/A if platform API is unavailable - Comprehensive error logging for API failures
This commit is contained in:
@@ -2119,8 +2119,53 @@ class BackupManager:
|
||||
|
||||
websitesName = ACLManager.findAllSites(currentACL, userID)
|
||||
|
||||
proc = httpProc(request, 'backup/OneClickBackupSchedule.html', {'destination': NormalBackupDests.objects.get(name=ocb.sftpUser).name, 'websites': websitesName},
|
||||
'scheduleBackups')
|
||||
# Fetch storage stats and backup info from platform API
|
||||
storage_info = {
|
||||
'total_storage': 'N/A',
|
||||
'used_storage': 'N/A',
|
||||
'available_storage': 'N/A',
|
||||
'usage_percentage': 0,
|
||||
'last_backup_run': 'Never',
|
||||
'last_backup_status': 'N/A',
|
||||
'total_backups': 0,
|
||||
'failed_backups': 0,
|
||||
'error_logs': []
|
||||
}
|
||||
|
||||
try:
|
||||
import requests
|
||||
url = 'https://platform.cyberpersons.com/Billing/GetBackupStats'
|
||||
payload = {
|
||||
'sub': ocb.subscription,
|
||||
'sftpUser': ocb.sftpUser,
|
||||
'serverIP': ACLManager.fetchIP()
|
||||
}
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
response = requests.post(url, headers=headers, data=json.dumps(payload), timeout=30)
|
||||
|
||||
if response.status_code == 200:
|
||||
api_data = response.json()
|
||||
if api_data.get('status') == 1:
|
||||
storage_info = api_data.get('data', storage_info)
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Successfully fetched backup stats for {ocb.sftpUser} [ManageOCBackups]')
|
||||
else:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Platform API returned error: {api_data.get("error_message")} [ManageOCBackups]')
|
||||
else:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Platform API returned HTTP {response.status_code} [ManageOCBackups]')
|
||||
except Exception as e:
|
||||
logging.CyberCPLogFileWriter.writeToFile(f'Failed to fetch backup stats: {str(e)} [ManageOCBackups]')
|
||||
|
||||
context = {
|
||||
'destination': NormalBackupDests.objects.get(name=ocb.sftpUser).name,
|
||||
'websites': websitesName,
|
||||
'storage_info': storage_info,
|
||||
'ocb_subscription': ocb.subscription,
|
||||
'ocb_plan_name': ocb.planName,
|
||||
'ocb_sftp_user': ocb.sftpUser
|
||||
}
|
||||
|
||||
proc = httpProc(request, 'backup/OneClickBackupSchedule.html', context, 'scheduleBackups')
|
||||
return proc.render()
|
||||
|
||||
def RestoreOCBackups(self, request=None, userID=None, data=None):
|
||||
|
||||
@@ -380,8 +380,8 @@
|
||||
</h1>
|
||||
<p class="page-subtitle">{% trans "Schedule automated backups to protect your data on localhost or remote server" %}</p>
|
||||
<div class="header-actions">
|
||||
<a href="https://cyberpanel.net/KnowledgeBase/home/schedule-backups-local-or-sftp/"
|
||||
target="_blank"
|
||||
<a href="https://cyberpanel.net/KnowledgeBase/home/schedule-backups-local-or-sftp/"
|
||||
target="_blank"
|
||||
class="btn-secondary">
|
||||
<i class="fas fa-book"></i>
|
||||
{% trans "Documentation" %}
|
||||
@@ -389,6 +389,114 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Storage and Backup Stats Section -->
|
||||
<div class="main-card" style="margin-bottom: 2rem;">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">
|
||||
<i class="fas fa-chart-pie"></i>
|
||||
{% trans "Backup Account Overview" %}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem;">
|
||||
<!-- Storage Stats -->
|
||||
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 1.5rem; border-radius: 12px; color: white;">
|
||||
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 0.5rem;">
|
||||
<i class="fas fa-hdd" style="font-size: 2rem; opacity: 0.9;"></i>
|
||||
<div>
|
||||
<div style="font-size: 0.875rem; opacity: 0.9;">{% trans "Storage Used" %}</div>
|
||||
<div style="font-size: 1.5rem; font-weight: 700;">{{ storage_info.used_storage }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="font-size: 0.875rem; opacity: 0.9;">{% trans "of" %} {{ storage_info.total_storage }}</div>
|
||||
<div style="background: rgba(255,255,255,0.2); height: 8px; border-radius: 4px; margin-top: 0.75rem; overflow: hidden;">
|
||||
<div style="background: rgba(255,255,255,0.9); height: 100%; width: {{ storage_info.usage_percentage }}%; transition: width 0.3s ease;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Last Backup -->
|
||||
<div style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); padding: 1.5rem; border-radius: 12px; color: white;">
|
||||
<div style="display: flex; align-items: center; gap: 1rem;">
|
||||
<i class="fas fa-clock" style="font-size: 2rem; opacity: 0.9;"></i>
|
||||
<div>
|
||||
<div style="font-size: 0.875rem; opacity: 0.9;">{% trans "Last Backup Run" %}</div>
|
||||
<div style="font-size: 1.125rem; font-weight: 600;">{{ storage_info.last_backup_run }}</div>
|
||||
<div style="font-size: 0.875rem; opacity: 0.9; margin-top: 0.25rem;">
|
||||
{% if storage_info.last_backup_status == 'success' %}
|
||||
<i class="fas fa-check-circle"></i> {% trans "Success" %}
|
||||
{% elif storage_info.last_backup_status == 'failed' %}
|
||||
<i class="fas fa-exclamation-circle"></i> {% trans "Failed" %}
|
||||
{% else %}
|
||||
{{ storage_info.last_backup_status }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Total Backups -->
|
||||
<div style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); padding: 1.5rem; border-radius: 12px; color: white;">
|
||||
<div style="display: flex; align-items: center; gap: 1rem;">
|
||||
<i class="fas fa-database" style="font-size: 2rem; opacity: 0.9;"></i>
|
||||
<div>
|
||||
<div style="font-size: 0.875rem; opacity: 0.9;">{% trans "Total Backups" %}</div>
|
||||
<div style="font-size: 1.5rem; font-weight: 700;">{{ storage_info.total_backups }}</div>
|
||||
{% if storage_info.failed_backups > 0 %}
|
||||
<div style="font-size: 0.875rem; opacity: 0.9; margin-top: 0.25rem;">
|
||||
<i class="fas fa-exclamation-triangle"></i> {{ storage_info.failed_backups }} {% trans "failed" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Account Info -->
|
||||
<div style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); padding: 1.5rem; border-radius: 12px; color: white;">
|
||||
<div style="display: flex; align-items: center; gap: 1rem;">
|
||||
<i class="fas fa-user-circle" style="font-size: 2rem; opacity: 0.9;"></i>
|
||||
<div>
|
||||
<div style="font-size: 0.875rem; opacity: 0.9;">{% trans "Backup Account" %}</div>
|
||||
<div style="font-size: 1.125rem; font-weight: 600;">{{ ocb_sftp_user }}</div>
|
||||
<div style="font-size: 0.875rem; opacity: 0.9; margin-top: 0.25rem;">{{ ocb_plan_name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error Logs Section -->
|
||||
{% if storage_info.error_logs %}
|
||||
<div class="main-card" style="margin-bottom: 2rem;">
|
||||
<div class="card-header" style="background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%);">
|
||||
<h2 class="card-title" style="color: #991b1b;">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
{% trans "Recent Backup Errors" %}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body" style="padding: 0;">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 180px;">{% trans "Date/Time" %}</th>
|
||||
<th style="width: 150px;">{% trans "Website" %}</th>
|
||||
<th>{% trans "Error Message" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for log in storage_info.error_logs %}
|
||||
<tr>
|
||||
<td>{{ log.timestamp }}</td>
|
||||
<td><strong>{{ log.website }}</strong></td>
|
||||
<td style="color: var(--danger-text, #991b1b);">{{ log.error_message }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div ng-controller="scheduleBackup">
|
||||
<!-- Create New Schedule Card -->
|
||||
<div class="main-card">
|
||||
|
||||
Reference in New Issue
Block a user