diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ed2321..5fc14f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Changelog +### v0.21.3 — Config Apply Infra Push + Fixes (2026-02-20) +- **Push infra backup after config apply**: After a successful `POST /api/config/apply`, the controller immediately pushes an infra backup to the Hub so the config sync status updates right away. +- **Fix double "v" prefix in startup event**: "Controller elindult (vv0.21.2)" → "Controller elindult (v0.21.3)". + ### v0.21.2 — Config Apply Bind Mount Fix (2026-02-20) - **Fix config apply on Docker bind mounts**: `POST /api/config/apply` failed with "device or resource busy" because `os.Rename()` doesn't work on bind-mounted files. Now falls back to direct write when rename fails. diff --git a/controller/cmd/controller/main.go b/controller/cmd/controller/main.go index 6dc5703..9aacf11 100644 --- a/controller/cmd/controller/main.go +++ b/controller/cmd/controller/main.go @@ -569,6 +569,11 @@ func main() { // --- Initialize API router --- apiRouter := api.NewRouter(cfg, *configPath, sett, stackMgr, syncer, cpuCollector, backupMgr, crossDriveRunner, metricsStore, updater, notifier, logger) + if hubPusher != nil { + apiRouter.OnConfigApplied = func() { + pushInfraBackup(cfg, sett, stackProv, hubPusher, logger) + } + } // --- Initialize web server --- webServer := web.NewServer(cfg, stackMgr, cpuCollector, backupMgr, crossDriveRunner, sched, sett, alertMgr, notifier, updater, logger, Version) diff --git a/controller/internal/api/router.go b/controller/internal/api/router.go index 2bf474e..2574502 100644 --- a/controller/internal/api/router.go +++ b/controller/internal/api/router.go @@ -38,6 +38,9 @@ type Router struct { updater *selfupdate.Updater notifier *notify.Notifier logger *log.Logger + + // OnConfigApplied is called after a successful config apply (e.g., to push infra backup). + OnConfigApplied func() } func NewRouter(cfg *config.Config, configPath string, sett *settings.Settings, stackMgr *stacks.Manager, syncer *catalogsync.Syncer, cpuCollector *system.CPUCollector, backupMgr *backup.Manager, crossDrive *backup.CrossDriveRunner, metricsStore *metrics.MetricsStore, updater *selfupdate.Updater, notif *notify.Notifier, logger *log.Logger) *Router { @@ -976,6 +979,11 @@ func (r *Router) configApply(w http.ResponseWriter, req *http.Request) { r.logger.Printf("[API] Config applied from Hub (%d bytes), restart needed to take effect", len(body)) writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Config applied. Restart controller to apply changes."}) + + // Push updated infra backup so Hub has fresh config data immediately + if r.OnConfigApplied != nil { + go r.OnConfigApplied() + } } func (r *Router) configHash(w http.ResponseWriter, _ *http.Request) { diff --git a/controller/internal/notify/notifier.go b/controller/internal/notify/notifier.go index 31b170b..79aa54e 100644 --- a/controller/internal/notify/notifier.go +++ b/controller/internal/notify/notifier.go @@ -286,7 +286,7 @@ func (n *Notifier) NotifyControllerUpdated(fromVer, toVer string, success bool) // NotifyControllerStarted sends a controller startup event. func (n *Notifier) NotifyControllerStarted(version string) { n.PushEvent("controller_started", "info", - fmt.Sprintf("Controller elindult (v%s)", version), nil) + fmt.Sprintf("Controller elindult (%s)", version), nil) } // NotifyStorageDisconnected sends a drive disconnection event.