feat(backup): add database backup/restore with scheduled retention and admin dashboard

- Add BackupRecord model for tracking backup archives
- Add migration 021_add_backup_records
- Add backup configuration settings (backup_enabled, backup_dir, backup_remote_destination, backup_remote_folder, backup_retain_hourly/daily/weekly)
- Add backup_tasks.py with create_backup, cleanup_old_backups, and helpers
- Register hourly/daily/weekly Celery beat schedules
- Add /api/admin/backup/* REST endpoints (list, create, download, restore, delete, cleanup)
- Add /admin/backup dashboard view and template
- Add backup link to admin dropdown navigation in base.html
- Add backup settings to SETTING_METADATA in settings_service.py
- Add comprehensive test suite (39 tests passing)

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-07 22:03:27 +00:00
parent f4a607df05
commit 2dd1ca0197
13 changed files with 1935 additions and 0 deletions
+42
View File
@@ -423,6 +423,48 @@ class Settings(BaseSettings):
description="Enable webhook delivery for document events",
)
# ── Backup / restore settings ──────────────────────────────────────────────
backup_enabled: bool = Field(
default=True,
description=(
"Enable automatic scheduled database backups. "
"When enabled, hourly, daily, and weekly backups are created automatically. Default: True."
),
)
backup_dir: Optional[str] = Field(
default=None,
description=("Directory where local backup archives are stored. Defaults to <workdir>/backups when not set."),
)
# Remote destination: one of s3, dropbox, google_drive, onedrive, nextcloud,
# webdav, ftp, sftp, email, or empty/None for local-only.
backup_remote_destination: Optional[str] = Field(
default=None,
description=(
"Storage provider to upload remote backup copies to. "
"Accepted values: s3, dropbox, google_drive, onedrive, nextcloud, webdav, ftp, sftp, email. "
"Leave empty to keep backups local only."
),
)
backup_remote_folder: str = Field(
default="backups",
description=(
"Sub-folder / key prefix used when uploading backup archives to the remote destination. Default: 'backups'."
),
)
# Retention counts (number of snapshots to keep per tier)
backup_retain_hourly: int = Field(
default=96,
description="Number of hourly backups to retain (default 96 = 4 days × 24 h).",
)
backup_retain_daily: int = Field(
default=21,
description="Number of daily backups to retain (default 21 = 3 weeks).",
)
backup_retain_weekly: int = Field(
default=13,
description="Number of weekly backups to retain (default 13 ≈ 3 months / 91 days).",
)
# File upload size limits (for security - see SECURITY_AUDIT.md)
max_upload_size: int = Field(
default=1073741824, # 1GB in bytes (1024 * 1024 * 1024)