| {% extends "base.html" %} |
| {% block title %}Backups - SQLite DBaaS{% endblock %} |
| {% block content %} |
| <div class="page-header"> |
| <div> |
| <h1 class="page-title">Backups</h1> |
| <p class="page-subtitle">Manage database snapshots and remote storage</p> |
| </div> |
| <a href="/admin/dashboard" class="m3-button m3-button--text"> |
| <span class="material-symbols-outlined">arrow_back</span> |
| Dashboard |
| </a> |
| </div> |
|
|
| |
| <div class="m3-card"> |
| <div class="m3-card-header"> |
| <div class="m3-card-icon"> |
| <span class="material-symbols-outlined">cloud_sync</span> |
| </div> |
| <div> |
| <div class="m3-card-title">HuggingFace Setup</div> |
| <div class="m3-card-subtitle">Remote backup configuration</div> |
| </div> |
| </div> |
| |
| <div class="diag-list"> |
| {% for check in hf_diagnosis.checks %} |
| <div class="diag-item"> |
| <div class="diag-status {% if '✅' in check.status %}success{% elif '❌' in check.status %}error{% endif %}"> |
| <span class="material-symbols-outlined"> |
| {% if '✅' in check.status %}check{% elif '❌' in check.status %}close{% else %}info{% endif %} |
| </span> |
| </div> |
| <div class="diag-content"> |
| <div class="diag-name">{{ check.name }}</div> |
| <div class="diag-value">{{ check.value }}</div> |
| </div> |
| </div> |
| {% endfor %} |
| </div> |
| </div> |
|
|
| |
| <div class="m3-card"> |
| <div class="m3-card-header"> |
| <div class="m3-card-icon"> |
| <span class="material-symbols-outlined">add_circle</span> |
| </div> |
| <div> |
| <div class="m3-card-title">Create Backup</div> |
| <div class="m3-card-subtitle">Local snapshot or remote upload</div> |
| </div> |
| </div> |
| |
| <div class="m3-button-group"> |
| <form method="POST" action="/admin/backups/create" style="display: inline;"> |
| <input type="hidden" name="backup_type" value="snapshot"> |
| <button type="submit" class="m3-button m3-button--outlined"> |
| <span class="material-symbols-outlined">photo_camera</span> |
| Snapshot |
| </button> |
| </form> |
| |
| <form method="POST" action="/admin/backups/create" style="display: inline;"> |
| <input type="hidden" name="backup_type" value="compressed"> |
| <button type="submit" class="m3-button m3-button--outlined"> |
| <span class="material-symbols-outlined">compress</span> |
| Compressed |
| </button> |
| </form> |
| |
| <form method="POST" action="/admin/backups/create" style="display: inline;"> |
| <input type="hidden" name="backup_type" value="vacuum"> |
| <button type="submit" class="m3-button m3-button--outlined"> |
| <span class="material-symbols-outlined">cleaning_services</span> |
| VACUUM |
| </button> |
| </form> |
| |
| <form method="POST" action="/admin/backups/create" style="display: inline;"> |
| <input type="hidden" name="backup_type" value="hf_upload"> |
| <button type="submit" class="m3-button m3-button--filled"> |
| <span class="material-symbols-outlined">cloud_upload</span> |
| Upload to HF |
| </button> |
| </form> |
| </div> |
| </div> |
|
|
| |
| <div class="m3-card"> |
| <div class="m3-card-header"> |
| <div class="m3-card-icon"> |
| <span class="material-symbols-outlined">folder</span> |
| </div> |
| <div> |
| <div class="m3-card-title">Local Snapshots</div> |
| <div class="m3-card-subtitle">{{ backups.snapshot_dir }}</div> |
| </div> |
| </div> |
| |
| {% if backups.local_snapshots %} |
| <div class="m3-table-container"> |
| <table class="m3-table"> |
| <thead> |
| <tr> |
| <th>Filename</th> |
| <th>Size</th> |
| <th>Modified</th> |
| <th>Actions</th> |
| </tr> |
| </thead> |
| <tbody> |
| {% for snapshot in backups.local_snapshots %} |
| <tr> |
| <td class="mono">{{ snapshot.name }}</td> |
| <td>{{ snapshot.size_mb }} MB</td> |
| <td>{{ snapshot.modified }}</td> |
| <td> |
| <a href="/admin/backups/download/{{ snapshot.name }}" class="m3-button m3-button--text"> |
| <span class="material-symbols-outlined">download</span> |
| </a> |
| </td> |
| </tr> |
| {% endfor %} |
| </tbody> |
| </table> |
| </div> |
| {% else %} |
| <div class="empty-state"> |
| <span class="material-symbols-outlined empty-icon">folder_off</span> |
| <div class="empty-title">No snapshots yet</div> |
| <div class="empty-description">Create your first snapshot above</div> |
| </div> |
| {% endif %} |
| </div> |
|
|
| |
| <div class="m3-card"> |
| <div class="m3-card-header"> |
| <div class="m3-card-icon"> |
| <span class="material-symbols-outlined">layers</span> |
| </div> |
| <div> |
| <div class="m3-card-title">3-Layer Backup Strategy</div> |
| </div> |
| </div> |
| |
| <div class="strategy-layers"> |
| <div class="strategy-layer"> |
| <div class="layer-number">1</div> |
| <div class="layer-content"> |
| <h4>Litestream — Real-time</h4> |
| <p>Automatic replication every second. Max 1s data loss. Local file replica.</p> |
| </div> |
| </div> |
| <div class="strategy-layer"> |
| <div class="layer-number">2</div> |
| <div class="layer-content"> |
| <h4>Python Backup — On-demand</h4> |
| <p>Atomic snapshots using sqlite3.backup(). Gzip compressed. Consistent while DB is in use.</p> |
| </div> |
| </div> |
| <div class="strategy-layer"> |
| <div class="layer-number">3</div> |
| <div class="layer-content"> |
| <h4>HF Bucket — Persistent</h4> |
| <p>Remote storage on HuggingFace. 100GB free. Survives container restarts.</p> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="m3-card"> |
| <div class="m3-card-header"> |
| <div class="m3-card-icon"> |
| <span class="material-symbols-outlined">api</span> |
| </div> |
| <div> |
| <div class="m3-card-title">API Endpoints</div> |
| <div class="m3-card-subtitle">For programmatic access</div> |
| </div> |
| </div> |
| |
| <div class="m3-code-block">GET /admin/api/diagnostics - Check HF setup |
| POST /admin/api/backup/test - Test backup creation |
| POST /admin/api/backup/upload - Upload to HF Bucket</div> |
| </div> |
| {% endblock %} |
|
|