3x-ui/docs/superpowers/specs/2026-04-26-database-backup-snapshot-design.md

240 lines
8.5 KiB
Markdown
Raw Normal View History

# Database Backup & Snapshot Design
Date: 2026-04-26
Related Module: database, web, x-ui.sh
Change Type: Feature
## Overview
Add backup, scheduled snapshot, export (download), and restore functionality for both SQLite and MariaDB databases to the 3X-UI panel. Operable via the web panel UI and x-ui.sh CLI.
## Scope
- MariaDB backup/restore (primary goal)
- SQLite backup/restore (unified with MariaDB under the new system)
- Scheduled automatic snapshots (configurable frequency)
- Manual ad-hoc backups
- Backup export/download via panel
- Restore from backup via panel or CLI
- Backup retention policy (keep last N backups)
- Existing `getDb`/`importDB` endpoints remain unchanged for SQLite raw .db file operations
## Architecture
### New Files
| File | Purpose |
|------|---------|
| `web/service/backup.go` | Backup service layer: mysqldump/sqlite3 dump execution, gzip, file management, scheduling |
| `web/controller/backup.go` | Backup API controller (HTTP handlers) |
| `web/html/settings/backup.html` | Backup management page (new tab in settings) |
| `web/job/backup_job.go` | Scheduled backup cron job |
### Modified Files
| File | Change |
|------|--------|
| `web/html/settings.html` | Add "Backup" tab entry |
| `web/job/job.go` | Register new backup cron job |
| `web/service/setting.go` | Add backup config read/write to AllSetting |
| `web/entity/entity.go` | Add backup config fields to AllSetting |
| `x-ui.sh` | Add `backup`/`restore`/`list-backups` subcommands; add backup menu items to `db_menu` |
| `main.go` | Register `backup` and `restore` CLI subcommands |
### Dependency Graph
```
Settings Page (backup.html)
↓ POST/GET
BackupController (web/controller/backup.go)
BackupService (web/service/backup.go)
↓ exec.Command("mysqldump") / exec.Command("sqlite3", ".dump")
MariaDB / SQLite Server
↓ file I/O
/etc/x-ui/backups/ (tar.gz files)
```
Scheduling:
```
web/job/backup_job.go → BackupService.CreateSnapshot()
↑ reads schedule config
web/service/setting.go (backup config)
```
## API Endpoints
All endpoints under `/panel/api/server`, registered in `web/controller/backup.go`. Admin authentication required for all.
| Method | Route | Purpose |
|--------|-------|---------|
| POST | `/panel/api/server/backup` | Create an immediate manual backup |
| POST | `/panel/api/server/restore/:filename` | Restore from a specified backup file |
| POST | `/panel/api/server/deleteBackup/:filename` | Delete a specified backup file |
| GET | `/panel/api/server/listBackups` | List all backups (filename, size, timestamp) |
| GET | `/panel/api/server/downloadBackup/:filename` | Download a specified backup file |
| POST | `/panel/api/server/backupConfig` | Update backup configuration |
Response format: standard `Msg{Success, Msg, Obj}` JSON.
## Backup File Format
All backups use `.tar.gz` archives with a consistent internal structure regardless of database type.
### Storage
- Directory: `/etc/x-ui/backups/`
- Filename: `backup-YYYY-MM-DD-HHmmss.tar.gz`
### Archive Contents
```
backup-2026-04-26-030000.tar.gz
├── metadata.json # {"dbType":"mariadb"|"sqlite","timestamp":"RFC3339","version":"v1.7.2.x"}
└── dump.sql # mysqldump output (MariaDB) or sqlite3 .dump output (SQLite)
```
### SQLite Backup Process
1. `database.Checkpoint()` to flush WAL
2. `exec.Command("sqlite3", dbPath, ".dump")` to generate SQL dump
3. Create metadata.json with `dbType: "sqlite"`
4. Bundle into tar.gz
### MariaDB Backup Process
1. Build DSN from config
2. `exec.Command("mysqldump", "--single-transaction", "--routines", "--triggers", ...)`
3. Create metadata.json with `dbType: "mariadb"`
4. Bundle into tar.gz
## Restore Logic
1. Read `metadata.json` from the archive to extract `dbType`
2. Compare `dbType` with current panel database type
3. If mismatch → reject with error: "Backup type (X) does not match current database (Y)"
4. If match → create a safety backup of current database before proceeding
5. Stop panel → restore → restart panel
6. Restoration:
- SQLite: drop all tables, import `.dump` via sqlite3
- MariaDB: drop database, recreate, import via `mysql` client
### Restore Flow
```
Read metadata.json → validate dbType match
→ create safety backup of current DB
→ systemctl stop x-ui (or internal stop)
→ execute restore (sqlite3 or mysql client)
→ systemctl start x-ui (or internal restart)
→ verify panel accessible
```
Safety backup filename pattern: `pre-restore-YYYY-MM-DD-HHmmss.tar.gz`. These are excluded from the retention count and auto-deleted after 7 days.
## Retention Policy
- Configurable via `max_backups` setting (default: 10, range: 1-100)
- After each backup creation, if total count exceeds `max_backups`, delete oldest files first
- Manual and scheduled backups share the same retention pool
- Space protection: refuse new backup if disk free space < 100 MB or backup directory total > 500 MB
## Scheduled Snapshots
Configuration fields in AllSetting:
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `backupEnabled` | bool | false | Enable scheduled backups |
| `backupFrequency` | string | "daily" | "hourly", "every12h", "daily", "weekly" |
| `backupHour` | int | 3 | Hour of day (0-23), used by daily and weekly |
| `backupMaxCount` | int | 10 | Max backups to retain |
### Schedule Runtime
| Frequency | When it runs |
|-----------|-------------|
| `hourly` | Minute 0 of every hour |
| `every12h` | 00:00 and 12:00 |
| `daily` | `backupHour:00` each day |
| `weekly` | Sunday at `backupHour:00` |
Scheduling logic in `web/job/backup_job.go`:
- Check `backupEnabled` on each tick
- Execute backup via `BackupService.CreateSnapshot()`
- Apply retention after each successful backup
- Log errors (do not crash the panel)
## Panel UI Design
### Location
New "Backup" tab in the settings page (`settings.html`).
### Layout
**Scheduled Backup Section:**
- Enable toggle switch
- Frequency dropdown: Every Hour / Every 12 Hours / Every Day / Every Week
- Hour picker (visible when daily or weekly selected)
- Max backups input (1-100)
- Save button
**Manual Operations Section:**
- "Create Backup Now" button
**Backup List Section:**
- Table with columns: Filename, Timestamp, Size, Actions
- Action buttons per row: Download, Restore, Delete
- Restore shows a warning confirmation dialog
- Delete requires secondary confirmation
- Auto-refresh every 30 seconds
## x-ui.sh CLI
### New Subcommands
| Command | Purpose |
|---------|---------|
| `x-ui backup` | Create an immediate manual backup |
| `x-ui restore <filename>` | Restore from `/etc/x-ui/backups/<filename>` |
| `x-ui list-backups` | List all backups with size and timestamp |
### db_menu Additions (menu 27)
New items after existing 1-16:
| # | Label | Action |
|---|-------|--------|
| 17 | Create database backup | Call `x-ui backup` |
| 18 | Restore from backup | Interactive file selection, then restore |
| 19 | List all backups | Show backup list |
| 20 | Configure auto-backup | Interactive config (frequency, retention) |
### Implementation
- `backup` and `restore` subcommands delegate to Go binary: `${xui_folder}/x-ui backup` / `${xui_folder}/x-ui restore --file=<name>`
- `list-backups` uses `ls -lh /etc/x-ui/backups/`
- Restore flow in x-ui.sh: validate → confirm → `systemctl stop x-ui``${xui_folder}/x-ui restore``systemctl start x-ui`
## Error Handling
- `mysqldump` or `sqlite3` not found on system → clear error message with install instructions
- Disk full → reject backup, notify user
- Backup file corrupted (invalid tar.gz or missing metadata.json) → reject restore
- Database type mismatch → reject with clear message
- Panel not stopped cleanly before restore → timeout and manual recovery instructions
## Testing
- Unit tests for `BackupService` methods (mock exec.Command where possible)
- Integration test: create backup → verify contents → restore → verify data integrity
- Test both SQLite and MariaDB paths
- Test retention policy (create N+1 backups, verify oldest deleted)
- Test dbType mismatch rejection
## Risks And Follow-Up
- `mysqldump` may not be installed on all systems; x-ui.sh already has `install_mariadb_client` that can install it. Panel UI should detect and surface this.
- Large database backups may take significant time; UI should show progress or timeout gracefully.
- Restore requires panel downtime (stop/start). This is communicated to the user before confirmation.
- Existing `getDb`/`importDB` endpoints remain unchanged for backward compatibility.