Files
opnsense-sftp/templates/dashboard.html
T
2025-11-01 16:04:10 +00:00

171 lines
9.6 KiB
HTML

{% extends "base.html" %}
{% block title %}Dashboard - OPNsense Backup Manager{% endblock %}
{% block content %}
<div class="space-y-8">
<div>
<h1 class="text-3xl font-bold text-neutral-100 mb-2">Dashboard</h1>
<p class="text-neutral-400">Overview of your OPNsense backup system</p>
</div>
<!-- Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="bg-neutral-800 rounded-lg p-6 border border-neutral-700">
<div class="flex items-center justify-between">
<div>
<p class="text-neutral-400 text-sm font-medium">Total Instances</p>
<p class="text-3xl font-bold text-orange-500 mt-2">{{ instances|length }}</p>
</div>
<div class="p-3 bg-orange-500/20 rounded-lg">
<svg class="w-8 h-8 text-orange-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path>
</svg>
</div>
</div>
</div>
<div class="bg-neutral-800 rounded-lg p-6 border border-neutral-700">
<div class="flex items-center justify-between">
<div>
<p class="text-neutral-400 text-sm font-medium">Total Backups</p>
<p class="text-3xl font-bold text-orange-500 mt-2">{{ backups|length }}</p>
</div>
<div class="p-3 bg-orange-500/20 rounded-lg">
<svg class="w-8 h-8 text-orange-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"></path>
</svg>
</div>
</div>
</div>
<div class="bg-neutral-800 rounded-lg p-6 border border-neutral-700">
<div class="flex items-center justify-between">
<div>
<p class="text-neutral-400 text-sm font-medium">SFTP Port</p>
<p class="text-3xl font-bold text-orange-500 mt-2">{{ sftp_server.port if sftp_server else 'N/A' }}</p>
</div>
<div class="p-3 bg-orange-500/20 rounded-lg">
<svg class="w-8 h-8 text-orange-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"></path>
</svg>
</div>
</div>
</div>
</div>
<!-- Actions -->
<div class="flex justify-between items-center">
<h2 class="text-2xl font-bold text-neutral-100">Quick Actions</h2>
<a href="{{ url_for('new_instance') }}" class="bg-orange-500 hover:bg-orange-600 text-white font-medium py-2 px-4 rounded-md transition-colors duration-200">
+ Add Instance
</a>
</div>
<!-- Instances List -->
<div class="bg-neutral-800 rounded-lg border border-neutral-700 overflow-hidden">
<div class="px-6 py-4 border-b border-neutral-700">
<h2 class="text-xl font-bold text-neutral-100">OPNsense Instances</h2>
</div>
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-neutral-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-300 uppercase tracking-wider">Name</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-300 uppercase tracking-wider">Identifier</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-300 uppercase tracking-wider">Last Backup</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-300 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-neutral-700">
{% if instances %}
{% for instance in instances %}
<tr class="hover:bg-neutral-700/50 transition-colors">
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-neutral-100">{{ instance.name }}</div>
{% if instance.description %}
<div class="text-sm text-neutral-400">{{ instance.description }}</div>
{% endif %}
</td>
<td class="px-6 py-4 whitespace-nowrap">
<code class="text-sm text-orange-400">{{ instance.identifier }}</code>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="text-sm text-neutral-400">
{% if instance.last_backup %}
{{ instance.last_backup.strftime('%Y-%m-%d %H:%M') }}
{% else %}
<span class="text-neutral-500">Never</span>
{% endif %}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<a href="{{ url_for('instance_detail', instance_id=instance.id) }}" class="text-orange-500 hover:text-orange-400 mr-4">View</a>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="4" class="px-6 py-8 text-center text-neutral-400">
No instances configured. <a href="{{ url_for('new_instance') }}" class="text-orange-500 hover:text-orange-400">Create one</a> to get started.
</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
</div>
<!-- Recent Backups -->
<div class="bg-neutral-800 rounded-lg border border-neutral-700 overflow-hidden">
<div class="px-6 py-4 border-b border-neutral-700 flex justify-between items-center">
<h2 class="text-xl font-bold text-neutral-100">Recent Backups</h2>
<a href="{{ url_for('backups') }}" class="text-sm text-orange-500 hover:text-orange-400">View All</a>
</div>
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-neutral-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-300 uppercase tracking-wider">Instance</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-300 uppercase tracking-wider">Filename</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-300 uppercase tracking-wider">Size</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-300 uppercase tracking-wider">Date</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-300 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-neutral-700">
{% if backups %}
{% for backup in backups[:10] %}
<tr class="hover:bg-neutral-700/50 transition-colors">
<td class="px-6 py-4 whitespace-nowrap">
<span class="text-sm text-neutral-100">{{ backup.instance_name or backup.instance_identifier }}</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<code class="text-sm text-neutral-300">{{ backup.filename }}</code>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="text-sm text-neutral-400">{{ "%.2f"|format(backup.file_size / 1024 / 1024) }} MB</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="text-sm text-neutral-400">{{ backup.uploaded_at.strftime('%Y-%m-%d %H:%M') if backup.uploaded_at else 'N/A' }}</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<a href="{{ url_for('download_backup', backup_id=backup.id) }}" class="text-orange-500 hover:text-orange-400">Download</a>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="5" class="px-6 py-8 text-center text-neutral-400">
No backups yet. Configure an instance and start backing up!
</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}