Skip to content

Commit b6f20a6

Browse files
committed
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
1 parent fb02243 commit b6f20a6

File tree

2 files changed

+157
-4
lines changed

2 files changed

+157
-4
lines changed

backup/backupManager.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,8 +2119,53 @@ def ManageOCBackups(self, request=None, userID=None, data=None):
21192119

21202120
websitesName = ACLManager.findAllSites(currentACL, userID)
21212121

2122-
proc = httpProc(request, 'backup/OneClickBackupSchedule.html', {'destination': NormalBackupDests.objects.get(name=ocb.sftpUser).name, 'websites': websitesName},
2123-
'scheduleBackups')
2122+
# Fetch storage stats and backup info from platform API
2123+
storage_info = {
2124+
'total_storage': 'N/A',
2125+
'used_storage': 'N/A',
2126+
'available_storage': 'N/A',
2127+
'usage_percentage': 0,
2128+
'last_backup_run': 'Never',
2129+
'last_backup_status': 'N/A',
2130+
'total_backups': 0,
2131+
'failed_backups': 0,
2132+
'error_logs': []
2133+
}
2134+
2135+
try:
2136+
import requests
2137+
url = 'https://platform.cyberpersons.com/Billing/GetBackupStats'
2138+
payload = {
2139+
'sub': ocb.subscription,
2140+
'sftpUser': ocb.sftpUser,
2141+
'serverIP': ACLManager.fetchIP()
2142+
}
2143+
headers = {'Content-Type': 'application/json'}
2144+
2145+
response = requests.post(url, headers=headers, data=json.dumps(payload), timeout=30)
2146+
2147+
if response.status_code == 200:
2148+
api_data = response.json()
2149+
if api_data.get('status') == 1:
2150+
storage_info = api_data.get('data', storage_info)
2151+
logging.CyberCPLogFileWriter.writeToFile(f'Successfully fetched backup stats for {ocb.sftpUser} [ManageOCBackups]')
2152+
else:
2153+
logging.CyberCPLogFileWriter.writeToFile(f'Platform API returned error: {api_data.get("error_message")} [ManageOCBackups]')
2154+
else:
2155+
logging.CyberCPLogFileWriter.writeToFile(f'Platform API returned HTTP {response.status_code} [ManageOCBackups]')
2156+
except Exception as e:
2157+
logging.CyberCPLogFileWriter.writeToFile(f'Failed to fetch backup stats: {str(e)} [ManageOCBackups]')
2158+
2159+
context = {
2160+
'destination': NormalBackupDests.objects.get(name=ocb.sftpUser).name,
2161+
'websites': websitesName,
2162+
'storage_info': storage_info,
2163+
'ocb_subscription': ocb.subscription,
2164+
'ocb_plan_name': ocb.planName,
2165+
'ocb_sftp_user': ocb.sftpUser
2166+
}
2167+
2168+
proc = httpProc(request, 'backup/OneClickBackupSchedule.html', context, 'scheduleBackups')
21242169
return proc.render()
21252170

21262171
def RestoreOCBackups(self, request=None, userID=None, data=None):

backup/templates/backup/OneClickBackupSchedule.html

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,15 +380,123 @@ <h1 class="page-title">
380380
</h1>
381381
<p class="page-subtitle">{% trans "Schedule automated backups to protect your data on localhost or remote server" %}</p>
382382
<div class="header-actions">
383-
<a href="https://cyberpanel.net/KnowledgeBase/home/schedule-backups-local-or-sftp/"
384-
target="_blank"
383+
<a href="https://cyberpanel.net/KnowledgeBase/home/schedule-backups-local-or-sftp/"
384+
target="_blank"
385385
class="btn-secondary">
386386
<i class="fas fa-book"></i>
387387
{% trans "Documentation" %}
388388
</a>
389389
</div>
390390
</div>
391391

