package app import ( "encoding/base64" "encoding/json" "fmt" "reflect" "strings" "github.com/seaweedfs/seaweedfs/weed/admin/maintenance" "github.com/seaweedfs/seaweedfs/weed/worker/tasks" "github.com/seaweedfs/seaweedfs/weed/admin/config" "github.com/seaweedfs/seaweedfs/weed/admin/view/components" ) // Helper function to convert task schema to JSON string func taskSchemaToJSON(schema *tasks.TaskConfigSchema) string { if schema == nil { return "{}" } data := map[string]interface{}{ "fields": schema.Fields, } jsonBytes, err := json.Marshal(data) if err != nil { return "{}" } return string(jsonBytes) } // Helper function to base64 encode the JSON to avoid HTML escaping issues func taskSchemaToBase64JSON(schema *tasks.TaskConfigSchema) string { jsonStr := taskSchemaToJSON(schema) return base64.StdEncoding.EncodeToString([]byte(jsonStr)) } templ TaskConfigSchema(data *maintenance.TaskConfigData, schema *tasks.TaskConfigSchema, config interface{}) {

{schema.DisplayName} Configuration

Task Configuration

{schema.Description}

for _, field := range schema.Fields { @TaskConfigField(field, config) }
Important Notes
} // TaskConfigField renders a single task configuration field based on schema with typed field lookup templ TaskConfigField(field *config.Field, config interface{}) { if field.InputType == "interval" {
if field.Description != "" {
{ field.Description }
}
} else if field.InputType == "checkbox" {
if field.Description != "" {
{ field.Description }
}
} else if field.InputType == "text" {
if field.Description != "" {
{ field.Description }
}
} else {
if field.Description != "" {
{ field.Description }
}
} } // Typed field getters for task configs - avoiding interface{} where possible func getTaskConfigBoolField(config interface{}, fieldName string) bool { switch fieldName { case "enabled": // Use reflection only for the common 'enabled' field in BaseConfig if value := getTaskFieldValue(config, fieldName); value != nil { if boolVal, ok := value.(bool); ok { return boolVal } } return false default: // For other boolean fields, use reflection if value := getTaskFieldValue(config, fieldName); value != nil { if boolVal, ok := value.(bool); ok { return boolVal } } return false } } func getTaskConfigInt32Field(config interface{}, fieldName string) int32 { switch fieldName { case "scan_interval_seconds", "max_concurrent": // Common fields that should be int/int32 if value := getTaskFieldValue(config, fieldName); value != nil { switch v := value.(type) { case int32: return v case int: return int32(v) case int64: return int32(v) } } return 0 default: // For other int fields, use reflection if value := getTaskFieldValue(config, fieldName); value != nil { switch v := value.(type) { case int32: return v case int: return int32(v) case int64: return int32(v) case float64: return int32(v) } } return 0 } } func getTaskConfigFloatField(config interface{}, fieldName string) float64 { if value := getTaskFieldValue(config, fieldName); value != nil { switch v := value.(type) { case float64: return v case float32: return float64(v) case int: return float64(v) case int32: return float64(v) case int64: return float64(v) } } return 0.0 } func getTaskConfigStringField(config interface{}, fieldName string) string { if value := getTaskFieldValue(config, fieldName); value != nil { if strVal, ok := value.(string); ok { return strVal } // Convert numbers to strings for form display switch v := value.(type) { case int: return fmt.Sprintf("%d", v) case int32: return fmt.Sprintf("%d", v) case int64: return fmt.Sprintf("%d", v) case float64: return fmt.Sprintf("%.6g", v) case float32: return fmt.Sprintf("%.6g", v) } } return "" } func getTaskNumberStep(field *config.Field) string { if field.Type == config.FieldTypeFloat { return "0.01" } return "1" } func getTaskFieldValue(config interface{}, fieldName string) interface{} { if config == nil { return nil } // Use reflection to get the field value from the config struct configValue := reflect.ValueOf(config) if configValue.Kind() == reflect.Ptr { configValue = configValue.Elem() } if configValue.Kind() != reflect.Struct { return nil } configType := configValue.Type() for i := 0; i < configValue.NumField(); i++ { field := configValue.Field(i) fieldType := configType.Field(i) // Handle embedded structs recursively (before JSON tag check) if field.Kind() == reflect.Struct && fieldType.Anonymous { if value := getTaskFieldValue(field.Interface(), fieldName); value != nil { return value } continue } // Get JSON tag name jsonTag := fieldType.Tag.Get("json") if jsonTag == "" { continue } // Remove options like ",omitempty" if commaIdx := strings.Index(jsonTag, ","); commaIdx > 0 { jsonTag = jsonTag[:commaIdx] } // Check if this is the field we're looking for if jsonTag == fieldName { return field.Interface() } } return nil }