You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							383 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							383 lines
						
					
					
						
							12 KiB
						
					
					
				| package handlers | |
| 
 | |
| import ( | |
| 	"net/http" | |
| 	"time" | |
| 
 | |
| 	"github.com/gin-gonic/gin" | |
| 	"github.com/seaweedfs/seaweedfs/weed/admin/dash" | |
| 	"github.com/seaweedfs/seaweedfs/weed/admin/maintenance" | |
| 	"github.com/seaweedfs/seaweedfs/weed/admin/view/app" | |
| 	"github.com/seaweedfs/seaweedfs/weed/admin/view/components" | |
| 	"github.com/seaweedfs/seaweedfs/weed/admin/view/layout" | |
| 	"github.com/seaweedfs/seaweedfs/weed/worker/tasks" | |
| 	"github.com/seaweedfs/seaweedfs/weed/worker/types" | |
| ) | |
| 
 | |
| // MaintenanceHandlers handles maintenance-related HTTP requests | |
| type MaintenanceHandlers struct { | |
| 	adminServer *dash.AdminServer | |
| } | |
| 
 | |
| // NewMaintenanceHandlers creates a new instance of MaintenanceHandlers | |
| func NewMaintenanceHandlers(adminServer *dash.AdminServer) *MaintenanceHandlers { | |
| 	return &MaintenanceHandlers{ | |
| 		adminServer: adminServer, | |
| 	} | |
| } | |
| 
 | |
| // ShowMaintenanceQueue displays the maintenance queue page | |
| func (h *MaintenanceHandlers) ShowMaintenanceQueue(c *gin.Context) { | |
| 	data, err := h.getMaintenanceQueueData() | |
| 	if err != nil { | |
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | |
| 		return | |
| 	} | |
| 
 | |
| 	// Render HTML template | |
| 	c.Header("Content-Type", "text/html") | |
| 	maintenanceComponent := app.MaintenanceQueue(data) | |
| 	layoutComponent := layout.Layout(c, maintenanceComponent) | |
| 	err = layoutComponent.Render(c.Request.Context(), c.Writer) | |
| 	if err != nil { | |
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to render template: " + err.Error()}) | |
| 		return | |
| 	} | |
| } | |
| 
 | |
| // ShowMaintenanceWorkers displays the maintenance workers page | |
| func (h *MaintenanceHandlers) ShowMaintenanceWorkers(c *gin.Context) { | |
| 	workersData, err := h.adminServer.GetMaintenanceWorkersData() | |
| 	if err != nil { | |
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | |
| 		return | |
| 	} | |
| 
 | |
| 	// Render HTML template | |
| 	c.Header("Content-Type", "text/html") | |
| 	workersComponent := app.MaintenanceWorkers(workersData) | |
| 	layoutComponent := layout.Layout(c, workersComponent) | |
| 	err = layoutComponent.Render(c.Request.Context(), c.Writer) | |
| 	if err != nil { | |
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to render template: " + err.Error()}) | |
| 		return | |
| 	} | |
| } | |
| 
 | |
| // ShowMaintenanceConfig displays the maintenance configuration page | |
| func (h *MaintenanceHandlers) ShowMaintenanceConfig(c *gin.Context) { | |
| 	config, err := h.getMaintenanceConfig() | |
| 	if err != nil { | |
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | |
| 		return | |
| 	} | |
| 
 | |
| 	// Render HTML template | |
| 	c.Header("Content-Type", "text/html") | |
| 	configComponent := app.MaintenanceConfig(config) | |
| 	layoutComponent := layout.Layout(c, configComponent) | |
| 	err = layoutComponent.Render(c.Request.Context(), c.Writer) | |
| 	if err != nil { | |
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to render template: " + err.Error()}) | |
| 		return | |
| 	} | |
| } | |
| 
 | |