392+
<!-- Storage and Backup Stats Section -->
393+
<div class="main-card" style="margin-bottom: 2rem;">
394+
<div class="card-header">
395+
<h2 class="card-title">
396+
<i class="fas fa-chart-pie"></i>
397+
{% trans "Backup Account Overview" %}
398+
</h2>
399+
</div>
400+
<div class="card-body">
401+
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem;">
402+
<!-- Storage Stats -->
403+
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 1.5rem; border-radius: 12px; color: white;">
404+
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 0.5rem;">
405+
<i class="fas fa-hdd" style="font-size: 2rem; opacity: 0.9;"></i>
406+
<div>
407+
<div style="font-size: 0.875rem; opacity: 0.9;">{% trans "Storage Used" %}</div>
408+
<div style="font-size: 1.5rem; font-weight: 700;">{{ storage_info.used_storage }}</div>
409+
</div>
410+
</div>
411+
<div style="font-size: 0.875rem; opacity: 0.9;">{% trans "of" %} {{ storage_info.total_storage }}</div>
412+
<div style="background: rgba(255,255,255,0.2); height: 8px; border-radius: 4px; margin-top: 0.75rem; overflow: hidden;">
413+
<div style="background: rgba(255,255,255,0.9); height: 100%; width: {{ storage_info.usage_percentage }}%; transition: width 0.3s ease;"></div>
414+
</div>
415+
</div>
416+
417+
<!-- Last Backup -->
418+
<div style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); padding: 1.5rem; border-radius: 12px; color: white;">
419+
<div style="display: flex; align-items: center; gap: 1rem;">
420+
<i class="fas fa-clock" style="font-size: 2rem; opacity: 0.9;"></i>
421+
<div>
422+
<div style="font-size: 0.875rem; opacity: 0.9;">{% trans "Last Backup Run" %}</div>
423+
<div style="font-size: 1.125rem; font-weight: 600;">{{ storage_info.last_backup_run }}</div>
424+
<div style="font-size: 0.875rem; opacity: 0.9; margin-top: 0.25rem;">
425+
{% if storage_info.last_backup_status == 'success' %}
426+
<i class="fas fa-check-circle"></i> {% trans "Success" %}
427+
{% elif storage_info.last_backup_status == 'failed' %}
428+
<i class="fas fa-exclamation-circle"></i> {% trans "Failed" %}
429+
{% else %}
430+
{{ storage_info.last_backup_status }}
431+
{% endif %}
432+
</div>
433+
</div>
434+
</div>
435+
</div>
436+
437+
<!-- Total Backups -->
438+
<div style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); padding: 1.5rem; border-radius: 12px; color: white;">
439+
<div style="display: flex; align-items: center; gap: 1rem;">
440+
<i class="fas fa-database" style="font-size: 2rem; opacity: 0.9;"></i>
441+
<div>
442+
<div style="font-size: 0.875rem; opacity: 0.9;">{% trans "Total Backups" %}</div>
443+
<div style="font-size: 1.5rem; font-weight: 700;">{{ storage_info.total_backups }}</div>
444+
{% if storage_info.failed_backups > 0 %}
445+
<div style="font-size: 0.875rem; opacity: 0.9; margin-top: 0.25rem;">
446+
<i class="fas fa-exclamation-triangle"></i> {{ storage_info.failed_backups }} {% trans "failed" %}
447+
</div>
448+
{% endif %}
449+
</div>
450+
</div>
451+
</div>
452+
453+
<!-- Account Info -->
454+
<div style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); padding: 1.5rem; border-radius: 12px; color: white;">
455+
<div style="display: flex; align-items: center; gap: 1rem;">
456+
<i class="fas fa-user-circle" style="font-size: 2rem; opacity: 0.9;"></i>
457+
<div>
458+
<div style="font-size: 0.875rem; opacity: 0.9;">{% trans "Backup Account" %}</div>
459+
<div style="font-size: 1.125rem; font-weight: 600;">{{ ocb_sftp_user }}</div>
460+
<div style="font-size: 0.875rem; opacity: 0.9; margin-top: 0.25rem;">{{ ocb_plan_name }}</div>
461+
</div>
462+
</div>
463+
</div>
464+
</div>
465+
</div>
466+
</div>
467+
468+
<!-- Error Logs Section -->
469+
{% if storage_info.error_logs %}
470+
<div class="main-card" style="margin-bottom: 2rem;">
471+
<div class="card-header" style="background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%);">
472+
<h2 class="card-title" style="color: #991b1b;">
473+
<i class="fas fa-exclamation-triangle"></i>
474+
{% trans "Recent Backup Errors" %}
475+
</h2>
476+
</div>
477+
<div class="card-body" style="padding: 0;">
478+
<table class="data-table">
479+
<thead>
480+
<tr>
481+
<th style="width: 180px;">{% trans "Date/Time" %}</th>
482+
<th style="width: 150px;">{% trans "Website" %}</th>
483+
<th>{% trans "Error Message" %}</th>
484+
</tr>
485+
</thead>
486+
<tbody>
487+
{% for log in storage_info.error_logs %}
488+
<tr>
489+
<td>{{ log.timestamp }}</td>
490+
<td><strong>{{ log.website }}</strong></td>
491+
<td style="color: var(--danger-text, #991b1b);">{{ log.error_message }}</td>
492+
</tr>
493+
{% endfor %}
494+
</tbody>
495+
</table>
496+
</div>
497+
</div>
498+
{% endif %}
499+
392500
<div ng-controller="scheduleBackup">
393501
<!-- Create New Schedule Card -->
394502
<div class="main-card">

0 commit comments

Comments
 (0)