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:
@@ -32,6 +32,7 @@ type Deployer struct {
|
||||
health *health.Checker
|
||||
notifier *notify.Notifier
|
||||
eventBus EventPublisher
|
||||
backuper PreDeployBackuper // optional; nil disables pre-deploy backups
|
||||
encKey [32]byte
|
||||
dnsMu sync.RWMutex
|
||||
dns dns.Provider // nil when wildcard DNS is active
|
||||
@@ -46,6 +47,13 @@ type EventPublisher interface {
|
||||
Publish(evt events.Event)
|
||||
}
|
||||
|
||||
// PreDeployBackuper takes a "pre-deploy" Tinyforge DB snapshot before any
|
||||
// deploy starts when the corresponding setting is enabled. Kept as a small
|
||||
// interface so the deployer does not import internal/backup.
|
||||
type PreDeployBackuper interface {
|
||||
CreateBackup(backupType string) (store.Backup, error)
|
||||
}
|
||||
|
||||
// New creates a new Deployer with all required dependencies.
|
||||
func New(
|
||||
dockerClient *docker.Client,
|
||||
@@ -72,6 +80,30 @@ func (d *Deployer) SetProxyProvider(provider proxy.Provider) {
|
||||
d.proxy = provider
|
||||
}
|
||||
|
||||
// SetPreDeployBackuper wires the backup engine in after construction so the
|
||||
// deployer can take a Tinyforge DB snapshot when the
|
||||
// auto_backup_before_deploy setting is enabled. Pass nil to disable.
|
||||
func (d *Deployer) SetPreDeployBackuper(b PreDeployBackuper) {
|
||||
d.backuper = b
|
||||
}
|
||||
|
||||
// maybeBackupBeforeDeploy creates a "pre-deploy" Tinyforge DB snapshot when
|
||||
// the setting is enabled. Failures are logged but do not abort the deploy:
|
||||
// missing a backup is preferable to refusing to ship a fix.
|
||||
func (d *Deployer) maybeBackupBeforeDeploy(deployID string, settings store.Settings) {
|
||||
if !settings.AutoBackupBeforeDeploy || d.backuper == nil {
|
||||
return
|
||||
}
|
||||
backup, err := d.backuper.CreateBackup("pre-deploy")
|
||||
if err != nil {
|
||||
slog.Warn("pre-deploy backup failed", "deploy_id", deployID, "error", err)
|
||||
d.logDeploy(deployID, fmt.Sprintf("Pre-deploy backup failed: %v", err), "warn")
|
||||
return
|
||||
}
|
||||
slog.Info("pre-deploy backup created", "deploy_id", deployID, "backup_id", backup.ID, "filename", backup.Filename)
|
||||
d.logDeploy(deployID, fmt.Sprintf("Pre-deploy backup created: %s", backup.Filename), "info")
|
||||
}
|
||||
|
||||
// SetDNSProvider sets the DNS provider for managing DNS records during deployments.
|
||||
// Pass nil to disable DNS management (wildcard DNS mode).
|
||||
func (d *Deployer) SetDNSProvider(provider dns.Provider) {
|
||||
@@ -160,6 +192,10 @@ func (d *Deployer) runDeploy(ctx context.Context, project store.Project, stage s
|
||||
)
|
||||
d.logDeploy(deployID, fmt.Sprintf("Starting deploy of %s:%s for project %s, stage %s", project.Image, imageTag, project.Name, stage.Name), "info")
|
||||
|
||||
// Take a pre-deploy DB snapshot if the operator opted in. Runs before
|
||||
// any state-mutating work so a corrupted deploy is recoverable.
|
||||
d.maybeBackupBeforeDeploy(deployID, settings)
|
||||
|
||||
// Enforce max_instances before deploying.
|
||||
if err := d.enforceMaxInstances(ctx, stage, deployID, settings); err != nil {
|
||||
d.logDeploy(deployID, fmt.Sprintf("Failed to enforce max instances: %v", err), "error")
|
||||
|
||||
Reference in New Issue
Block a user