| // ShowTaskConfig displays the configuration page for a specific task type | |
| func (h *MaintenanceHandlers) ShowTaskConfig(c *gin.Context) { | |
| 	taskTypeName := c.Param("taskType") | |
| 
 | |
| 	// Get the task type | |
| 	taskType := maintenance.GetMaintenanceTaskType(taskTypeName) | |
| 	if taskType == "" { | |
| 		c.JSON(http.StatusNotFound, gin.H{"error": "Task type not found"}) | |
| 		return | |
| 	} | |
| 
 | |
| 	// Get the UI provider for this task type | |
| 	uiRegistry := tasks.GetGlobalUIRegistry() | |
| 	typesRegistry := tasks.GetGlobalTypesRegistry() | |
| 
 | |
| 	var provider types.TaskUIProvider | |
| 	for workerTaskType := range typesRegistry.GetAllDetectors() { | |
| 		if string(workerTaskType) == string(taskType) { | |
| 			provider = uiRegistry.GetProvider(workerTaskType) | |
| 			break | |
| 		} | |
| 	} | |
| 
 | |
| 	if provider == nil { | |
| 		c.JSON(http.StatusNotFound, gin.H{"error": "UI provider not found for task type"}) | |
| 		return | |
| 	} | |
| 
 | |
| 	// Try to get templ UI provider first - temporarily disabled | |
| 	// templUIProvider := getTemplUIProvider(taskType) | |
| 	var configSections []components.ConfigSectionData | |
| 
 | |
| 	// Temporarily disabled templ UI provider | |
| 	// if templUIProvider != nil { | |
| 	//	// Use the new templ-based UI provider | |
| 	//	currentConfig := templUIProvider.GetCurrentConfig() | |
| 	//	sections, err := templUIProvider.RenderConfigSections(currentConfig) | |
| 	//	if err != nil { | |
| 	//		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to render configuration sections: " + err.Error()}) | |
| 	//		return | |
| 	//	} | |
| 	//	configSections = sections | |
| 	// } else { | |
| 	// Fallback to basic configuration for providers that haven't been migrated yet | |
| 	configSections = []components.ConfigSectionData{ | |
| 		{ | |
| 			Title:       "Configuration Settings", | |
| 			Icon:        "fas fa-cogs", | |
| 			Description: "Configure task detection and scheduling parameters", | |
| 			Fields: []interface{}{ | |
| 				components.CheckboxFieldData{ | |
| 					FormFieldData: components.FormFieldData{ | |
| 						Name:        "enabled", | |
| 						Label:       "Enable Task", | |
| 						Description: "Whether this task type should be enabled", | |
| 					}, | |
| 					Checked: true, | |
| 				}, | |
| 				components.NumberFieldData{ | |
| 					FormFieldData: components.FormFieldData{ | |
| 						Name:        "max_concurrent", | |
| 						Label:       "Max Concurrent Tasks", | |
| 						Description: "Maximum number of concurrent tasks", | |
| 						Required:    true, | |
| 					}, | |
| 					Value: 2, | |
| 					Step:  "1", | |
| 					Min:   floatPtr(1), | |
| 				}, | |
| 				components.DurationFieldData{ | |
| 					FormFieldData: components.FormFieldData{ | |
| 						Name:        "scan_interval", | |
| 						Label:       "Scan Interval", | |
| 						Description: "How often to scan for tasks", | |
| 						Required:    true, | |
| 					}, | |
| 					Value: "30m", | |
| 				}, | |
| 			}, | |
| 		}, | |
| 	} | |
| 	// } // End of disabled templ UI provider else block | |
|  | |
| 	// Create task configuration data using templ components | |
| 	configData := &app.TaskConfigTemplData{ | |
| 		TaskType:       taskType, | |
| 		TaskName:       provider.GetDisplayName(), | |
| 		TaskIcon:       provider.GetIcon(), | |
| 		Description:    provider.GetDescription(), | |
| 		ConfigSections: configSections, | |
| 	} | |
| 
 | |
| 	// Render HTML template using templ components | |
| 	c.Header("Content-Type", "text/html") | |
| 	taskConfigComponent := app.TaskConfigTempl(configData) | |
| 	layoutComponent := layout.Layout(c, taskConfigComponent) | |
| 	err := layoutComponent.Render(c.Request.Context(), c.Writer) | |
| 	if err != nil { | |
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to render template: " + err.Error()}) | |
| 		return | |
| 	} | |
| } | |
| 
 | |
