feat(backup): take Tinyforge DB snapshot before every deploy

Adds an opt-in "auto_backup_before_deploy" setting that triggers a
"pre-deploy" backup at the start of every project deploy via the deploy
pipeline (covers both the async HTTP path and the sync poller/webhook
path). Failures are logged to the deploy log but do not abort — missing
a backup is preferable to refusing to ship a fix.

- store: settings.auto_backup_before_deploy column + scan/update wiring
- backup: accept "pre-deploy" as a valid backup_type
- deployer: small PreDeployBackuper interface, hooked into runDeploy
  right after settings load and before any state-mutating work
- api: settings request/response surface the new flag
- web: ToggleSwitch on the backup settings page; "Pre-deploy" badge
  variant in the backup list (badge-warning so it stands out)
- i18n: en/ru strings for the toggle, help text, and badge label
This commit is contained in:
2026-05-07 02:14:26 +03:00
parent 0405ecd9ce
commit 8b886ddf2b
11 changed files with 95 additions and 13 deletions
+13 -8
View File
@@ -43,11 +43,12 @@ type settingsRequest struct {
TraefikCertResolver *string `json:"traefik_cert_resolver,omitempty"`
TraefikNetwork *string `json:"traefik_network,omitempty"`
TraefikAPIURL *string `json:"traefik_api_url,omitempty"`
BackupEnabled *bool `json:"backup_enabled,omitempty"`
BackupIntervalHours *int `json:"backup_interval_hours,omitempty"`
BackupRetentionCount *int `json:"backup_retention_count,omitempty"`
StatsIntervalSeconds *int `json:"stats_interval_seconds,omitempty"`
StatsRetentionHours *int `json:"stats_retention_hours,omitempty"`
BackupEnabled *bool `json:"backup_enabled,omitempty"`
BackupIntervalHours *int `json:"backup_interval_hours,omitempty"`
BackupRetentionCount *int `json:"backup_retention_count,omitempty"`
AutoBackupBeforeDeploy *bool `json:"auto_backup_before_deploy,omitempty"`
StatsIntervalSeconds *int `json:"stats_interval_seconds,omitempty"`
StatsRetentionHours *int `json:"stats_retention_hours,omitempty"`
}
// getSettings handles GET /api/settings.
@@ -86,9 +87,10 @@ func (s *Server) getSettings(w http.ResponseWriter, r *http.Request) {
"traefik_cert_resolver": settings.TraefikCertResolver,
"traefik_network": settings.TraefikNetwork,
"traefik_api_url": settings.TraefikAPIURL,
"backup_enabled": settings.BackupEnabled,
"backup_interval_hours": settings.BackupIntervalHours,
"backup_retention_count": settings.BackupRetentionCount,
"backup_enabled": settings.BackupEnabled,
"backup_interval_hours": settings.BackupIntervalHours,
"backup_retention_count": settings.BackupRetentionCount,
"auto_backup_before_deploy": settings.AutoBackupBeforeDeploy,
"stats_interval_seconds": settings.StatsIntervalSeconds,
"stats_retention_hours": settings.StatsRetentionHours,
"updated_at": settings.UpdatedAt,
@@ -243,6 +245,9 @@ func (s *Server) updateSettings(w http.ResponseWriter, r *http.Request) {
}
updated.BackupRetentionCount = *req.BackupRetentionCount
}
if req.AutoBackupBeforeDeploy != nil {
updated.AutoBackupBeforeDeploy = *req.AutoBackupBeforeDeploy
}
if req.StatsIntervalSeconds != nil {
v := *req.StatsIntervalSeconds
if v != 0 && (v < 5 || v > 300) {