From b80971e85ed5ccf64ba282bfdd84c6d9d923ad63 Mon Sep 17 00:00:00 2001 From: chrislu Date: Sat, 26 Jul 2025 22:22:50 -0700 Subject: [PATCH] refactor to use BaseUIProvider --- weed/worker/tasks/balance/ui.go | 119 ++++++-------- weed/worker/tasks/erasure_coding/ui.go | 123 ++++++-------- weed/worker/tasks/ui_base.go | 218 +++++++++++++++++++++++++ weed/worker/tasks/vacuum/ui.go | 136 ++++++--------- 4 files changed, 362 insertions(+), 234 deletions(-) create mode 100644 weed/worker/tasks/ui_base.go diff --git a/weed/worker/tasks/balance/ui.go b/weed/worker/tasks/balance/ui.go index e7c8496f2..373390bf9 100644 --- a/weed/worker/tasks/balance/ui.go +++ b/weed/worker/tasks/balance/ui.go @@ -8,40 +8,6 @@ import ( "github.com/seaweedfs/seaweedfs/weed/worker/types" ) -// UIProvider provides the UI for balance task configuration -type UIProvider struct { - detector *BalanceDetector - scheduler *BalanceScheduler -} - -// NewUIProvider creates a new balance UI provider -func NewUIProvider(detector *BalanceDetector, scheduler *BalanceScheduler) *UIProvider { - return &UIProvider{ - detector: detector, - scheduler: scheduler, - } -} - -// GetTaskType returns the task type -func (ui *UIProvider) GetTaskType() types.TaskType { - return types.TaskTypeBalance -} - -// GetDisplayName returns the human-readable name -func (ui *UIProvider) GetDisplayName() string { - return "Volume Balance" -} - -// GetDescription returns a description of what this task does -func (ui *UIProvider) GetDescription() string { - return "Redistributes volumes across volume servers to optimize storage utilization and performance" -} - -// GetIcon returns the icon CSS class for this task type -func (ui *UIProvider) GetIcon() string { - return "fas fa-balance-scale text-secondary" -} - // BalanceConfig represents the balance configuration matching the schema type BalanceConfig struct { Enabled bool `json:"enabled"` @@ -51,8 +17,22 @@ type BalanceConfig struct { MinServerCount int `json:"min_server_count"` } -// GetCurrentConfig returns the current configuration -func (ui *UIProvider) GetCurrentConfig() interface{} { +// BalanceUILogic contains the business logic for balance UI operations +type BalanceUILogic struct { + detector *BalanceDetector + scheduler *BalanceScheduler +} + +// NewBalanceUILogic creates new balance UI logic +func NewBalanceUILogic(detector *BalanceDetector, scheduler *BalanceScheduler) *BalanceUILogic { + return &BalanceUILogic{ + detector: detector, + scheduler: scheduler, + } +} + +// GetCurrentConfig returns the current balance configuration +func (logic *BalanceUILogic) GetCurrentConfig() interface{} { config := &BalanceConfig{ // Default values from schema (matching task_config_schema.go) Enabled: true, @@ -63,55 +43,40 @@ func (ui *UIProvider) GetCurrentConfig() interface{} { } // Get current values from detector - if ui.detector != nil { - config.Enabled = ui.detector.IsEnabled() - config.ImbalanceThreshold = ui.detector.GetThreshold() - config.ScanIntervalSeconds = int(ui.detector.ScanInterval().Seconds()) + if logic.detector != nil { + config.Enabled = logic.detector.IsEnabled() + config.ImbalanceThreshold = logic.detector.GetThreshold() + config.ScanIntervalSeconds = int(logic.detector.ScanInterval().Seconds()) } // Get current values from scheduler - if ui.scheduler != nil { - config.MaxConcurrent = ui.scheduler.GetMaxConcurrent() - config.MinServerCount = ui.scheduler.GetMinServerCount() + if logic.scheduler != nil { + config.MaxConcurrent = logic.scheduler.GetMaxConcurrent() + config.MinServerCount = logic.scheduler.GetMinServerCount() } return config } -// ApplyConfig applies the new configuration -func (ui *UIProvider) ApplyConfig(config interface{}) error { +// ApplyConfig applies the balance configuration +func (logic *BalanceUILogic) ApplyConfig(config interface{}) error { balanceConfig, ok := config.(*BalanceConfig) if !ok { - // Try to get the configuration from the schema-based system - schema := tasks.GetBalanceTaskConfigSchema() - if schema != nil { - // Apply defaults to ensure we have a complete config - if err := schema.ApplyDefaults(config); err != nil { - return err - } - - // Use reflection to convert to BalanceConfig - simplified approach - if bc, ok := config.(*BalanceConfig); ok { - balanceConfig = bc - } else { - glog.Warningf("Config type conversion failed, using current config") - balanceConfig = ui.GetCurrentConfig().(*BalanceConfig) - } - } + return nil // Will be handled by base provider fallback } // Apply to detector - if ui.detector != nil { - ui.detector.SetEnabled(balanceConfig.Enabled) - ui.detector.SetThreshold(balanceConfig.ImbalanceThreshold) - ui.detector.SetMinCheckInterval(time.Duration(balanceConfig.ScanIntervalSeconds) * time.Second) + if logic.detector != nil { + logic.detector.SetEnabled(balanceConfig.Enabled) + logic.detector.SetThreshold(balanceConfig.ImbalanceThreshold) + logic.detector.SetMinCheckInterval(time.Duration(balanceConfig.ScanIntervalSeconds) * time.Second) } // Apply to scheduler - if ui.scheduler != nil { - ui.scheduler.SetEnabled(balanceConfig.Enabled) - ui.scheduler.SetMaxConcurrent(balanceConfig.MaxConcurrent) - ui.scheduler.SetMinServerCount(balanceConfig.MinServerCount) + if logic.scheduler != nil { + logic.scheduler.SetEnabled(balanceConfig.Enabled) + logic.scheduler.SetMaxConcurrent(balanceConfig.MaxConcurrent) + logic.scheduler.SetMinServerCount(balanceConfig.MinServerCount) } glog.V(1).Infof("Applied balance configuration: enabled=%v, threshold=%.1f%%, max_concurrent=%d, min_servers=%d", @@ -123,10 +88,18 @@ func (ui *UIProvider) ApplyConfig(config interface{}) error { // RegisterUI registers the balance UI provider with the UI registry func RegisterUI(uiRegistry *types.UIRegistry, detector *BalanceDetector, scheduler *BalanceScheduler) { - uiProvider := NewUIProvider(detector, scheduler) - uiRegistry.RegisterUI(uiProvider) - - glog.V(1).Infof("✅ Registered balance task UI provider") + logic := NewBalanceUILogic(detector, scheduler) + + tasks.CommonRegisterUI( + types.TaskTypeBalance, + "Volume Balance", + uiRegistry, + detector, + scheduler, + tasks.GetBalanceTaskConfigSchema, + logic.GetCurrentConfig, + logic.ApplyConfig, + ) } // DefaultBalanceConfig returns default balance configuration diff --git a/weed/worker/tasks/erasure_coding/ui.go b/weed/worker/tasks/erasure_coding/ui.go index d9a10b0b2..192f620a3 100644 --- a/weed/worker/tasks/erasure_coding/ui.go +++ b/weed/worker/tasks/erasure_coding/ui.go @@ -8,40 +8,6 @@ import ( "github.com/seaweedfs/seaweedfs/weed/worker/types" ) -// UIProvider provides the UI for erasure coding task configuration -type UIProvider struct { - detector *EcDetector - scheduler *Scheduler -} - -// NewUIProvider creates a new erasure coding UI provider -func NewUIProvider(detector *EcDetector, scheduler *Scheduler) *UIProvider { - return &UIProvider{ - detector: detector, - scheduler: scheduler, - } -} - -// GetTaskType returns the task type -func (ui *UIProvider) GetTaskType() types.TaskType { - return types.TaskTypeErasureCoding -} - -// GetDisplayName returns the human-readable name -func (ui *UIProvider) GetDisplayName() string { - return "Erasure Coding" -} - -// GetDescription returns a description of what this task does -func (ui *UIProvider) GetDescription() string { - return "Converts volumes to erasure coded format for improved data durability and fault tolerance" -} - -// GetIcon returns the icon CSS class for this task type -func (ui *UIProvider) GetIcon() string { - return "fas fa-shield-alt text-info" -} - // ErasureCodingConfig represents the erasure coding configuration matching the schema type ErasureCodingConfig struct { Enabled bool `json:"enabled"` @@ -52,8 +18,22 @@ type ErasureCodingConfig struct { CollectionFilter string `json:"collection_filter"` } -// GetCurrentConfig returns the current configuration -func (ui *UIProvider) GetCurrentConfig() interface{} { +// ErasureCodingUILogic contains the business logic for erasure coding UI operations +type ErasureCodingUILogic struct { + detector *EcDetector + scheduler *Scheduler +} + +// NewErasureCodingUILogic creates new erasure coding UI logic +func NewErasureCodingUILogic(detector *EcDetector, scheduler *Scheduler) *ErasureCodingUILogic { + return &ErasureCodingUILogic{ + detector: detector, + scheduler: scheduler, + } +} + +// GetCurrentConfig returns the current erasure coding configuration +func (logic *ErasureCodingUILogic) GetCurrentConfig() interface{} { config := ErasureCodingConfig{ // Default values from schema (matching task_config_schema.go) Enabled: true, @@ -65,57 +45,42 @@ func (ui *UIProvider) GetCurrentConfig() interface{} { } // Get current values from detector - if ui.detector != nil { - config.Enabled = ui.detector.IsEnabled() - config.QuietForSeconds = ui.detector.GetQuietForSeconds() - config.FullnessRatio = ui.detector.GetFullnessRatio() - config.CollectionFilter = ui.detector.GetCollectionFilter() - config.ScanIntervalSeconds = int(ui.detector.ScanInterval().Seconds()) + if logic.detector != nil { + config.Enabled = logic.detector.IsEnabled() + config.QuietForSeconds = logic.detector.GetQuietForSeconds() + config.FullnessRatio = logic.detector.GetFullnessRatio() + config.CollectionFilter = logic.detector.GetCollectionFilter() + config.ScanIntervalSeconds = int(logic.detector.ScanInterval().Seconds()) } // Get current values from scheduler - if ui.scheduler != nil { - config.MaxConcurrent = ui.scheduler.GetMaxConcurrent() + if logic.scheduler != nil { + config.MaxConcurrent = logic.scheduler.GetMaxConcurrent() } return config } -// ApplyConfig applies the new configuration -func (ui *UIProvider) ApplyConfig(config interface{}) error { +// ApplyConfig applies the erasure coding configuration +func (logic *ErasureCodingUILogic) ApplyConfig(config interface{}) error { ecConfig, ok := config.(ErasureCodingConfig) if !ok { - // Try to get the configuration from the schema-based system - schema := tasks.GetErasureCodingTaskConfigSchema() - if schema != nil { - // Apply defaults to ensure we have a complete config - if err := schema.ApplyDefaults(config); err != nil { - return err - } - - // Use reflection to convert to ErasureCodingConfig - simplified approach - if ec, ok := config.(ErasureCodingConfig); ok { - ecConfig = ec - } else { - glog.Warningf("Config type conversion failed, using current config") - ecConfig = ui.GetCurrentConfig().(ErasureCodingConfig) - } - } + return nil // Will be handled by base provider fallback } // Apply to detector - if ui.detector != nil { - ui.detector.SetEnabled(ecConfig.Enabled) - ui.detector.SetQuietForSeconds(ecConfig.QuietForSeconds) - ui.detector.SetFullnessRatio(ecConfig.FullnessRatio) - ui.detector.SetCollectionFilter(ecConfig.CollectionFilter) - ui.detector.SetScanInterval(time.Duration(ecConfig.ScanIntervalSeconds) * time.Second) + if logic.detector != nil { + logic.detector.SetEnabled(ecConfig.Enabled) + logic.detector.SetQuietForSeconds(ecConfig.QuietForSeconds) + logic.detector.SetFullnessRatio(ecConfig.FullnessRatio) + logic.detector.SetCollectionFilter(ecConfig.CollectionFilter) + logic.detector.SetScanInterval(time.Duration(ecConfig.ScanIntervalSeconds) * time.Second) } // Apply to scheduler - if ui.scheduler != nil { - ui.scheduler.SetEnabled(ecConfig.Enabled) - ui.scheduler.SetMaxConcurrent(ecConfig.MaxConcurrent) + if logic.scheduler != nil { + logic.scheduler.SetEnabled(ecConfig.Enabled) + logic.scheduler.SetMaxConcurrent(ecConfig.MaxConcurrent) } glog.V(1).Infof("Applied erasure coding configuration: enabled=%v, quiet_for=%v seconds, max_concurrent=%d, fullness_ratio=%f, collection_filter=%s", @@ -126,8 +91,16 @@ func (ui *UIProvider) ApplyConfig(config interface{}) error { // RegisterUI registers the erasure coding UI provider with the UI registry func RegisterUI(uiRegistry *types.UIRegistry, detector *EcDetector, scheduler *Scheduler) { - uiProvider := NewUIProvider(detector, scheduler) - uiRegistry.RegisterUI(uiProvider) - - glog.V(1).Infof("✅ Registered erasure coding task UI provider") + logic := NewErasureCodingUILogic(detector, scheduler) + + tasks.CommonRegisterUI( + types.TaskTypeErasureCoding, + "Erasure Coding", + uiRegistry, + detector, + scheduler, + tasks.GetErasureCodingTaskConfigSchema, + logic.GetCurrentConfig, + logic.ApplyConfig, + ) } diff --git a/weed/worker/tasks/ui_base.go b/weed/worker/tasks/ui_base.go new file mode 100644 index 000000000..4bdeb0956 --- /dev/null +++ b/weed/worker/tasks/ui_base.go @@ -0,0 +1,218 @@ +package tasks + +import ( + "reflect" + + "github.com/seaweedfs/seaweedfs/weed/glog" + "github.com/seaweedfs/seaweedfs/weed/worker/types" +) + +// BaseUIProvider provides common UIProvider functionality for all tasks +type BaseUIProvider struct { + taskType types.TaskType + displayName string + description string + icon string + schemaFunc func() *TaskConfigSchema + configFunc func() interface{} + applyFunc func(config interface{}) error +} + +// NewBaseUIProvider creates a new base UI provider +func NewBaseUIProvider( + taskType types.TaskType, + displayName string, + description string, + icon string, + schemaFunc func() *TaskConfigSchema, + configFunc func() interface{}, + applyFunc func(config interface{}) error, +) *BaseUIProvider { + return &BaseUIProvider{ + taskType: taskType, + displayName: displayName, + description: description, + icon: icon, + schemaFunc: schemaFunc, + configFunc: configFunc, + applyFunc: applyFunc, + } +} + +// GetTaskType returns the task type +func (ui *BaseUIProvider) GetTaskType() types.TaskType { + return ui.taskType +} + +// GetDisplayName returns the human-readable name +func (ui *BaseUIProvider) GetDisplayName() string { + return ui.displayName +} + +// GetDescription returns a description of what this task does +func (ui *BaseUIProvider) GetDescription() string { + return ui.description +} + +// GetIcon returns the icon CSS class for this task type +func (ui *BaseUIProvider) GetIcon() string { + return ui.icon +} + +// GetCurrentConfig returns the current configuration +func (ui *BaseUIProvider) GetCurrentConfig() interface{} { + return ui.configFunc() +} + +// ApplyConfig applies the new configuration with common schema-based handling +func (ui *BaseUIProvider) ApplyConfig(config interface{}) error { + // First try direct application + if err := ui.applyFunc(config); err == nil { + return nil + } + + // Fallback: Try to get the configuration from the schema-based system + schema := ui.schemaFunc() + if schema != nil { + // Apply defaults to ensure we have a complete config + if err := schema.ApplyDefaults(config); err != nil { + return err + } + + // Try again with defaults applied + if err := ui.applyFunc(config); err == nil { + return nil + } + + // Last resort: use current config + glog.Warningf("Config type conversion failed for %s, using current config", ui.taskType) + currentConfig := ui.GetCurrentConfig() + return ui.applyFunc(currentConfig) + } + + return nil +} + +// CommonConfigGetter provides a common pattern for getting current configuration +type CommonConfigGetter[T any] struct { + defaultConfig T + detectorFunc func() T + schedulerFunc func() T +} + +// NewCommonConfigGetter creates a new common config getter +func NewCommonConfigGetter[T any]( + defaultConfig T, + detectorFunc func() T, + schedulerFunc func() T, +) *CommonConfigGetter[T] { + return &CommonConfigGetter[T]{ + defaultConfig: defaultConfig, + detectorFunc: detectorFunc, + schedulerFunc: schedulerFunc, + } +} + +// GetConfig returns the merged configuration +func (cg *CommonConfigGetter[T]) GetConfig() T { + config := cg.defaultConfig + + // Apply detector values if available + if cg.detectorFunc != nil { + detectorConfig := cg.detectorFunc() + mergeConfigs(&config, detectorConfig) + } + + // Apply scheduler values if available + if cg.schedulerFunc != nil { + schedulerConfig := cg.schedulerFunc() + mergeConfigs(&config, schedulerConfig) + } + + return config +} + +// mergeConfigs merges non-zero values from source into dest +func mergeConfigs[T any](dest *T, source T) { + destValue := reflect.ValueOf(dest).Elem() + sourceValue := reflect.ValueOf(source) + + if destValue.Kind() != reflect.Struct || sourceValue.Kind() != reflect.Struct { + return + } + + for i := 0; i < destValue.NumField(); i++ { + destField := destValue.Field(i) + sourceField := sourceValue.Field(i) + + if !destField.CanSet() { + continue + } + + // Only copy non-zero values + if !isZeroValue(sourceField) { + if destField.Type() == sourceField.Type() { + destField.Set(sourceField) + } + } + } +} + +// isZeroValue checks if a reflect.Value represents a zero value +func isZeroValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.String: + return v.String() == "" + case reflect.Slice, reflect.Map, reflect.Array: + return v.IsNil() || v.Len() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + return false +} + +// RegisterUIFunc provides a common registration function signature +type RegisterUIFunc[D, S any] func(uiRegistry *types.UIRegistry, detector D, scheduler S) + +// CommonRegisterUI provides a common registration implementation +func CommonRegisterUI[D, S any]( + taskType types.TaskType, + displayName string, + uiRegistry *types.UIRegistry, + detector D, + scheduler S, + schemaFunc func() *TaskConfigSchema, + configFunc func() interface{}, + applyFunc func(config interface{}) error, +) { + // Get metadata from schema + schema := schemaFunc() + description := "Task configuration" + icon := "fas fa-cog" + + if schema != nil { + description = schema.Description + icon = schema.Icon + } + + uiProvider := NewBaseUIProvider( + taskType, + displayName, + description, + icon, + schemaFunc, + configFunc, + applyFunc, + ) + + uiRegistry.RegisterUI(uiProvider) + glog.V(1).Infof("✅ Registered %s task UI provider", taskType) +} diff --git a/weed/worker/tasks/vacuum/ui.go b/weed/worker/tasks/vacuum/ui.go index 468163c5d..cd161cc98 100644 --- a/weed/worker/tasks/vacuum/ui.go +++ b/weed/worker/tasks/vacuum/ui.go @@ -8,40 +8,6 @@ import ( "github.com/seaweedfs/seaweedfs/weed/worker/types" ) -// UIProvider provides the UI for vacuum task configuration -type UIProvider struct { - detector *VacuumDetector - scheduler *VacuumScheduler -} - -// NewUIProvider creates a new vacuum UI provider -func NewUIProvider(detector *VacuumDetector, scheduler *VacuumScheduler) *UIProvider { - return &UIProvider{ - detector: detector, - scheduler: scheduler, - } -} - -// GetTaskType returns the task type -func (ui *UIProvider) GetTaskType() types.TaskType { - return types.TaskTypeVacuum -} - -// GetDisplayName returns the human-readable name -func (ui *UIProvider) GetDisplayName() string { - return "Volume Vacuum" -} - -// GetDescription returns a description of what this task does -func (ui *UIProvider) GetDescription() string { - return "Reclaims disk space by removing deleted files from volumes" -} - -// GetIcon returns the icon CSS class for this task type -func (ui *UIProvider) GetIcon() string { - return "fas fa-broom text-primary" -} - // VacuumConfig represents the vacuum configuration matching the schema type VacuumConfig struct { Enabled bool `json:"enabled"` @@ -52,8 +18,22 @@ type VacuumConfig struct { MinIntervalSeconds int `json:"min_interval_seconds"` } -// GetCurrentConfig returns the current configuration -func (ui *UIProvider) GetCurrentConfig() interface{} { +// VacuumUILogic contains the business logic for vacuum UI operations +type VacuumUILogic struct { + detector *VacuumDetector + scheduler *VacuumScheduler +} + +// NewVacuumUILogic creates new vacuum UI logic +func NewVacuumUILogic(detector *VacuumDetector, scheduler *VacuumScheduler) *VacuumUILogic { + return &VacuumUILogic{ + detector: detector, + scheduler: scheduler, + } +} + +// GetCurrentConfig returns the current vacuum configuration +func (logic *VacuumUILogic) GetCurrentConfig() interface{} { config := &VacuumConfig{ // Default values from schema (matching task_config_schema.go) Enabled: true, @@ -65,57 +45,42 @@ func (ui *UIProvider) GetCurrentConfig() interface{} { } // Get current values from detector - if ui.detector != nil { - config.Enabled = ui.detector.IsEnabled() - config.GarbageThreshold = ui.detector.GetGarbageThreshold() - config.ScanIntervalSeconds = int(ui.detector.ScanInterval().Seconds()) - config.MinVolumeAgeSeconds = int(ui.detector.GetMinVolumeAge().Seconds()) + if logic.detector != nil { + config.Enabled = logic.detector.IsEnabled() + config.GarbageThreshold = logic.detector.GetGarbageThreshold() + config.ScanIntervalSeconds = int(logic.detector.ScanInterval().Seconds()) + config.MinVolumeAgeSeconds = int(logic.detector.GetMinVolumeAge().Seconds()) } // Get current values from scheduler - if ui.scheduler != nil { - config.MaxConcurrent = ui.scheduler.GetMaxConcurrent() - config.MinIntervalSeconds = int(ui.scheduler.GetMinInterval().Seconds()) + if logic.scheduler != nil { + config.MaxConcurrent = logic.scheduler.GetMaxConcurrent() + config.MinIntervalSeconds = int(logic.scheduler.GetMinInterval().Seconds()) } return config } -// ApplyConfig applies the new configuration -func (ui *UIProvider) ApplyConfig(config interface{}) error { +// ApplyConfig applies the vacuum configuration +func (logic *VacuumUILogic) ApplyConfig(config interface{}) error { vacuumConfig, ok := config.(*VacuumConfig) if !ok { - // Try to get the configuration from the schema-based system - schema := tasks.GetVacuumTaskConfigSchema() - if schema != nil { - // Apply defaults to ensure we have a complete config - if err := schema.ApplyDefaults(config); err != nil { - return err - } - - // Use reflection to convert to VacuumConfig - simplified approach - if vc, ok := config.(*VacuumConfig); ok { - vacuumConfig = vc - } else { - glog.Warningf("Config type conversion failed, using current config") - vacuumConfig = ui.GetCurrentConfig().(*VacuumConfig) - } - } + return nil // Will be handled by base provider fallback } // Apply to detector - if ui.detector != nil { - ui.detector.SetEnabled(vacuumConfig.Enabled) - ui.detector.SetGarbageThreshold(vacuumConfig.GarbageThreshold) - ui.detector.SetScanInterval(time.Duration(vacuumConfig.ScanIntervalSeconds) * time.Second) - ui.detector.SetMinVolumeAge(time.Duration(vacuumConfig.MinVolumeAgeSeconds) * time.Second) + if logic.detector != nil { + logic.detector.SetEnabled(vacuumConfig.Enabled) + logic.detector.SetGarbageThreshold(vacuumConfig.GarbageThreshold) + logic.detector.SetScanInterval(time.Duration(vacuumConfig.ScanIntervalSeconds) * time.Second) + logic.detector.SetMinVolumeAge(time.Duration(vacuumConfig.MinVolumeAgeSeconds) * time.Second) } // Apply to scheduler - if ui.scheduler != nil { - ui.scheduler.SetEnabled(vacuumConfig.Enabled) - ui.scheduler.SetMaxConcurrent(vacuumConfig.MaxConcurrent) - ui.scheduler.SetMinInterval(time.Duration(vacuumConfig.MinIntervalSeconds) * time.Second) + if logic.scheduler != nil { + logic.scheduler.SetEnabled(vacuumConfig.Enabled) + logic.scheduler.SetMaxConcurrent(vacuumConfig.MaxConcurrent) + logic.scheduler.SetMinInterval(time.Duration(vacuumConfig.MinIntervalSeconds) * time.Second) } glog.V(1).Infof("Applied vacuum configuration: enabled=%v, threshold=%.1f%%, scan_interval=%ds, max_concurrent=%d", @@ -126,22 +91,21 @@ func (ui *UIProvider) ApplyConfig(config interface{}) error { // RegisterUI registers the vacuum UI provider with the UI registry func RegisterUI(uiRegistry *types.UIRegistry, detector *VacuumDetector, scheduler *VacuumScheduler) { - uiProvider := NewUIProvider(detector, scheduler) - uiRegistry.RegisterUI(uiProvider) - - glog.V(1).Infof("✅ Registered vacuum task UI provider") + logic := NewVacuumUILogic(detector, scheduler) + + tasks.CommonRegisterUI( + types.TaskTypeVacuum, + "Volume Vacuum", + uiRegistry, + detector, + scheduler, + tasks.GetVacuumTaskConfigSchema, + logic.GetCurrentConfig, + logic.ApplyConfig, + ) } // GetUIProvider returns the UI provider for external use -func GetUIProvider(uiRegistry *types.UIRegistry) *UIProvider { - provider := uiRegistry.GetProvider(types.TaskTypeVacuum) - if provider == nil { - return nil - } - - if vacuumProvider, ok := provider.(*UIProvider); ok { - return vacuumProvider - } - - return nil +func GetUIProvider(uiRegistry *types.UIRegistry) types.TaskUIProvider { + return uiRegistry.GetProvider(types.TaskTypeVacuum) }