| // UpdateTaskConfig updates configuration for a specific task type | |
| func (h *MaintenanceHandlers) UpdateTaskConfig(c *gin.Context) { | |
| 	taskTypeName := c.Param("taskType") | |
| 
 | |
| 	// Get the task type | |
| 	taskType := maintenance.GetMaintenanceTaskType(taskTypeName) | |
| 	if taskType == "" { | |
| 		c.JSON(http.StatusNotFound, gin.H{"error": "Task type not found"}) | |
| 		return | |
| 	} | |
| 
 | |
| 	// Try to get templ UI provider first - temporarily disabled | |
| 	// templUIProvider := getTemplUIProvider(taskType) | |
|  | |
| 	// Parse form data | |
| 	err := c.Request.ParseForm() | |
| 	if err != nil { | |
| 		c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to parse form data: " + err.Error()}) | |
| 		return | |
| 	} | |
| 
 | |
| 	// Convert form data to map | |
| 	formData := make(map[string][]string) | |
| 	for key, values := range c.Request.PostForm { | |
| 		formData[key] = values | |
| 	} | |
| 
 | |
| 	var config interface{} | |
| 
 | |
| 	// Temporarily disabled templ UI provider | |
| 	// if templUIProvider != nil { | |
| 	//	// Use the new templ-based UI provider | |
| 	//	config, err = templUIProvider.ParseConfigForm(formData) | |
| 	//	if err != nil { | |
| 	//		c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to parse configuration: " + err.Error()}) | |
| 	//		return | |
| 	//	} | |
| 	//	// Apply configuration using templ provider | |
| 	//	err = templUIProvider.ApplyConfig(config) | |
| 	//	if err != nil { | |
| 	//		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to apply configuration: " + err.Error()}) | |
| 	//		return | |
| 	//	} | |
| 	// } else { | |
| 	// Fallback to old UI provider for tasks that haven't been migrated yet | |
| 	// Fallback to old UI provider for tasks that haven't been migrated yet | |
| 	uiRegistry := tasks.GetGlobalUIRegistry() | |
| 	typesRegistry := tasks.GetGlobalTypesRegistry() | |
| 
 | |
| 	var provider types.TaskUIProvider | |
| 	for workerTaskType := range typesRegistry.GetAllDetectors() { | |
| 		if string(workerTaskType) == string(taskType) { | |
| 			provider = uiRegistry.GetProvider(workerTaskType) | |
| 			break | |
| 		} | |
| 	} | |
| 
 | |
| 	if provider == nil { | |
| 		c.JSON(http.StatusNotFound, gin.H{"error": "UI provider not found for task type"}) | |
| 		return | |
| 	} | |
| 
 | |
| 	// Parse configuration from form using old provider | |
| 	config, err = provider.ParseConfigForm(formData) | |
| 	if err != nil { | |
| 		c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to parse configuration: " + err.Error()}) | |
| 		return | |
| 	} | |
| 
 | |
| 	// Apply configuration using old provider | |
| 	err = provider.ApplyConfig(config) | |
| 	if err != nil { | |
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to apply configuration: " + err.Error()}) | |
| 		return | |
| 	} | |
| 	// } // End of disabled templ UI provider else block | |
|  | |
| 	// Redirect back to task configuration page | |
| 	c.Redirect(http.StatusSeeOther, "/maintenance/config/"+taskTypeName) | |
| } | |
| 
 | |
| // UpdateMaintenanceConfig updates maintenance configuration from form | |
| func (h *MaintenanceHandlers) UpdateMaintenanceConfig(c *gin.Context) { | |
| 	var config maintenance.MaintenanceConfig | |
| 	if err := c.ShouldBind(&config); err != nil { | |
| 		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | |
| 		return | |
| 	} | |
| 
 | |
| 	err := h.updateMaintenanceConfig(&config) | |
| 	if err != nil { | |
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | |
| 		return | |
| 	} | |
| 
 | |
| 	c.Redirect(http.StatusSeeOther, "/maintenance/config") | |
| } | |
| 
 | |
| // Helper methods that delegate to AdminServer | |
|  | |
| func (h *MaintenanceHandlers) getMaintenanceQueueData() (*maintenance.MaintenanceQueueData, error) { | |
| 	tasks, err := h.getMaintenanceTasks() | |
| 	if err != nil { | |
| 		return nil, err | |
| 	} | |
| 
 | |
| 	workers, err := h.getMaintenanceWorkers() | |
| 	if err != nil { | |
| 		return nil, err | |
| 	} | |
| 
 | |
| 	stats, err := h.getMaintenanceQueueStats() | |
| 	if err != nil { | |
| 		return nil, err | |
| 	} | |
| 
 | |
| 	return &maintenance.MaintenanceQueueData{ | |
| 		Tasks:       tasks, | |
| 		Workers:     workers, | |
| 		Stats:       stats, | |
| 		LastUpdated: time.Now(), | |
| 	}, nil | |
| } | |
| 
 | |
