Files

60 lines
5.8 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends 'base.html' %}
{% block content %}
<div class="backup-page">
<div class="backup-page-header">
<div>
<h1>{{ t('backup.title', 'Backups') }}</h1>
<p class="muted">{{ t('backup.description', 'Create, download, upload and restore complete AssetManager backups.') }}</p>
</div>
<form method="post" action="/settings/backup/create" class="backup-create-form">
<button type="submit" class="button backup-primary-action">{{ t('backup.create_now', 'Create backup now') }}</button>
</form>
</div>
<div class="backup-summary-grid">
<section class="backup-summary-card">
<span class="backup-summary-icon" aria-hidden="true">📁</span>
<div><span class="backup-summary-label">{{ t('backup.directory', 'Backup directory') }}</span><strong><code>{{ backup_directory }}</code></strong></div>
</section>
<section class="backup-summary-card">
<span class="backup-summary-icon" aria-hidden="true">🕒</span>
<div><span class="backup-summary-label">{{ t('backup.interval', 'Interval') }}</span><strong>{{ t('backup.every_hours', 'Every {hours} hours', hours=backup_interval_hours) }}</strong></div>
</section>
<section class="backup-summary-card">
<span class="backup-summary-icon" aria-hidden="true">🗓️</span>
<div><span class="backup-summary-label">{{ t('backup.retention', 'Retention') }}</span><strong>{{ t('backup.days', '{days} days', days=backup_retention_days) }}</strong></div>
</section>
</div>
<section class="panel backup-panel">
<div class="backup-panel-heading">
<div><h2>{{ t('backup.automatic_title', 'Automatic backup') }}</h2><p class="muted">{{ t('backup.automatic_help', 'Backups are created automatically according to the configured interval and deleted after the retention period.') }}</p></div>
<span class="backup-status-badge">{{ t('backup.active', 'Active') }}</span>
</div>
<div class="backup-content-note"><span aria-hidden="true"></span><p>{{ t('backup.contents_help', 'Each backup contains a PostgreSQL database dump, configuration, APPINFO and uploaded files. Environment variables and the .env file are not included.') }}</p></div>
</section>
<section class="panel backup-panel">
<div class="backup-panel-heading"><div><h2>{{ t('backup.upload_title', 'Upload backup') }}</h2><p class="muted">{{ t('backup.upload_help', 'Upload an AssetManager backup ZIP to make it available for download or restoration.') }}</p></div></div>
<form method="post" action="/settings/backup/upload" enctype="multipart/form-data" class="backup-upload-form">
<input type="file" name="backup_file" accept=".zip,application/zip" required>
<button type="submit" class="button-secondary">{{ t('backup.upload', 'Upload') }}</button>
</form>
</section>
<section class="panel backup-panel">
<div class="backup-panel-heading"><div><h2>{{ t('backup.available', 'Available backups') }}</h2><p class="muted">{{ t('backup.available_help', 'Stored backups can be downloaded, restored or deleted here.') }}</p></div><span class="backup-count">{{ backups|length }}</span></div>
{% if backups %}
<div class="table-scroll backup-table-wrap"><table class="data-table backup-table"><thead><tr>
<th>{{ t('backup.file', 'File') }}</th><th>{{ t('backup.created', 'Created') }}</th><th>{{ t('backup.version', 'Version') }}</th><th>{{ t('backup.reason', 'Reason') }}</th><th>{{ t('backup.size', 'Size') }}</th><th>{{ t('common.actions', 'Actions') }}</th>
</tr></thead><tbody>{% for backup in backups %}<tr>
<td><code>{{ backup.name }}</code>{% if not backup.valid %}<br><span class="muted">{{ t('backup.invalid_metadata', 'Metadata could not be read') }}</span>{% endif %}</td>
<td><time class="local-time" datetime="{{ backup.modified_at.isoformat() }}" data-utc="{{ backup.modified_at.isoformat() }}">{{ backup.modified_at.strftime('%Y-%m-%d %H:%M:%S') }}</time></td><td>{{ backup.version if backup.version else t('backup.version_unknown') }}</td><td>{{ t('backup.reason_' ~ (backup.reason or 'unknown'), backup.reason or '—') }}</td><td>{{ '%.2f'|format(backup.size / 1024 / 1024) }} MB</td>
<td><div class="backup-actions"><a class="button button-secondary" href="/settings/backup/{{ backup.name }}/download">{{ t('backup.download', 'Download') }}</a><button type="button" class="button-secondary" onclick="document.getElementById('restore-{{ loop.index }}').showModal()">{{ t('backup.restore', 'Restore') }}</button><form method="post" action="/settings/backup/{{ backup.name }}/delete" onsubmit="return confirm('{{ t('backup.delete_confirm', 'Delete this backup permanently?')|e }}')"><button type="submit" class="button-danger">{{ t('common.delete', 'Delete') }}</button></form></div>
<dialog id="restore-{{ loop.index }}" class="backup-dialog"><form method="post" action="/settings/backup/{{ backup.name }}/restore"><h2>{{ t('backup.restore_title', 'Restore backup') }}</h2><p>{{ t('backup.restore_warning', 'The current database, configuration and uploads will be replaced. A safety backup is created immediately before restoration.') }}</p><label>{{ t('backup.restore_confirmation', 'Enter RESTORE to confirm') }}<input name="confirmation" autocomplete="off" required></label><div class="form-actions"><button type="button" class="button-secondary" onclick="this.closest('dialog').close()">{{ t('common.cancel', 'Cancel') }}</button><button type="submit" class="button-danger">{{ t('backup.restore_now', 'Restore now') }}</button></div></form></dialog></td>
</tr>{% endfor %}</tbody></table></div>
{% else %}<div class="backup-empty-state"><span aria-hidden="true">🗄️</span><strong>{{ t('backup.none_title', 'No backups yet') }}</strong><p class="muted">{{ t('backup.none', 'No backups are available yet.') }}</p></div>{% endif %}
</section>
</div>
{% endblock %}