| func (h *MaintenanceHandlers) getMaintenanceQueueStats() (*maintenance.QueueStats, error) { | |
| 	// This would integrate with the maintenance queue to get real statistics | |
| 	// For now, return mock data | |
| 	return &maintenance.QueueStats{ | |
| 		PendingTasks:   5, | |
| 		RunningTasks:   2, | |
| 		CompletedToday: 15, | |
| 		FailedToday:    1, | |
| 		TotalTasks:     23, | |
| 	}, nil | |
| } | |
| 
 | |
| func (h *MaintenanceHandlers) getMaintenanceTasks() ([]*maintenance.MaintenanceTask, error) { | |
| 	// This would integrate with the maintenance queue to get real tasks | |
| 	// For now, return mock data | |
| 	return []*maintenance.MaintenanceTask{}, nil | |
| } | |
| 
 | |
| func (h *MaintenanceHandlers) getMaintenanceWorkers() ([]*maintenance.MaintenanceWorker, error) { | |
| 	// This would integrate with the maintenance system to get real workers | |
| 	// For now, return mock data | |
| 	return []*maintenance.MaintenanceWorker{}, nil | |
| } | |
| 
 | |
| func (h *MaintenanceHandlers) getMaintenanceConfig() (*maintenance.MaintenanceConfigData, error) { | |
| 	// Delegate to AdminServer's real persistence method | |
| 	return h.adminServer.GetMaintenanceConfigData() | |
| } | |
| 
 | |
| func (h *MaintenanceHandlers) updateMaintenanceConfig(config *maintenance.MaintenanceConfig) error { | |
| 	// Delegate to AdminServer's real persistence method | |
| 	return h.adminServer.UpdateMaintenanceConfigData(config) | |
| } | |
| 
 | |
| // floatPtr is a helper function to create float64 pointers | |
| func floatPtr(f float64) *float64 { | |
| 	return &f | |
| } | |
| 
 | |
| // Global templ UI registry - temporarily disabled | |
| // var globalTemplUIRegistry *types.UITemplRegistry | |
|  | |
| // initTemplUIRegistry initializes the global templ UI registry - temporarily disabled | |
| func initTemplUIRegistry() { | |
| 	// Temporarily disabled due to missing types | |
| 	// if globalTemplUIRegistry == nil { | |
| 	//	globalTemplUIRegistry = types.NewUITemplRegistry() | |
| 	//	// Register vacuum templ UI provider using shared instances | |
| 	//	vacuumDetector, vacuumScheduler := vacuum.GetSharedInstances() | |
| 	//	vacuum.RegisterUITempl(globalTemplUIRegistry, vacuumDetector, vacuumScheduler) | |
| 	//	// Register erasure coding templ UI provider using shared instances | |
| 	//	erasureCodingDetector, erasureCodingScheduler := erasure_coding.GetSharedInstances() | |
| 	//	erasure_coding.RegisterUITempl(globalTemplUIRegistry, erasureCodingDetector, erasureCodingScheduler) | |
| 	//	// Register balance templ UI provider using shared instances | |
| 	//	balanceDetector, balanceScheduler := balance.GetSharedInstances() | |
| 	//	balance.RegisterUITempl(globalTemplUIRegistry, balanceDetector, balanceScheduler) | |
| 	// } | |
| } | |
| 
 | |
| // getTemplUIProvider gets the templ UI provider for a task type - temporarily disabled | |
| func getTemplUIProvider(taskType maintenance.MaintenanceTaskType) interface{} { | |
| 	// initTemplUIRegistry() | |
| 	// Convert maintenance task type to worker task type | |
| 	// typesRegistry := tasks.GetGlobalTypesRegistry() | |
| 	// for workerTaskType := range typesRegistry.GetAllDetectors() { | |
| 	//	if string(workerTaskType) == string(taskType) { | |
| 	//		return globalTemplUIRegistry.GetProvider(workerTaskType) | |
| 	//	} | |
| 	// } | |
| 	return nil | |
| }
 |